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

Side by Side Diff: pkg/compiler/lib/src/js_backend/native_emitter.dart

Issue 848063003: dart2js: move NativeEmitter into js_emitter directory. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Remove spurious new line. Created 5 years, 11 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of js_backend;
6
7 class NativeEmitter {
8
9 final Map<Element, ClassBuilder> cachedBuilders;
10
11 final CodeEmitterTask emitterTask;
12
13 // Whether the application contains native classes.
14 bool hasNativeClasses = false;
15
16 // Caches the native subtypes of a native class.
17 Map<ClassElement, List<ClassElement>> subtypes;
18
19 // Caches the direct native subtypes of a native class.
20 Map<ClassElement, List<ClassElement>> directSubtypes;
21
22 // Caches the methods that have a native body.
23 Set<FunctionElement> nativeMethods;
24
25 // Do we need the native emitter to take care of handling
26 // noSuchMethod for us? This flag is set to true in the emitter if
27 // it finds any native class that needs noSuchMethod handling.
28 bool handleNoSuchMethod = false;
29
30 NativeEmitter(CodeEmitterTask emitterTask)
31 : this.emitterTask = emitterTask,
32 subtypes = new Map<ClassElement, List<ClassElement>>(),
33 directSubtypes = new Map<ClassElement, List<ClassElement>>(),
34 nativeMethods = new Set<FunctionElement>(),
35 cachedBuilders = emitterTask.compiler.cacheStrategy.newMap();
36
37 Compiler get compiler => emitterTask.compiler;
38 JavaScriptBackend get backend => compiler.backend;
39
40 jsAst.Expression get defPropFunction {
41 Element element = backend.findHelper('defineProperty');
42 return emitterTask.staticFunctionAccess(element);
43 }
44
45 /**
46 * Writes code to associate dispatch tags with interceptors to [nativeBuffer].
47 *
48 * The interceptors are filtered to avoid emitting trivial interceptors. For
49 * example, if the program contains no code that can distinguish between the
50 * numerous subclasses of `Element` then we can pretend that `Element` is a
51 * leaf class, and all instances of subclasses of `Element` are instances of
52 * `Element`.
53 *
54 * There is also a performance benefit (in addition to the obvious code size
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
57 * improves performance when more classes can be treated as leaves.
58 *
59 * [classes] contains native classes, mixin applications, and user subclasses
60 * of native classes. ONLY the native classes are generated here. [classes]
61 * is sorted in desired output order.
62 *
63 * [additionalProperties] is used to collect properties that are pushed up
64 * from the above optimizations onto a non-native class, e.g, `Interceptor`.
65 */
66 void generateNativeClasses(
67 List<ClassElement> classes,
68 Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties) {
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
71 // in reverse.
72
73 List<ClassElement> preOrder = <ClassElement>[];
74 Set<ClassElement> seen = new Set<ClassElement>();
75 seen..add(compiler.objectClass)
76 ..add(backend.jsInterceptorClass);
77 void walk(ClassElement element) {
78 if (seen.contains(element)) return;
79 seen.add(element);
80 walk(element.superclass);
81 preOrder.add(element);
82 }
83 classes.forEach(walk);
84
85 // Generate code for each native class into [ClassBuilder]s.
86
87 Map<ClassElement, ClassBuilder> builders =
88 new Map<ClassElement, ClassBuilder>();
89 for (ClassElement classElement in classes) {
90 if (classElement.isNative) {
91 ClassBuilder builder = generateNativeClass(classElement);
92 builders[classElement] = builder;
93 }
94 }
95
96 // 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
98 // needed class.
99
100 Set<ClassElement> neededClasses = new Set<ClassElement>();
101 Set<ClassElement> nonleafClasses = new Set<ClassElement>();
102
103 Map<ClassElement, List<ClassElement>> extensionPoints =
104 computeExtensionPoints(preOrder);
105
106 neededClasses.add(compiler.objectClass);
107
108 Set<ClassElement> neededByConstant =
109 emitterTask.interceptorsReferencedFromConstants();
110 Set<ClassElement> modifiedClasses =
111 emitterTask.typeTestRegistry.classesModifiedByEmitRuntimeTypeSupport();
112
113 for (ClassElement classElement in preOrder.reversed) {
114 // 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
116 // subclass is needed.
117 ClassBuilder builder = builders[classElement];
118 bool needed = false;
119 if (builder == null) {
120 // Mixin applications (native+mixin) are non-native, so [classElement]
121 // has already been emitted as a regular class. Mark [classElement] as
122 // 'needed' to ensure the native superclass is needed.
123 needed = true;
124 } else if (!builder.isTrivial) {
125 needed = true;
126 } else if (neededByConstant.contains(classElement)) {
127 needed = true;
128 } else if (modifiedClasses.contains(classElement)) {
129 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer
130 // adds information to a class prototype or constructor.
131 needed = true;
132 } else if (extensionPoints.containsKey(classElement)) {
133 needed = true;
134 }
135 if (classElement.isNative &&
136 native.nativeTagsForcedNonLeaf(classElement)) {
137 needed = true;
138 nonleafClasses.add(classElement);
139 }
140
141 if (needed || neededClasses.contains(classElement)) {
142 neededClasses.add(classElement);
143 neededClasses.add(classElement.superclass);
144 nonleafClasses.add(classElement.superclass);
145 }
146 }
147
148 // Collect all the tags that map to each native class.
149
150 Map<ClassElement, Set<String>> leafTags =
151 new Map<ClassElement, Set<String>>();
152 Map<ClassElement, Set<String>> nonleafTags =
153 new Map<ClassElement, Set<String>>();
154
155 for (ClassElement classElement in classes) {
156 if (!classElement.isNative) continue;
157 List<String> nativeTags = native.nativeTagsOfClass(classElement);
158
159 if (nonleafClasses.contains(classElement) ||
160 extensionPoints.containsKey(classElement)) {
161 nonleafTags
162 .putIfAbsent(classElement, () => new Set<String>())
163 .addAll(nativeTags);
164 } else {
165 ClassElement sufficingInterceptor = classElement;
166 while (!neededClasses.contains(sufficingInterceptor)) {
167 sufficingInterceptor = sufficingInterceptor.superclass;
168 }
169 if (sufficingInterceptor == compiler.objectClass) {
170 sufficingInterceptor = backend.jsInterceptorClass;
171 }
172 leafTags
173 .putIfAbsent(sufficingInterceptor, () => new Set<String>())
174 .addAll(nativeTags);
175 }
176 }
177
178 // Add properties containing the information needed to construct maps used
179 // by getNativeInterceptor and custom elements.
180 if (compiler.enqueuer.codegen.nativeEnqueuer
181 .hasInstantiatedNativeClasses()) {
182 void generateClassInfo(ClassElement classElement) {
183 // Property has the form:
184 //
185 // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...",
186 //
187 // If there is no data following a semicolon, the semicolon can be
188 // omitted.
189
190 String formatTags(Iterable<String> tags) {
191 if (tags == null) return '';
192 return (tags.toList()..sort()).join('|');
193 }
194
195 List<ClassElement> extensions = extensionPoints[classElement];
196
197 String leafStr = formatTags(leafTags[classElement]);
198 String nonleafStr = formatTags(nonleafTags[classElement]);
199
200 StringBuffer sb = new StringBuffer(leafStr);
201 if (nonleafStr != '') {
202 sb..write(';')..write(nonleafStr);
203 }
204 if (extensions != null) {
205 sb..write(';')
206 ..writeAll(extensions.map(backend.namer.getNameOfClass), '|');
207 }
208 String encoding = sb.toString();
209
210 ClassBuilder builder = builders[classElement];
211 if (builder == null) {
212 // No builder because this is an intermediate mixin application or
213 // Interceptor - these are not direct native classes.
214 if (encoding != '') {
215 Map<String, jsAst.Expression> properties =
216 additionalProperties.putIfAbsent(classElement,
217 () => new LinkedHashMap<String, jsAst.Expression>());
218 properties[backend.namer.nativeSpecProperty] = js.string(encoding);
219 }
220 } else {
221 builder.addProperty(
222 backend.namer.nativeSpecProperty, js.string(encoding));
223 }
224 }
225 generateClassInfo(backend.jsInterceptorClass);
226 for (ClassElement classElement in classes) {
227 generateClassInfo(classElement);
228 }
229 }
230
231 // Emit the native class interceptors that were actually used.
232 for (ClassElement classElement in classes) {
233 if (!classElement.isNative) continue;
234 if (neededClasses.contains(classElement)) {
235 // Define interceptor class for [classElement].
236 emitterTask.oldEmitter.classEmitter.emitClassBuilderWithReflectionData(
237 backend.namer.getNameOfClass(classElement),
238 classElement, builders[classElement],
239 emitterTask.oldEmitter.getElementDescriptor(classElement));
240 emitterTask.oldEmitter.needsClassSupport = true;
241 }
242 }
243 }
244
245 /**
246 * 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
248 * instead of a Set for out stability).
249 */
250 Map<ClassElement, List<ClassElement>> computeExtensionPoints(
251 List<ClassElement> classes) {
252 ClassElement nativeSuperclassOf(ClassElement element) {
253 if (element == null) return null;
254 if (element.isNative) return element;
255 return nativeSuperclassOf(element.superclass);
256 }
257
258 ClassElement nativeAncestorOf(ClassElement element) {
259 return nativeSuperclassOf(element.superclass);
260 }
261
262 Map<ClassElement, List<ClassElement>> map =
263 new Map<ClassElement, List<ClassElement>>();
264
265 for (ClassElement classElement in classes) {
266 if (classElement.isNative) continue;
267 ClassElement nativeAncestor = nativeAncestorOf(classElement);
268 if (nativeAncestor != null) {
269 map
270 .putIfAbsent(nativeAncestor, () => <ClassElement>[])
271 .add(classElement);
272 }
273 }
274 return map;
275 }
276
277 ClassBuilder generateNativeClass(ClassElement classElement) {
278 // TODO(sra): Issue #13731- this is commented out as part of custom element
279 // constructor work.
280 //assert(!classElement.hasBackendMembers);
281 hasNativeClasses = true;
282
283 ClassElement superclass = classElement.superclass;
284 assert(superclass != null);
285 // Fix superclass. TODO(sra): make native classes inherit from Interceptor.
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 ClassBuilder builder;
294 if (compiler.hasIncrementalSupport) {
295 builder = cachedBuilders[classElement];
296 if (builder != null) return builder;
297 builder = new ClassBuilder(classElement, backend.namer);
298 cachedBuilders[classElement] = builder;
299 } else {
300 builder = new ClassBuilder(classElement, backend.namer);
301 }
302 builder.superName = superName;
303
304 emitterTask.oldEmitter.classEmitter.emitClassConstructor(
305 classElement, builder);
306 bool hasFields = emitterTask.oldEmitter.classEmitter.emitFields(
307 classElement, builder, classIsNative: true);
308 int propertyCount = builder.properties.length;
309 emitterTask.oldEmitter.classEmitter.emitClassGettersSetters(
310 classElement, builder);
311 emitterTask.oldEmitter.classEmitter.emitInstanceMembers(
312 classElement, builder);
313 emitterTask.oldEmitter.typeTestEmitter.emitIsTests(classElement, builder);
314
315 if (!hasFields &&
316 builder.properties.length == propertyCount &&
317 superclass is! MixinApplicationElement) {
318 builder.isTrivial = true;
319 }
320
321 return builder;
322 }
323
324 void finishGenerateNativeClasses() {
325 // TODO(sra): Put specialized version of getNativeMethods on
326 // `Object.prototype` to avoid checking in `getInterceptor` and
327 // specializations.
328 }
329
330 void potentiallyConvertDartClosuresToJs(
331 List<jsAst.Statement> statements,
332 FunctionElement member,
333 List<jsAst.Parameter> stubParameters) {
334 FunctionSignature parameters = member.functionSignature;
335 Element converter = backend.findHelper('convertDartClosureToJS');
336 jsAst.Expression closureConverter =
337 emitterTask.staticFunctionAccess(converter);
338 parameters.forEachParameter((ParameterElement parameter) {
339 String name = parameter.name;
340 // If [name] is not in [stubParameters], then the parameter is an optional
341 // parameter that was not provided for this stub.
342 for (jsAst.Parameter stubParameter in stubParameters) {
343 if (stubParameter.name == name) {
344 DartType type = parameter.type.unalias(compiler);
345 if (type is FunctionType) {
346 // The parameter type is a function type either directly or through
347 // typedef(s).
348 FunctionType functionType = type;
349 int arity = functionType.computeArity();
350 statements.add(
351 js.statement('# = #(#, $arity)',
352 [name, closureConverter, name]));
353 break;
354 }
355 }
356 }
357 });
358 }
359
360 List<jsAst.Statement> generateParameterStubStatements(
361 FunctionElement member,
362 bool isInterceptedMethod,
363 String invocationName,
364 List<jsAst.Parameter> stubParameters,
365 List<jsAst.Expression> argumentsBuffer,
366 int indexOfLastOptionalArgumentInParameters) {
367 // The target JS function may check arguments.length so we need to
368 // make sure not to pass any unspecified optional arguments to it.
369 // For example, for the following Dart method:
370 // foo([x, y, z]);
371 // The call:
372 // foo(y: 1)
373 // must be turned into a JS call to:
374 // foo(null, y).
375
376 ClassElement classElement = member.enclosingClass;
377
378 List<jsAst.Statement> statements = <jsAst.Statement>[];
379 potentiallyConvertDartClosuresToJs(statements, member, stubParameters);
380
381 String target;
382 jsAst.Expression receiver;
383 List<jsAst.Expression> arguments;
384
385 assert(invariant(member, nativeMethods.contains(member)));
386 // When calling a JS method, we call it with the native name, and only the
387 // arguments up until the last one provided.
388 target = member.fixedBackendName;
389
390 if (isInterceptedMethod) {
391 receiver = argumentsBuffer[0];
392 arguments = argumentsBuffer.sublist(1,
393 indexOfLastOptionalArgumentInParameters + 1);
394 } else {
395 receiver = js('this');
396 arguments = argumentsBuffer.sublist(0,
397 indexOfLastOptionalArgumentInParameters + 1);
398 }
399 statements.add(
400 js.statement('return #.#(#)', [receiver, target, arguments]));
401
402 return statements;
403 }
404
405 bool isSupertypeOfNativeClass(Element element) {
406 if (element.isTypeVariable) {
407 compiler.internalError(element, "Is check for type variable.");
408 return false;
409 }
410 if (element.computeType(compiler).unalias(compiler) is FunctionType) {
411 // The element type is a function type either directly or through
412 // typedef(s).
413 return false;
414 }
415
416 if (!element.isClass) {
417 compiler.internalError(element, "Is check does not handle element.");
418 return false;
419 }
420
421 if (backend.classesMixedIntoInterceptedClasses.contains(element)) {
422 return true;
423 }
424
425 return subtypes[element] != null;
426 }
427
428 bool requiresNativeIsCheck(Element element) {
429 // TODO(sra): Remove this function. It determines if a native type may
430 // satisfy a check against [element], in which case an interceptor must be
431 // used. We should also use an interceptor if the check can't be satisfied
432 // by a native class in case we get a native instance that tries to spoof
433 // the type info. i.e the criteria for whether or not to use an interceptor
434 // is whether the receiver can be native, not the type of the test.
435 if (element == null || !element.isClass) return false;
436 ClassElement cls = element;
437 if (Elements.isNativeOrExtendsNative(cls)) return true;
438 return isSupertypeOfNativeClass(element);
439 }
440
441 void assembleCode(CodeOutput targetOutput) {
442 List<jsAst.Property> objectProperties = <jsAst.Property>[];
443
444 jsAst.Property addProperty(String name, jsAst.Expression value) {
445 jsAst.Property prop = new jsAst.Property(js.string(name), value);
446 objectProperties.add(prop);
447 return prop;
448 }
449
450 if (hasNativeClasses) {
451 // If the native emitter has been asked to take care of the
452 // noSuchMethod handlers, we do that now.
453 if (handleNoSuchMethod) {
454 emitterTask.oldEmitter.nsmEmitter.emitNoSuchMethodHandlers(addProperty);
455 }
456 }
457
458 // If we have any properties to add to Object.prototype, we run
459 // through them and add them using defineProperty.
460 if (!objectProperties.isEmpty) {
461 jsAst.Expression init = js(r'''
462 (function(table) {
463 for(var key in table)
464 #(Object.prototype, key, table[key]);
465 })(#)''',
466 [ defPropFunction,
467 new jsAst.ObjectInitializer(objectProperties)]);
468
469 if (emitterTask.compiler.enableMinification) {
470 targetOutput.add(';');
471 }
472 targetOutput.addBuffer(jsAst.prettyPrint(
473 new jsAst.ExpressionStatement(init), compiler));
474 targetOutput.add('\n');
475 }
476
477 targetOutput.add('\n');
478 }
479 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/js_backend.dart ('k') | pkg/compiler/lib/src/js_emitter/js_emitter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698