| 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 | 8 |
| 9 final Map<Element, ClassBuilder> cachedBuilders; | 9 final Map<Element, ClassBuilder> cachedBuilders; |
| 10 | 10 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 * | 53 * |
| 54 * There is also a performance benefit (in addition to the obvious code size | 54 * There is also a performance benefit (in addition to the obvious code size |
| 55 * benefit), due to how [getNativeInterceptor] works. Finding the interceptor | 55 * benefit), due to how [getNativeInterceptor] works. Finding the interceptor |
| 56 * of a leaf class in the hierarchy is more efficient that a non-leaf, so it | 56 * of a leaf class in the hierarchy is more efficient that a non-leaf, so it |
| 57 * improves performance when more classes can be treated as leaves. | 57 * improves performance when more classes can be treated as leaves. |
| 58 * | 58 * |
| 59 * [classes] contains native classes, mixin applications, and user subclasses | 59 * [classes] contains native classes, mixin applications, and user subclasses |
| 60 * of native classes. ONLY the native classes are generated here. [classes] | 60 * of native classes. ONLY the native classes are generated here. [classes] |
| 61 * is sorted in desired output order. | 61 * is sorted in desired output order. |
| 62 * | 62 * |
| 63 * [additionalProperties] is used to collect properties that are pushed up | 63 * [allAdditionalProperties] is used to collect properties that are pushed up |
| 64 * from the above optimizations onto a non-native class, e.g, `Interceptor`. | 64 * from the above optimizations onto a non-native class, e.g, `Interceptor`. |
| 65 */ | 65 */ |
| 66 void generateNativeClasses( | 66 void generateNativeClasses( |
| 67 List<Class> classes, | 67 List<Class> classes, |
| 68 Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties) { | 68 Map<Class, Map<String, jsAst.Expression>> allAdditionalProperties) { |
| 69 // Compute a pre-order traversal of the subclass forest. We actually want a | 69 // Compute a pre-order traversal of the subclass forest. We actually want a |
| 70 // post-order traversal but it is easier to compute the pre-order and use it | 70 // post-order traversal but it is easier to compute the pre-order and use it |
| 71 // in reverse. | 71 // in reverse. |
| 72 | 72 |
| 73 if (classes.isNotEmpty) { |
| 74 hasNativeClasses = true; |
| 75 } |
| 76 |
| 73 List<Class> preOrder = <Class>[]; | 77 List<Class> preOrder = <Class>[]; |
| 74 Set<Class> seen = new Set<Class>(); | 78 Set<Class> seen = new Set<Class>(); |
| 75 | 79 |
| 76 Class objectClass = null; | 80 Class objectClass = null; |
| 77 Class jsInterceptorClass = null; | 81 Class jsInterceptorClass = null; |
| 78 void walk(Class cls) { | 82 void walk(Class cls) { |
| 79 if (cls.element == compiler.objectClass) { | 83 if (cls.element == compiler.objectClass) { |
| 80 objectClass = cls; | 84 objectClass = cls; |
| 81 return; | 85 return; |
| 82 } | 86 } |
| 83 if (cls.element == backend.jsInterceptorClass) { | 87 if (cls.element == backend.jsInterceptorClass) { |
| 84 jsInterceptorClass = cls; | 88 jsInterceptorClass = cls; |
| 85 return; | 89 return; |
| 86 } | 90 } |
| 87 if (seen.contains(cls)) return; | 91 if (seen.contains(cls)) return; |
| 88 seen.add(cls); | 92 seen.add(cls); |
| 89 walk(cls.superclass); | 93 walk(cls.superclass); |
| 90 preOrder.add(cls); | 94 preOrder.add(cls); |
| 91 } | 95 } |
| 92 classes.forEach(walk); | 96 classes.forEach(walk); |
| 93 | 97 |
| 94 // Generate code for each native class into [ClassBuilder]s. | |
| 95 | |
| 96 Map<Class, ClassBuilder> builders = new Map<Class, ClassBuilder>(); | |
| 97 for (Class cls in classes) { | |
| 98 if (cls.isNative) { | |
| 99 ClassBuilder builder = createBuilderAndSetIsTrivial(cls); | |
| 100 builders[cls] = builder; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 // Find which classes are needed and which are non-leaf classes. Any class | 98 // Find which classes are needed and which are non-leaf classes. Any class |
| 105 // that is not needed can be treated as a leaf class equivalent to some | 99 // that is not needed can be treated as a leaf class equivalent to some |
| 106 // needed class. | 100 // needed class. |
| 107 | 101 |
| 108 Set<Class> neededClasses = new Set<Class>(); | 102 Set<Class> neededClasses = new Set<Class>(); |
| 109 Set<Class> nonleafClasses = new Set<Class>(); | 103 Set<Class> nonleafClasses = new Set<Class>(); |
| 110 | 104 |
| 111 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder); | 105 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder); |
| 112 | 106 |
| 113 neededClasses.add(objectClass); | 107 neededClasses.add(objectClass); |
| 114 | 108 |
| 115 Set<ClassElement> neededByConstant = emitterTask | 109 Set<ClassElement> neededByConstant = emitterTask |
| 116 .computeInterceptorsReferencedFromConstants(); | 110 .computeInterceptorsReferencedFromConstants(); |
| 117 Set<ClassElement> modifiedClasses = emitterTask.typeTestRegistry | 111 Set<ClassElement> modifiedClasses = emitterTask.typeTestRegistry |
| 118 .computeClassesModifiedByEmitRuntimeTypeSupport(); | 112 .computeClassesModifiedByEmitRuntimeTypeSupport(); |
| 119 | 113 |
| 120 for (Class cls in preOrder.reversed) { | 114 for (Class cls in preOrder.reversed) { |
| 121 ClassElement classElement = cls.element; | 115 ClassElement classElement = cls.element; |
| 122 // Post-order traversal ensures we visit the subclasses before their | 116 // Post-order traversal ensures we visit the subclasses before their |
| 123 // superclass. This makes it easy to tell if a class is needed because a | 117 // superclass. This makes it easy to tell if a class is needed because a |
| 124 // subclass is needed. | 118 // subclass is needed. |
| 125 ClassBuilder builder = builders[cls]; | |
| 126 bool needed = false; | 119 bool needed = false; |
| 127 if (builder == null) { | 120 if (!cls.isNative) { |
| 128 assert(!cls.isNative); | |
| 129 // Mixin applications (native+mixin) are non-native, so [classElement] | 121 // Mixin applications (native+mixin) are non-native, so [classElement] |
| 130 // has already been emitted as a regular class. Mark [classElement] as | 122 // has already been emitted as a regular class. Mark [classElement] as |
| 131 // 'needed' to ensure the native superclass is needed. | 123 // 'needed' to ensure the native superclass is needed. |
| 132 needed = true; | 124 needed = true; |
| 133 } else if (!builder.isTrivial) { | 125 } else if (!isTrivialClass(cls)) { |
| 134 needed = true; | 126 needed = true; |
| 135 } else if (neededByConstant.contains(classElement)) { | 127 } else if (neededByConstant.contains(classElement)) { |
| 136 needed = true; | 128 needed = true; |
| 137 } else if (modifiedClasses.contains(classElement)) { | 129 } else if (modifiedClasses.contains(classElement)) { |
| 138 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 130 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
| 139 // adds information to a class prototype or constructor. | 131 // adds information to a class prototype or constructor. |
| 140 needed = true; | 132 needed = true; |
| 141 } else if (extensionPoints.containsKey(cls)) { | 133 } else if (extensionPoints.containsKey(cls)) { |
| 142 needed = true; | 134 needed = true; |
| 143 } | 135 } |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 StringBuffer sb = new StringBuffer(leafStr); | 199 StringBuffer sb = new StringBuffer(leafStr); |
| 208 if (nonleafStr != '') { | 200 if (nonleafStr != '') { |
| 209 sb..write(';')..write(nonleafStr); | 201 sb..write(';')..write(nonleafStr); |
| 210 } | 202 } |
| 211 if (extensions != null) { | 203 if (extensions != null) { |
| 212 sb..write(';') | 204 sb..write(';') |
| 213 ..writeAll(extensions.map((Class cls) => cls.name), '|'); | 205 ..writeAll(extensions.map((Class cls) => cls.name), '|'); |
| 214 } | 206 } |
| 215 String encoding = sb.toString(); | 207 String encoding = sb.toString(); |
| 216 | 208 |
| 217 ClassBuilder builder = builders[cls]; | 209 if (cls.isNative || encoding != '') { |
| 218 if (builder == null) { | 210 Map<String, jsAst.Expression> properties = |
| 219 // No builder because this is an intermediate mixin application or | 211 allAdditionalProperties.putIfAbsent(cls, |
| 220 // Interceptor - these are not direct native classes. | 212 () => new Map<String, jsAst.Expression>()); |
| 221 if (encoding != '') { | 213 properties[backend.namer.nativeSpecProperty] = js.string(encoding); |
| 222 Map<String, jsAst.Expression> properties = | |
| 223 additionalProperties.putIfAbsent(cls.element, | |
| 224 () => new Map<String, jsAst.Expression>()); | |
| 225 properties[backend.namer.nativeSpecProperty] = js.string(encoding); | |
| 226 } | |
| 227 } else { | |
| 228 builder.addProperty( | |
| 229 backend.namer.nativeSpecProperty, js.string(encoding)); | |
| 230 } | 214 } |
| 231 } | 215 } |
| 232 generateClassInfo(jsInterceptorClass); | 216 generateClassInfo(jsInterceptorClass); |
| 233 for (Class cls in classes) { | 217 for (Class cls in classes) { |
| 234 if (!cls.isNative || neededClasses.contains(cls)) { | 218 if (!cls.isNative || neededClasses.contains(cls)) { |
| 235 generateClassInfo(cls); | 219 generateClassInfo(cls); |
| 236 } | 220 } |
| 237 } | 221 } |
| 238 } | 222 } |
| 239 | 223 |
| 240 // Emit the native class interceptors that were actually used. | 224 // Emit the native class interceptors that were actually used. |
| 241 for (Class cls in classes) { | 225 for (Class cls in classes) { |
| 242 assert(!cls.onlyForRti); | 226 assert(!cls.onlyForRti); |
| 243 ClassElement classElement = cls.element; | 227 ClassElement classElement = cls.element; |
| 244 if (!cls.isNative) continue; | 228 if (!cls.isNative) continue; |
| 245 if (neededClasses.contains(cls)) { | 229 if (neededClasses.contains(cls)) { |
| 246 ClassBuilder builder = builders[cls]; | 230 // TODO(sra): Issue #13731- this is commented out as part of custom |
| 247 assert(builder != null); | 231 // element constructor work. |
| 232 //assert(!classElement.hasBackendMembers); |
| 248 | 233 |
| 249 emitterTask.oldEmitter.classEmitter.emitConstructorsForCSP(cls); | 234 ClassBuilder enclosingBuilder = |
| 250 emitterTask.oldEmitter.classEmitter.emitFields( | 235 emitterTask.oldEmitter.getElementDescriptor(classElement); |
| 251 cls, builder, classIsNative: true); | 236 emitterTask.oldEmitter.emitClass(cls, enclosingBuilder); |
| 252 emitterTask.oldEmitter.classEmitter.emitCheckedClassSetters( | |
| 253 cls, builder); | |
| 254 emitterTask.oldEmitter.classEmitter.emitClassGettersSettersForCSP( | |
| 255 cls, builder); | |
| 256 emitterTask.oldEmitter.classEmitter.emitInstanceMembers( | |
| 257 cls, builder); | |
| 258 emitterTask.oldEmitter.classEmitter.emitCallStubs(cls, builder); | |
| 259 emitterTask.oldEmitter.classEmitter | |
| 260 .emitRuntimeTypeInformation(cls, builder); | |
| 261 | |
| 262 // Define interceptor class for [classElement]. | |
| 263 emitterTask.oldEmitter.classEmitter.emitClassBuilderWithReflectionData( | |
| 264 cls, | |
| 265 builders[cls], | |
| 266 emitterTask.oldEmitter.getElementDescriptor(classElement)); | |
| 267 emitterTask.oldEmitter.needsClassSupport = true; | |
| 268 } | 237 } |
| 269 } | 238 } |
| 270 } | 239 } |
| 271 | 240 |
| 272 /** | 241 /** |
| 273 * Computes the native classes that are extended (subclassed) by non-native | 242 * Computes the native classes that are extended (subclassed) by non-native |
| 274 * classes and the set non-mative classes that extend them. (A List is used | 243 * classes and the set non-mative classes that extend them. (A List is used |
| 275 * instead of a Set for out stability). | 244 * instead of a Set for out stability). |
| 276 */ | 245 */ |
| 277 Map<Class, List<Class>> computeExtensionPoints(List<Class> classes) { | 246 Map<Class, List<Class>> computeExtensionPoints(List<Class> classes) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 292 Class nativeAncestor = nativeAncestorOf(cls); | 261 Class nativeAncestor = nativeAncestorOf(cls); |
| 293 if (nativeAncestor != null) { | 262 if (nativeAncestor != null) { |
| 294 map | 263 map |
| 295 .putIfAbsent(nativeAncestor, () => <Class>[]) | 264 .putIfAbsent(nativeAncestor, () => <Class>[]) |
| 296 .add(cls); | 265 .add(cls); |
| 297 } | 266 } |
| 298 } | 267 } |
| 299 return map; | 268 return map; |
| 300 } | 269 } |
| 301 | 270 |
| 302 ClassBuilder createBuilderAndSetIsTrivial(Class cls) { | 271 bool isTrivialClass(Class cls) { |
| 303 ClassElement classElement = cls.element; | |
| 304 | |
| 305 // TODO(sra): Issue #13731- this is commented out as part of custom element | |
| 306 // constructor work. | |
| 307 //assert(!classElement.hasBackendMembers); | |
| 308 hasNativeClasses = true; | |
| 309 | |
| 310 Class superclass = cls.superclass; | |
| 311 assert(superclass != null); | |
| 312 assert(superclass.element != compiler.objectClass); | |
| 313 | |
| 314 ClassBuilder builder; | |
| 315 if (compiler.hasIncrementalSupport) { | |
| 316 builder = cachedBuilders[classElement]; | |
| 317 if (builder != null) return builder; | |
| 318 builder = new ClassBuilder(classElement, backend.namer); | |
| 319 cachedBuilders[classElement] = builder; | |
| 320 } else { | |
| 321 builder = new ClassBuilder(classElement, backend.namer); | |
| 322 } | |
| 323 builder.superName = superclass.name; | |
| 324 | |
| 325 bool needsAccessor(Field field) { | 272 bool needsAccessor(Field field) { |
| 326 return field.needsGetter || | 273 return field.needsGetter || |
| 327 field.needsUncheckedSetter || | 274 field.needsUncheckedSetter || |
| 328 field.needsCheckedSetter; | 275 field.needsCheckedSetter; |
| 329 } | 276 } |
| 330 | 277 |
| 331 builder.isTrivial = | 278 return |
| 332 cls.methods.isEmpty && | 279 cls.methods.isEmpty && |
| 333 cls.isChecks.isEmpty && | 280 cls.isChecks.isEmpty && |
| 334 cls.callStubs.isEmpty && | 281 cls.callStubs.isEmpty && |
| 335 !superclass.isMixinApplication && | 282 !cls.superclass.isMixinApplication && |
| 336 !cls.fields.any(needsAccessor); | 283 !cls.fields.any(needsAccessor); |
| 337 | |
| 338 return builder; | |
| 339 } | 284 } |
| 340 | 285 |
| 341 void finishGenerateNativeClasses() { | 286 void finishGenerateNativeClasses() { |
| 342 // TODO(sra): Put specialized version of getNativeMethods on | 287 // TODO(sra): Put specialized version of getNativeMethods on |
| 343 // `Object.prototype` to avoid checking in `getInterceptor` and | 288 // `Object.prototype` to avoid checking in `getInterceptor` and |
| 344 // specializations. | 289 // specializations. |
| 345 } | 290 } |
| 346 | 291 |
| 347 void potentiallyConvertDartClosuresToJs( | 292 void potentiallyConvertDartClosuresToJs( |
| 348 List<jsAst.Statement> statements, | 293 List<jsAst.Statement> statements, |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 487 targetOutput.add(';'); | 432 targetOutput.add(';'); |
| 488 } | 433 } |
| 489 targetOutput.addBuffer(jsAst.prettyPrint( | 434 targetOutput.addBuffer(jsAst.prettyPrint( |
| 490 new jsAst.ExpressionStatement(init), compiler)); | 435 new jsAst.ExpressionStatement(init), compiler)); |
| 491 targetOutput.add('\n'); | 436 targetOutput.add('\n'); |
| 492 } | 437 } |
| 493 | 438 |
| 494 targetOutput.add('\n'); | 439 targetOutput.add('\n'); |
| 495 } | 440 } |
| 496 } | 441 } |
| OLD | NEW |