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 js_backend; | 5 part of js_backend; |
6 | 6 |
7 class NativeEmitter { | 7 class NativeEmitter { |
8 | 8 |
9 CodeEmitterTask emitter; | 9 CodeEmitterTask emitter; |
10 CodeBuffer nativeBuffer; | 10 CodeBuffer nativeBuffer; |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 const SourceString('defineNativeMethods')); | 86 const SourceString('defineNativeMethods')); |
87 return backend.namer.isolateAccess(element); | 87 return backend.namer.isolateAccess(element); |
88 } | 88 } |
89 | 89 |
90 String get defineNativeMethodsNonleafName { | 90 String get defineNativeMethodsNonleafName { |
91 Element element = compiler.findHelper( | 91 Element element = compiler.findHelper( |
92 const SourceString('defineNativeMethodsNonleaf')); | 92 const SourceString('defineNativeMethodsNonleaf')); |
93 return backend.namer.isolateAccess(element); | 93 return backend.namer.isolateAccess(element); |
94 } | 94 } |
95 | 95 |
| 96 String get defineNativeMethodsExtendedName { |
| 97 Element element = compiler.findHelper( |
| 98 const SourceString('defineNativeMethodsExtended')); |
| 99 return backend.namer.isolateAccess(element); |
| 100 } |
| 101 |
96 String get defineNativeMethodsFinishName { | 102 String get defineNativeMethodsFinishName { |
97 Element element = compiler.findHelper( | 103 Element element = compiler.findHelper( |
98 const SourceString('defineNativeMethodsFinish')); | 104 const SourceString('defineNativeMethodsFinish')); |
99 return backend.namer.isolateAccess(element); | 105 return backend.namer.isolateAccess(element); |
100 } | 106 } |
101 | 107 |
102 List<String> nativeTagsOfClass(ClassElement cls) { | 108 List<String> nativeTagsOfClass(ClassElement cls) { |
103 String quotedName = cls.nativeTagInfo.slowToString(); | 109 String quotedName = cls.nativeTagInfo.slowToString(); |
104 return quotedName.substring(1, quotedName.length - 1).split(','); | 110 return quotedName.substring(1, quotedName.length - 1).split(','); |
105 } | 111 } |
106 | 112 |
107 /** | 113 /** |
108 * Writes the class definitions for the interceptors to [mainBuffer]. | 114 * Writes the class definitions for the interceptors to [mainBuffer]. |
109 * Writes code to associate dispatch tags with interceptors to [nativeBuffer]. | 115 * Writes code to associate dispatch tags with interceptors to [nativeBuffer]. |
110 * | 116 * |
111 * The interceptors are filtered to avoid emitting trivial interceptors. For | 117 * The interceptors are filtered to avoid emitting trivial interceptors. For |
112 * example, if the program contains no code that can distinguish between the | 118 * example, if the program contains no code that can distinguish between the |
113 * numerous subclasses of `Element` then we can pretend that `Element` is a | 119 * numerous subclasses of `Element` then we can pretend that `Element` is a |
114 * leaf class, and all instances of subclasses of `Element` are instances of | 120 * leaf class, and all instances of subclasses of `Element` are instances of |
115 * `Element`. | 121 * `Element`. |
116 * | 122 * |
117 * There is also a performance benefit (in addition to the obvious code size | 123 * There is also a performance benefit (in addition to the obvious code size |
118 * benefit), due to how [getNativeInterceptor] works. Finding the interceptor | 124 * benefit), due to how [getNativeInterceptor] works. Finding the interceptor |
119 * of a leaf class in the hierarchy is more efficient that a non-leaf, so it | 125 * of a leaf class in the hierarchy is more efficient that a non-leaf, so it |
120 * improves performance when more classes can be treated as leaves. | 126 * improves performance when more classes can be treated as leaves. |
| 127 * |
| 128 * [classes] contains native classes, mixin applications, and user subclasses |
| 129 * of native classes. ONLY the native classes are generated here. [classes] |
| 130 * is sorted in desired output order. |
121 */ | 131 */ |
122 void generateNativeClasses(List<ClassElement> classes, | 132 void generateNativeClasses(List<ClassElement> classes, |
123 CodeBuffer mainBuffer) { | 133 CodeBuffer mainBuffer) { |
124 // Compute a pre-order traversal of the subclass forest. We actually want a | 134 // Compute a pre-order traversal of the subclass forest. We actually want a |
125 // post-order traversal but it is easier to compute the pre-order and use it | 135 // post-order traversal but it is easier to compute the pre-order and use it |
126 // in reverse. | 136 // in reverse. |
127 | 137 |
128 List<ClassElement> preOrder = <ClassElement>[]; | 138 List<ClassElement> preOrder = <ClassElement>[]; |
129 Set<ClassElement> seen = new Set<ClassElement>(); | 139 Set<ClassElement> seen = new Set<ClassElement>(); |
| 140 seen..add(compiler.objectClass) |
| 141 ..add(backend.jsInterceptorClass); |
130 void walk(ClassElement element) { | 142 void walk(ClassElement element) { |
131 if (seen.contains(element) || element == compiler.objectClass) return; | 143 if (seen.contains(element)) return; |
132 seen.add(element); | 144 seen.add(element); |
133 walk(element.superclass); | 145 walk(element.superclass); |
134 preOrder.add(element); | 146 preOrder.add(element); |
135 } | 147 } |
136 classes.forEach(walk); | 148 classes.forEach(walk); |
137 | 149 |
138 // Generate code for each native class into [ClassBuilder]s. | 150 // Generate code for each native class into [ClassBuilder]s. |
139 | 151 |
140 Map<ClassElement, ClassBuilder> builders = | 152 Map<ClassElement, ClassBuilder> builders = |
141 new Map<ClassElement, ClassBuilder>(); | 153 new Map<ClassElement, ClassBuilder>(); |
142 for (ClassElement classElement in classes) { | 154 for (ClassElement classElement in classes) { |
143 ClassBuilder builder = generateNativeClass(classElement); | 155 if (classElement.isNative()) { |
144 builders[classElement] = builder; | 156 ClassBuilder builder = generateNativeClass(classElement); |
| 157 builders[classElement] = builder; |
| 158 } |
145 } | 159 } |
146 | 160 |
147 // Find which classes are needed and which are non-leaf classes. Any class | 161 // Find which classes are needed and which are non-leaf classes. Any class |
148 // that is not needed can be treated as a leaf class equivalent to some | 162 // that is not needed can be treated as a leaf class equivalent to some |
149 // needed class. | 163 // needed class. |
150 | 164 |
151 Set<ClassElement> neededClasses = new Set<ClassElement>(); | 165 Set<ClassElement> neededClasses = new Set<ClassElement>(); |
152 Set<ClassElement> nonleafClasses = new Set<ClassElement>(); | 166 Set<ClassElement> nonleafClasses = new Set<ClassElement>(); |
| 167 |
| 168 Map<ClassElement, List<ClassElement>> extensionPoints = |
| 169 computeExtensionPoints(preOrder); |
| 170 |
153 neededClasses.add(compiler.objectClass); | 171 neededClasses.add(compiler.objectClass); |
154 | 172 |
155 Set<ClassElement> neededByConstant = | 173 Set<ClassElement> neededByConstant = |
156 emitter.interceptorsReferencedFromConstants(); | 174 emitter.interceptorsReferencedFromConstants(); |
157 Set<ClassElement> modifiedClasses = | 175 Set<ClassElement> modifiedClasses = |
158 emitter.classesModifiedByEmitRuntimeTypeSupport(); | 176 emitter.classesModifiedByEmitRuntimeTypeSupport(); |
159 | 177 |
160 for (ClassElement classElement in preOrder.reversed) { | 178 for (ClassElement classElement in preOrder.reversed) { |
161 // Post-order traversal ensures we visit the subclasses before their | 179 // Post-order traversal ensures we visit the subclasses before their |
162 // superclass. This makes it easy to tell if a class is needed because a | 180 // superclass. This makes it easy to tell if a class is needed because a |
163 // subclass is needed. | 181 // subclass is needed. |
164 ClassBuilder builder = builders[classElement]; | 182 ClassBuilder builder = builders[classElement]; |
165 bool needed = false; | 183 bool needed = false; |
166 if (builder == null) { | 184 if (builder == null) { |
167 // Mixin applications (native+mixin) are non-native, so [classElement] | 185 // Mixin applications (native+mixin) are non-native, so [classElement] |
168 // has already been emitted as a regular class. Mark [classElement] as | 186 // has already been emitted as a regular class. Mark [classElement] as |
169 // 'needed' to ensure the native superclass is needed. | 187 // 'needed' to ensure the native superclass is needed. |
170 needed = true; | 188 needed = true; |
171 } else if (!builder.isTrivial) { | 189 } else if (!builder.isTrivial) { |
172 needed = true; | 190 needed = true; |
173 } else if (neededByConstant.contains(classElement)) { | 191 } else if (neededByConstant.contains(classElement)) { |
174 needed = true; | 192 needed = true; |
175 } else if (modifiedClasses.contains(classElement)) { | 193 } else if (modifiedClasses.contains(classElement)) { |
176 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 194 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
177 // adds information to a class prototype or constructor. | 195 // adds information to a class prototype or constructor. |
178 needed = true; | 196 needed = true; |
| 197 } else if (extensionPoints.containsKey(classElement)) { |
| 198 needed = true; |
179 } | 199 } |
180 | 200 |
181 if (needed || neededClasses.contains(classElement)) { | 201 if (needed || neededClasses.contains(classElement)) { |
182 neededClasses.add(classElement); | 202 neededClasses.add(classElement); |
183 neededClasses.add(classElement.superclass); | 203 neededClasses.add(classElement.superclass); |
184 nonleafClasses.add(classElement.superclass); | 204 nonleafClasses.add(classElement.superclass); |
185 } | 205 } |
186 } | 206 } |
187 | 207 |
188 // Collect all the tags that map to each class. | 208 // Collect all the tags that map to each native class. |
189 | 209 |
190 Map<ClassElement, Set<String>> leafTags = | 210 Map<ClassElement, Set<String>> leafTags = |
191 new Map<ClassElement, Set<String>>(); | 211 new Map<ClassElement, Set<String>>(); |
192 Map<ClassElement, Set<String>> nonleafTags = | 212 Map<ClassElement, Set<String>> nonleafTags = |
193 new Map<ClassElement, Set<String>>(); | 213 new Map<ClassElement, Set<String>>(); |
194 | 214 |
195 for (ClassElement classElement in classes) { | 215 for (ClassElement classElement in classes) { |
| 216 if (!classElement.isNative()) continue; |
196 List<String> nativeTags = nativeTagsOfClass(classElement); | 217 List<String> nativeTags = nativeTagsOfClass(classElement); |
197 | 218 |
198 if (nonleafClasses.contains(classElement)) { | 219 if (nonleafClasses.contains(classElement) || |
| 220 extensionPoints.containsKey(classElement)) { |
199 nonleafTags | 221 nonleafTags |
200 .putIfAbsent(classElement, () => new Set<String>()) | 222 .putIfAbsent(classElement, () => new Set<String>()) |
201 .addAll(nativeTags); | 223 .addAll(nativeTags); |
202 } else { | 224 } else { |
203 ClassElement sufficingInterceptor = classElement; | 225 ClassElement sufficingInterceptor = classElement; |
204 while (!neededClasses.contains(sufficingInterceptor)) { | 226 while (!neededClasses.contains(sufficingInterceptor)) { |
205 sufficingInterceptor = sufficingInterceptor.superclass; | 227 sufficingInterceptor = sufficingInterceptor.superclass; |
206 } | 228 } |
207 if (sufficingInterceptor == compiler.objectClass) { | 229 if (sufficingInterceptor == compiler.objectClass) { |
208 sufficingInterceptor = backend.jsInterceptorClass; | 230 sufficingInterceptor = backend.jsInterceptorClass; |
209 } | 231 } |
210 leafTags | 232 leafTags |
211 .putIfAbsent(sufficingInterceptor, () => new Set<String>()) | 233 .putIfAbsent(sufficingInterceptor, () => new Set<String>()) |
212 .addAll(nativeTags); | 234 .addAll(nativeTags); |
213 } | 235 } |
214 } | 236 } |
215 | 237 |
216 // Emit code to set up dispatch data that maps tags to the interceptors, but | 238 // Emit code to set up dispatch data that maps tags to the interceptors, but |
217 // only if native classes are actually instantiated. | 239 // only if native classes are actually instantiated. |
218 if (compiler.enqueuer.codegen.nativeEnqueuer | 240 if (compiler.enqueuer.codegen.nativeEnqueuer |
219 .hasInstantiatedNativeClasses()) { | 241 .hasInstantiatedNativeClasses()) { |
220 void generateDefines(ClassElement classElement) { | 242 void generateDefines(ClassElement classElement) { |
221 generateDefineNativeMethods(leafTags[classElement], classElement, | 243 generateDefineNativeMethods(leafTags[classElement], |
222 defineNativeMethodsName); | 244 null, |
223 generateDefineNativeMethods(nonleafTags[classElement], classElement, | 245 classElement, defineNativeMethodsName); |
224 defineNativeMethodsNonleafName); | 246 List<ClassElement> extensions = extensionPoints[classElement]; |
| 247 if (extensions == null) { |
| 248 generateDefineNativeMethods(nonleafTags[classElement], |
| 249 null, |
| 250 classElement, defineNativeMethodsNonleafName); |
| 251 } else { |
| 252 generateDefineNativeMethods(nonleafTags[classElement], |
| 253 makeSubclassList(extensions), |
| 254 classElement, defineNativeMethodsExtendedName); |
| 255 } |
225 } | 256 } |
226 generateDefines(backend.jsInterceptorClass); | 257 generateDefines(backend.jsInterceptorClass); |
227 for (ClassElement classElement in classes) { | 258 for (ClassElement classElement in classes) { |
228 generateDefines(classElement); | 259 generateDefines(classElement); |
229 } | 260 } |
230 } | 261 } |
231 | 262 |
232 // Emit the native class interceptors that were actually used. | 263 // Emit the native class interceptors that were actually used. |
233 for (ClassElement classElement in classes) { | 264 for (ClassElement classElement in classes) { |
| 265 if (!classElement.isNative()) continue; |
234 if (neededClasses.contains(classElement)) { | 266 if (neededClasses.contains(classElement)) { |
235 // Define interceptor class for [classElement]. | 267 // Define interceptor class for [classElement]. |
236 emitter.emitClassBuilderWithReflectionData( | 268 emitter.emitClassBuilderWithReflectionData( |
237 backend.namer.getName(classElement), | 269 backend.namer.getName(classElement), |
238 classElement, builders[classElement], | 270 classElement, builders[classElement], |
239 emitter.bufferForElement(classElement, mainBuffer)); | 271 emitter.bufferForElement(classElement, mainBuffer)); |
240 emitter.needsDefineClass = true; | 272 emitter.needsDefineClass = true; |
241 } | 273 } |
242 } | 274 } |
243 } | 275 } |
244 | 276 |
| 277 /** |
| 278 * Computes the native classes that are extended (subclassed) by non-native |
| 279 * classes and the set non-mative classes that extend them. (A List is used |
| 280 * instead of a Set for out stability). |
| 281 */ |
| 282 Map<ClassElement, List<ClassElement>> computeExtensionPoints( |
| 283 List<ClassElement> classes) { |
| 284 ClassElement nativeSuperclassOf(ClassElement element) { |
| 285 if (element == null) return null; |
| 286 if (element.isNative()) return element; |
| 287 return nativeSuperclassOf(element.superclass); |
| 288 } |
| 289 |
| 290 ClassElement nativeAncestorOf(ClassElement element) { |
| 291 return nativeSuperclassOf(element.superclass); |
| 292 } |
| 293 |
| 294 Map<ClassElement, List<ClassElement>> map = |
| 295 new Map<ClassElement, List<ClassElement>>(); |
| 296 |
| 297 for (ClassElement classElement in classes) { |
| 298 if (classElement.isNative()) continue; |
| 299 ClassElement nativeAncestor = nativeAncestorOf(classElement); |
| 300 if (nativeAncestor != null) { |
| 301 map |
| 302 .putIfAbsent(nativeAncestor, () => <ClassElement>[]) |
| 303 .add(classElement); |
| 304 } |
| 305 } |
| 306 |
| 307 return map; |
| 308 } |
| 309 |
245 ClassBuilder generateNativeClass(ClassElement classElement) { | 310 ClassBuilder generateNativeClass(ClassElement classElement) { |
246 assert(!classElement.hasBackendMembers); | 311 assert(!classElement.hasBackendMembers); |
247 nativeClasses.add(classElement); | 312 nativeClasses.add(classElement); |
248 | 313 |
249 ClassElement superclass = classElement.superclass; | 314 ClassElement superclass = classElement.superclass; |
250 assert(superclass != null); | 315 assert(superclass != null); |
251 // Fix superclass. TODO(sra): make native classes inherit from Interceptor. | 316 // Fix superclass. TODO(sra): make native classes inherit from Interceptor. |
| 317 assert(superclass != compiler.objectClass); |
252 if (superclass == compiler.objectClass) { | 318 if (superclass == compiler.objectClass) { |
253 superclass = backend.jsInterceptorClass; | 319 superclass = backend.jsInterceptorClass; |
254 } | 320 } |
255 | 321 |
256 String superName = backend.namer.getName(superclass); | 322 String superName = backend.namer.getName(superclass); |
257 | 323 |
258 ClassBuilder builder = new ClassBuilder(); | 324 ClassBuilder builder = new ClassBuilder(); |
259 emitter.emitClassConstructor(classElement, builder); | 325 emitter.emitClassConstructor(classElement, builder); |
260 emitter.emitSuper(superName, builder); | 326 emitter.emitSuper(superName, builder); |
261 bool hasFields = emitter.emitFields( | 327 bool hasFields = emitter.emitFields( |
262 classElement, builder, superName, classIsNative: true); | 328 classElement, builder, superName, classIsNative: true); |
263 int propertyCount = builder.properties.length; | 329 int propertyCount = builder.properties.length; |
264 emitter.emitClassGettersSetters(classElement, builder); | 330 emitter.emitClassGettersSetters(classElement, builder); |
265 emitter.emitInstanceMembers(classElement, builder); | 331 emitter.emitInstanceMembers(classElement, builder); |
266 emitter.emitIsTests(classElement, builder); | 332 emitter.emitIsTests(classElement, builder); |
267 | 333 |
268 if (!hasFields && | 334 if (!hasFields && |
269 builder.properties.length == propertyCount && | 335 builder.properties.length == propertyCount && |
270 superclass is! MixinApplicationElement) { | 336 superclass is! MixinApplicationElement) { |
271 builder.isTrivial = true; | 337 builder.isTrivial = true; |
272 } | 338 } |
273 | 339 |
274 return builder; | 340 return builder; |
275 } | 341 } |
276 | 342 |
277 void generateDefineNativeMethods( | 343 void generateDefineNativeMethods( |
278 Set<String> tags, ClassElement classElement, String definer) { | 344 Set<String> tags, jsAst.Expression extraArgument, |
| 345 ClassElement classElement, String definer) { |
279 if (tags == null) return; | 346 if (tags == null) return; |
280 String tagsString = (tags.toList()..sort()).join('|'); | 347 String tagsString = (tags.toList()..sort()).join('|'); |
281 jsAst.Expression definition = | 348 |
282 js(definer)( | 349 List arguments = [ |
283 [js.string(tagsString), | 350 js.string(tagsString), |
284 js(backend.namer.isolateAccess(classElement))]); | 351 js(backend.namer.isolateAccess(classElement))]; |
| 352 if (extraArgument != null) { |
| 353 arguments.add(extraArgument); |
| 354 } |
| 355 jsAst.Expression definition = js(definer)(arguments); |
285 | 356 |
286 nativeBuffer.add(jsAst.prettyPrint(definition, compiler)); | 357 nativeBuffer.add(jsAst.prettyPrint(definition, compiler)); |
287 nativeBuffer.add('$N$n'); | 358 nativeBuffer.add('$N$n'); |
288 } | 359 } |
289 | 360 |
| 361 jsAst.Expression makeSubclassList(List<ClassElement> classes) { |
| 362 return new jsAst.ArrayInitializer.from( |
| 363 classes.map((ClassElement classElement) => |
| 364 js(backend.namer.isolateAccess(classElement)))); |
| 365 } |
290 | 366 |
291 void finishGenerateNativeClasses() { | 367 void finishGenerateNativeClasses() { |
292 // TODO(sra): Put specialized version of getNativeMethods on | 368 // TODO(sra): Put specialized version of getNativeMethods on |
293 // `Object.prototype` to avoid checking in `getInterceptor` and | 369 // `Object.prototype` to avoid checking in `getInterceptor` and |
294 // specializations. | 370 // specializations. |
295 | 371 |
296 // jsAst.Expression call = js(defineNativeMethodsFinishName)([]); | 372 // jsAst.Expression call = js(defineNativeMethodsFinishName)([]); |
297 // nativeBuffer.add(jsAst.prettyPrint(call, compiler)); | 373 // nativeBuffer.add(jsAst.prettyPrint(call, compiler)); |
298 // nativeBuffer.add('$N$n'); | 374 // nativeBuffer.add('$N$n'); |
299 } | 375 } |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
444 if (emitter.compiler.enableMinification) targetBuffer.add(';'); | 520 if (emitter.compiler.enableMinification) targetBuffer.add(';'); |
445 targetBuffer.add(jsAst.prettyPrint( | 521 targetBuffer.add(jsAst.prettyPrint( |
446 new jsAst.ExpressionStatement(init), compiler)); | 522 new jsAst.ExpressionStatement(init), compiler)); |
447 targetBuffer.add('\n'); | 523 targetBuffer.add('\n'); |
448 } | 524 } |
449 | 525 |
450 targetBuffer.add(nativeBuffer); | 526 targetBuffer.add(nativeBuffer); |
451 targetBuffer.add('\n'); | 527 targetBuffer.add('\n'); |
452 } | 528 } |
453 } | 529 } |
OLD | NEW |