Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart

Issue 15026006: Support for extending native classes (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698