| 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 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 * [additionalProperties] 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<ClassElement> classes, | 67 List<Class> classes, |
| 68 Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties) { | 68 Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties) { |
| 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 List<ClassElement> preOrder = <ClassElement>[]; | 73 List<Class> preOrder = <Class>[]; |
| 74 Set<ClassElement> seen = new Set<ClassElement>(); | 74 Set<Class> seen = new Set<Class>(); |
| 75 seen..add(compiler.objectClass) | 75 |
| 76 ..add(backend.jsInterceptorClass); | 76 Class objectClass = null; |
| 77 void walk(ClassElement element) { | 77 Class jsInterceptorClass = null; |
| 78 if (seen.contains(element)) return; | 78 void walk(Class cls) { |
| 79 seen.add(element); | 79 if (cls.element == compiler.objectClass) { |
| 80 walk(element.superclass); | 80 objectClass = cls; |
| 81 preOrder.add(element); | 81 return; |
| 82 } |
| 83 if (cls.element == backend.jsInterceptorClass) { |
| 84 jsInterceptorClass = cls; |
| 85 return; |
| 86 } |
| 87 if (seen.contains(cls)) return; |
| 88 seen.add(cls); |
| 89 walk(cls.superclass); |
| 90 preOrder.add(cls); |
| 82 } | 91 } |
| 83 classes.forEach(walk); | 92 classes.forEach(walk); |
| 84 | 93 |
| 85 // Generate code for each native class into [ClassBuilder]s. | 94 // Generate code for each native class into [ClassBuilder]s. |
| 86 | 95 |
| 87 Map<ClassElement, ClassBuilder> builders = | 96 Map<Class, ClassBuilder> builders = new Map<Class, ClassBuilder>(); |
| 88 new Map<ClassElement, ClassBuilder>(); | 97 for (Class cls in classes) { |
| 89 for (ClassElement classElement in classes) { | 98 if (cls.isNative) { |
| 90 if (classElement.isNative) { | 99 ClassBuilder builder = generateNativeClass(cls); |
| 91 ClassBuilder builder = generateNativeClass(classElement); | 100 builders[cls] = builder; |
| 92 builders[classElement] = builder; | |
| 93 } | 101 } |
| 94 } | 102 } |
| 95 | 103 |
| 96 // Find which classes are needed and which are non-leaf classes. Any class | 104 // Find which classes are needed and which are non-leaf classes. Any class |
| 97 // that is not needed can be treated as a leaf class equivalent to some | 105 // that is not needed can be treated as a leaf class equivalent to some |
| 98 // needed class. | 106 // needed class. |
| 99 | 107 |
| 100 Set<ClassElement> neededClasses = new Set<ClassElement>(); | 108 Set<Class> neededClasses = new Set<Class>(); |
| 101 Set<ClassElement> nonleafClasses = new Set<ClassElement>(); | 109 Set<Class> nonleafClasses = new Set<Class>(); |
| 102 | 110 |
| 103 Map<ClassElement, List<ClassElement>> extensionPoints = | 111 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder); |
| 104 computeExtensionPoints(preOrder); | |
| 105 | 112 |
| 106 neededClasses.add(compiler.objectClass); | 113 neededClasses.add(objectClass); |
| 107 | 114 |
| 108 Set<ClassElement> neededByConstant = | 115 Set<ClassElement> neededByConstant = |
| 109 emitterTask.interceptorsReferencedFromConstants(); | 116 emitterTask.interceptorsReferencedFromConstants(); |
| 110 Set<ClassElement> modifiedClasses = | 117 Set<ClassElement> modifiedClasses = |
| 111 emitterTask.typeTestRegistry.classesModifiedByEmitRuntimeTypeSupport(); | 118 emitterTask.typeTestRegistry.classesModifiedByEmitRuntimeTypeSupport(); |
| 112 | 119 |
| 113 for (ClassElement classElement in preOrder.reversed) { | 120 for (Class cls in preOrder.reversed) { |
| 121 ClassElement classElement = cls.element; |
| 114 // Post-order traversal ensures we visit the subclasses before their | 122 // Post-order traversal ensures we visit the subclasses before their |
| 115 // superclass. This makes it easy to tell if a class is needed because a | 123 // superclass. This makes it easy to tell if a class is needed because a |
| 116 // subclass is needed. | 124 // subclass is needed. |
| 117 ClassBuilder builder = builders[classElement]; | 125 ClassBuilder builder = builders[cls]; |
| 118 bool needed = false; | 126 bool needed = false; |
| 119 if (builder == null) { | 127 if (builder == null) { |
| 120 // Mixin applications (native+mixin) are non-native, so [classElement] | 128 // Mixin applications (native+mixin) are non-native, so [classElement] |
| 121 // has already been emitted as a regular class. Mark [classElement] as | 129 // has already been emitted as a regular class. Mark [classElement] as |
| 122 // 'needed' to ensure the native superclass is needed. | 130 // 'needed' to ensure the native superclass is needed. |
| 123 needed = true; | 131 needed = true; |
| 124 } else if (!builder.isTrivial) { | 132 } else if (!builder.isTrivial) { |
| 125 needed = true; | 133 needed = true; |
| 126 } else if (neededByConstant.contains(classElement)) { | 134 } else if (neededByConstant.contains(classElement)) { |
| 127 needed = true; | 135 needed = true; |
| 128 } else if (modifiedClasses.contains(classElement)) { | 136 } else if (modifiedClasses.contains(classElement)) { |
| 129 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 137 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
| 130 // adds information to a class prototype or constructor. | 138 // adds information to a class prototype or constructor. |
| 131 needed = true; | 139 needed = true; |
| 132 } else if (extensionPoints.containsKey(classElement)) { | 140 } else if (extensionPoints.containsKey(cls)) { |
| 133 needed = true; | 141 needed = true; |
| 134 } | 142 } |
| 135 if (classElement.isNative && | 143 if (cls.isNative && |
| 136 native.nativeTagsForcedNonLeaf(classElement)) { | 144 native.nativeTagsForcedNonLeaf(classElement)) { |
| 137 needed = true; | 145 needed = true; |
| 138 nonleafClasses.add(classElement); | 146 nonleafClasses.add(cls); |
| 139 } | 147 } |
| 140 | 148 |
| 141 if (needed || neededClasses.contains(classElement)) { | 149 if (needed || neededClasses.contains(cls)) { |
| 142 neededClasses.add(classElement); | 150 neededClasses.add(cls); |
| 143 neededClasses.add(classElement.superclass); | 151 neededClasses.add(cls.superclass); |
| 144 nonleafClasses.add(classElement.superclass); | 152 nonleafClasses.add(cls.superclass); |
| 145 } | 153 } |
| 146 } | 154 } |
| 147 | 155 |
| 148 // Collect all the tags that map to each native class. | 156 // Collect all the tags that map to each native class. |
| 149 | 157 |
| 150 Map<ClassElement, Set<String>> leafTags = | 158 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); |
| 151 new Map<ClassElement, Set<String>>(); | 159 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); |
| 152 Map<ClassElement, Set<String>> nonleafTags = | |
| 153 new Map<ClassElement, Set<String>>(); | |
| 154 | 160 |
| 155 for (ClassElement classElement in classes) { | 161 for (Class cls in classes) { |
| 156 if (!classElement.isNative) continue; | 162 if (!cls.isNative) continue; |
| 157 List<String> nativeTags = native.nativeTagsOfClass(classElement); | 163 List<String> nativeTags = native.nativeTagsOfClass(cls.element); |
| 158 | 164 |
| 159 if (nonleafClasses.contains(classElement) || | 165 if (nonleafClasses.contains(cls) || |
| 160 extensionPoints.containsKey(classElement)) { | 166 extensionPoints.containsKey(cls)) { |
| 161 nonleafTags | 167 nonleafTags |
| 162 .putIfAbsent(classElement, () => new Set<String>()) | 168 .putIfAbsent(cls, () => new Set<String>()) |
| 163 .addAll(nativeTags); | 169 .addAll(nativeTags); |
| 164 } else { | 170 } else { |
| 165 ClassElement sufficingInterceptor = classElement; | 171 Class sufficingInterceptor = cls; |
| 166 while (!neededClasses.contains(sufficingInterceptor)) { | 172 while (!neededClasses.contains(sufficingInterceptor)) { |
| 167 sufficingInterceptor = sufficingInterceptor.superclass; | 173 sufficingInterceptor = sufficingInterceptor.superclass; |
| 168 } | 174 } |
| 169 if (sufficingInterceptor == compiler.objectClass) { | 175 if (sufficingInterceptor == objectClass) { |
| 170 sufficingInterceptor = backend.jsInterceptorClass; | 176 sufficingInterceptor = jsInterceptorClass; |
| 171 } | 177 } |
| 172 leafTags | 178 leafTags |
| 173 .putIfAbsent(sufficingInterceptor, () => new Set<String>()) | 179 .putIfAbsent(sufficingInterceptor, () => new Set<String>()) |
| 174 .addAll(nativeTags); | 180 .addAll(nativeTags); |
| 175 } | 181 } |
| 176 } | 182 } |
| 177 | 183 |
| 178 // Add properties containing the information needed to construct maps used | 184 // Add properties containing the information needed to construct maps used |
| 179 // by getNativeInterceptor and custom elements. | 185 // by getNativeInterceptor and custom elements. |
| 180 if (compiler.enqueuer.codegen.nativeEnqueuer | 186 if (compiler.enqueuer.codegen.nativeEnqueuer |
| 181 .hasInstantiatedNativeClasses()) { | 187 .hasInstantiatedNativeClasses()) { |
| 182 void generateClassInfo(ClassElement classElement) { | 188 void generateClassInfo(Class cls) { |
| 183 // Property has the form: | 189 // Property has the form: |
| 184 // | 190 // |
| 185 // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...", | 191 // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...", |
| 186 // | 192 // |
| 187 // If there is no data following a semicolon, the semicolon can be | 193 // If there is no data following a semicolon, the semicolon can be |
| 188 // omitted. | 194 // omitted. |
| 189 | 195 |
| 190 String formatTags(Iterable<String> tags) { | 196 String formatTags(Iterable<String> tags) { |
| 191 if (tags == null) return ''; | 197 if (tags == null) return ''; |
| 192 return (tags.toList()..sort()).join('|'); | 198 return (tags.toList()..sort()).join('|'); |
| 193 } | 199 } |
| 194 | 200 |
| 195 List<ClassElement> extensions = extensionPoints[classElement]; | 201 List<Class> extensions = extensionPoints[cls]; |
| 196 | 202 |
| 197 String leafStr = formatTags(leafTags[classElement]); | 203 String leafStr = formatTags(leafTags[cls]); |
| 198 String nonleafStr = formatTags(nonleafTags[classElement]); | 204 String nonleafStr = formatTags(nonleafTags[cls]); |
| 199 | 205 |
| 200 StringBuffer sb = new StringBuffer(leafStr); | 206 StringBuffer sb = new StringBuffer(leafStr); |
| 201 if (nonleafStr != '') { | 207 if (nonleafStr != '') { |
| 202 sb..write(';')..write(nonleafStr); | 208 sb..write(';')..write(nonleafStr); |
| 203 } | 209 } |
| 204 if (extensions != null) { | 210 if (extensions != null) { |
| 205 sb..write(';') | 211 sb..write(';') |
| 206 ..writeAll(extensions.map(backend.namer.getNameOfClass), '|'); | 212 ..writeAll(extensions.map((Class cls) => cls.name), '|'); |
| 207 } | 213 } |
| 208 String encoding = sb.toString(); | 214 String encoding = sb.toString(); |
| 209 | 215 |
| 210 ClassBuilder builder = builders[classElement]; | 216 ClassBuilder builder = builders[cls]; |
| 211 if (builder == null) { | 217 if (builder == null) { |
| 212 // No builder because this is an intermediate mixin application or | 218 // No builder because this is an intermediate mixin application or |
| 213 // Interceptor - these are not direct native classes. | 219 // Interceptor - these are not direct native classes. |
| 214 if (encoding != '') { | 220 if (encoding != '') { |
| 215 Map<String, jsAst.Expression> properties = | 221 Map<String, jsAst.Expression> properties = |
| 216 additionalProperties.putIfAbsent(classElement, | 222 additionalProperties.putIfAbsent(cls.element, |
| 217 () => new Map<String, jsAst.Expression>()); | 223 () => new Map<String, jsAst.Expression>()); |
| 218 properties[backend.namer.nativeSpecProperty] = js.string(encoding); | 224 properties[backend.namer.nativeSpecProperty] = js.string(encoding); |
| 219 } | 225 } |
| 220 } else { | 226 } else { |
| 221 builder.addProperty( | 227 builder.addProperty( |
| 222 backend.namer.nativeSpecProperty, js.string(encoding)); | 228 backend.namer.nativeSpecProperty, js.string(encoding)); |
| 223 } | 229 } |
| 224 } | 230 } |
| 225 generateClassInfo(backend.jsInterceptorClass); | 231 generateClassInfo(jsInterceptorClass); |
| 226 for (ClassElement classElement in classes) { | 232 for (Class cls in classes) { |
| 227 generateClassInfo(classElement); | 233 generateClassInfo(cls); |
| 228 } | 234 } |
| 229 } | 235 } |
| 230 | 236 |
| 231 // Emit the native class interceptors that were actually used. | 237 // Emit the native class interceptors that were actually used. |
| 232 for (ClassElement classElement in classes) { | 238 for (Class cls in classes) { |
| 233 if (!classElement.isNative) continue; | 239 ClassElement classElement = cls.element; |
| 234 if (neededClasses.contains(classElement)) { | 240 if (!cls.isNative) continue; |
| 241 if (neededClasses.contains(cls)) { |
| 235 // Define interceptor class for [classElement]. | 242 // Define interceptor class for [classElement]. |
| 236 emitterTask.oldEmitter.classEmitter.emitClassBuilderWithReflectionData( | 243 emitterTask.oldEmitter.classEmitter.emitClassBuilderWithReflectionData( |
| 237 backend.namer.getNameOfClass(classElement), | 244 cls.name, |
| 238 classElement, builders[classElement], | 245 classElement, builders[cls], |
| 239 emitterTask.oldEmitter.getElementDescriptor(classElement)); | 246 emitterTask.oldEmitter.getElementDescriptor(classElement)); |
| 240 emitterTask.oldEmitter.needsClassSupport = true; | 247 emitterTask.oldEmitter.needsClassSupport = true; |
| 241 } | 248 } |
| 242 } | 249 } |
| 243 } | 250 } |
| 244 | 251 |
| 245 /** | 252 /** |
| 246 * Computes the native classes that are extended (subclassed) by non-native | 253 * Computes the native classes that are extended (subclassed) by non-native |
| 247 * classes and the set non-mative classes that extend them. (A List is used | 254 * classes and the set non-mative classes that extend them. (A List is used |
| 248 * instead of a Set for out stability). | 255 * instead of a Set for out stability). |
| 249 */ | 256 */ |
| 250 Map<ClassElement, List<ClassElement>> computeExtensionPoints( | 257 Map<Class, List<Class>> computeExtensionPoints(List<Class> classes) { |
| 251 List<ClassElement> classes) { | 258 Class nativeSuperclassOf(Class cls) { |
| 252 ClassElement nativeSuperclassOf(ClassElement element) { | 259 if (cls == null) return null; |
| 253 if (element == null) return null; | 260 if (cls.isNative) return cls; |
| 254 if (element.isNative) return element; | 261 return nativeSuperclassOf(cls.superclass); |
| 255 return nativeSuperclassOf(element.superclass); | |
| 256 } | 262 } |
| 257 | 263 |
| 258 ClassElement nativeAncestorOf(ClassElement element) { | 264 Class nativeAncestorOf(Class cls) { |
| 259 return nativeSuperclassOf(element.superclass); | 265 return nativeSuperclassOf(cls.superclass); |
| 260 } | 266 } |
| 261 | 267 |
| 262 Map<ClassElement, List<ClassElement>> map = | 268 Map<Class, List<Class>> map = new Map<Class, List<Class>>(); |
| 263 new Map<ClassElement, List<ClassElement>>(); | |
| 264 | 269 |
| 265 for (ClassElement classElement in classes) { | 270 for (Class cls in classes) { |
| 266 if (classElement.isNative) continue; | 271 if (cls.isNative) continue; |
| 267 ClassElement nativeAncestor = nativeAncestorOf(classElement); | 272 Class nativeAncestor = nativeAncestorOf(cls); |
| 268 if (nativeAncestor != null) { | 273 if (nativeAncestor != null) { |
| 269 map | 274 map |
| 270 .putIfAbsent(nativeAncestor, () => <ClassElement>[]) | 275 .putIfAbsent(nativeAncestor, () => <Class>[]) |
| 271 .add(classElement); | 276 .add(cls); |
| 272 } | 277 } |
| 273 } | 278 } |
| 274 return map; | 279 return map; |
| 275 } | 280 } |
| 276 | 281 |
| 277 ClassBuilder generateNativeClass(ClassElement classElement) { | 282 ClassBuilder generateNativeClass(Class cls) { |
| 283 ClassElement classElement = cls.element; |
| 284 |
| 278 // TODO(sra): Issue #13731- this is commented out as part of custom element | 285 // TODO(sra): Issue #13731- this is commented out as part of custom element |
| 279 // constructor work. | 286 // constructor work. |
| 280 //assert(!classElement.hasBackendMembers); | 287 //assert(!classElement.hasBackendMembers); |
| 281 hasNativeClasses = true; | 288 hasNativeClasses = true; |
| 282 | 289 |
| 283 ClassElement superclass = classElement.superclass; | 290 Class superclass = cls.superclass; |
| 284 assert(superclass != null); | 291 assert(superclass != null); |
| 285 // Fix superclass. TODO(sra): make native classes inherit from Interceptor. | 292 assert(superclass.element != compiler.objectClass); |
| 286 assert(superclass != compiler.objectClass); | |
| 287 if (superclass == compiler.objectClass) { | |
| 288 superclass = backend.jsInterceptorClass; | |
| 289 } | |
| 290 | |
| 291 String superName = backend.namer.getNameOfClass(superclass); | |
| 292 | 293 |
| 293 ClassBuilder builder; | 294 ClassBuilder builder; |
| 294 if (compiler.hasIncrementalSupport) { | 295 if (compiler.hasIncrementalSupport) { |
| 295 builder = cachedBuilders[classElement]; | 296 builder = cachedBuilders[classElement]; |
| 296 if (builder != null) return builder; | 297 if (builder != null) return builder; |
| 297 builder = new ClassBuilder(classElement, backend.namer); | 298 builder = new ClassBuilder(classElement, backend.namer); |
| 298 cachedBuilders[classElement] = builder; | 299 cachedBuilders[classElement] = builder; |
| 299 } else { | 300 } else { |
| 300 builder = new ClassBuilder(classElement, backend.namer); | 301 builder = new ClassBuilder(classElement, backend.namer); |
| 301 } | 302 } |
| 302 builder.superName = superName; | 303 builder.superName = superclass.name; |
| 303 | 304 |
| 304 emitterTask.oldEmitter.classEmitter.emitClassConstructor( | 305 emitterTask.oldEmitter.classEmitter.emitClassConstructor( |
| 305 classElement, builder); | 306 classElement, builder); |
| 306 bool hasFields = emitterTask.oldEmitter.classEmitter.emitFields( | 307 bool hasFields = emitterTask.oldEmitter.classEmitter.emitFields( |
| 307 classElement, builder, classIsNative: true); | 308 classElement, builder, classIsNative: true); |
| 308 int propertyCount = builder.properties.length; | 309 int propertyCount = builder.properties.length; |
| 309 emitterTask.oldEmitter.classEmitter.emitClassGettersSetters( | 310 emitterTask.oldEmitter.classEmitter.emitClassGettersSetters( |
| 310 classElement, builder); | 311 classElement, builder); |
| 311 emitterTask.oldEmitter.classEmitter.emitInstanceMembers( | 312 emitterTask.oldEmitter.classEmitter.emitInstanceMembers( |
| 312 classElement, builder); | 313 classElement, builder); |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 470 targetOutput.add(';'); | 471 targetOutput.add(';'); |
| 471 } | 472 } |
| 472 targetOutput.addBuffer(jsAst.prettyPrint( | 473 targetOutput.addBuffer(jsAst.prettyPrint( |
| 473 new jsAst.ExpressionStatement(init), compiler)); | 474 new jsAst.ExpressionStatement(init), compiler)); |
| 474 targetOutput.add('\n'); | 475 targetOutput.add('\n'); |
| 475 } | 476 } |
| 476 | 477 |
| 477 targetOutput.add('\n'); | 478 targetOutput.add('\n'); |
| 478 } | 479 } |
| 479 } | 480 } |
| OLD | NEW |