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

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

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

Powered by Google App Engine
This is Rietveld 408576698