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

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

Issue 1215463006: Move nativeInfo encoding into NativeGenerator. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 5 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
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 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 12 matching lines...) Expand all
23 Set<FunctionElement> nativeMethods; 23 Set<FunctionElement> nativeMethods;
24 24
25 NativeEmitter(CodeEmitterTask emitterTask) 25 NativeEmitter(CodeEmitterTask emitterTask)
26 : this.emitterTask = emitterTask, 26 : this.emitterTask = emitterTask,
27 subtypes = new Map<ClassElement, List<ClassElement>>(), 27 subtypes = new Map<ClassElement, List<ClassElement>>(),
28 directSubtypes = new Map<ClassElement, List<ClassElement>>(), 28 directSubtypes = new Map<ClassElement, List<ClassElement>>(),
29 nativeMethods = new Set<FunctionElement>(), 29 nativeMethods = new Set<FunctionElement>(),
30 cachedBuilders = emitterTask.compiler.cacheStrategy.newMap(); 30 cachedBuilders = emitterTask.compiler.cacheStrategy.newMap();
31 31
32 Compiler get compiler => emitterTask.compiler; 32 Compiler get compiler => emitterTask.compiler;
33
33 JavaScriptBackend get backend => compiler.backend; 34 JavaScriptBackend get backend => compiler.backend;
34 35
35 jsAst.Expression get defPropFunction { 36 jsAst.Expression get defPropFunction {
36 Element element = backend.findHelper('defineProperty'); 37 Element element = backend.findHelper('defineProperty');
37 return emitterTask.staticFunctionAccess(element); 38 return emitterTask.staticFunctionAccess(element);
38 } 39 }
39 40
40 /** 41 /**
41 * Prepares native classes for emission. Returns the unneeded classes. 42 * Prepares native classes for emission. Returns the unneeded classes.
42 * 43 *
43 * Removes trivial classes (that can be represented by a super type) and 44 * Removes trivial classes (that can be represented by a super type) and
44 * generates properties that have to be added to classes (native or not). 45 * generates properties that have to be added to classes (native or not).
45 * 46 *
46 * Updates the `nativeInfo` field of the given classes. This data 47 * Updates the `nativeLeafTags`, `nativeNonLeafTags` and `nativeExtensions`
47 * must be emitted with the corresponding classes. 48 * fields of the given classes. This data must be emitted with the
49 * corresponding classes.
48 * 50 *
49 * The interceptors are filtered to avoid emitting trivial interceptors. For 51 * The interceptors are filtered to avoid emitting trivial interceptors. For
50 * example, if the program contains no code that can distinguish between the 52 * example, if the program contains no code that can distinguish between the
51 * numerous subclasses of `Element` then we can pretend that `Element` is a 53 * numerous subclasses of `Element` then we can pretend that `Element` is a
52 * leaf class, and all instances of subclasses of `Element` are instances of 54 * leaf class, and all instances of subclasses of `Element` are instances of
53 * `Element`. 55 * `Element`.
54 * 56 *
55 * There is also a performance benefit (in addition to the obvious code size 57 * There is also a performance benefit (in addition to the obvious code size
56 * benefit), due to how [getNativeInterceptor] works. Finding the interceptor 58 * benefit), due to how [getNativeInterceptor] works. Finding the interceptor
57 * of a leaf class in the hierarchy is more efficient that a non-leaf, so it 59 * of a leaf class in the hierarchy is more efficient that a non-leaf, so it
58 * improves performance when more classes can be treated as leaves. 60 * improves performance when more classes can be treated as leaves.
59 * 61 *
60 * [classes] contains native classes, mixin applications, and user subclasses 62 * [classes] contains native classes, mixin applications, and user subclasses
61 * of native classes. 63 * of native classes.
62 */ 64 */
63 Set<Class> prepareNativeClasses(List<Class> classes) { 65 Set<Class> prepareNativeClasses(List<Class> classes) {
64 assert(classes.every((Class cls) => cls != null)); 66 assert(classes.every((Class cls) => cls != null));
65 67
66 hasNativeClasses = classes.isNotEmpty; 68 hasNativeClasses = classes.isNotEmpty;
67 69
68 // Compute a pre-order traversal of the subclass forest. We actually want a 70 // Compute a pre-order traversal of the subclass forest. We actually want a
69 // post-order traversal but it is easier to compute the pre-order and use it 71 // post-order traversal but it is easier to compute the pre-order and use it
70 // in reverse. 72 // in reverse.
71 List<Class> preOrder = <Class>[]; 73 List<Class> preOrder = <Class>[];
72 Set<Class> seen = new Set<Class>(); 74 Set<Class> seen = new Set<Class>();
73 75
74 Class objectClass = null; 76 Class objectClass = null;
75 Class jsInterceptorClass = null; 77 Class jsInterceptorClass = null;
78
76 void walk(Class cls) { 79 void walk(Class cls) {
77 if (cls.element == compiler.objectClass) { 80 if (cls.element == compiler.objectClass) {
78 objectClass = cls; 81 objectClass = cls;
79 return; 82 return;
80 } 83 }
81 if (cls.element == backend.jsInterceptorClass) { 84 if (cls.element == backend.jsInterceptorClass) {
82 jsInterceptorClass = cls; 85 jsInterceptorClass = cls;
83 return; 86 return;
84 } 87 }
85 if (seen.contains(cls)) return; 88 if (seen.contains(cls)) return;
86 seen.add(cls); 89 seen.add(cls);
87 walk(cls.superclass); 90 walk(cls.superclass);
88 preOrder.add(cls); 91 preOrder.add(cls);
89 } 92 }
90 classes.forEach(walk); 93 classes.forEach(walk);
91 94
92 // Find which classes are needed and which are non-leaf classes. Any class 95 // Find which classes are needed and which are non-leaf classes. Any class
93 // that is not needed can be treated as a leaf class equivalent to some 96 // that is not needed can be treated as a leaf class equivalent to some
94 // needed class. 97 // needed class.
95 98
96 Set<Class> neededClasses = new Set<Class>(); 99 Set<Class> neededClasses = new Set<Class>();
97 Set<Class> nonleafClasses = new Set<Class>(); 100 Set<Class> nonLeafClasses = new Set<Class>();
98 101
99 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder); 102 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder);
100 103
101 neededClasses.add(objectClass); 104 neededClasses.add(objectClass);
102 105
103 Set<ClassElement> neededByConstant = emitterTask 106 Set<ClassElement> neededByConstant = emitterTask
104 .computeInterceptorsReferencedFromConstants(); 107 .computeInterceptorsReferencedFromConstants();
105 Set<ClassElement> modifiedClasses = emitterTask.typeTestRegistry 108 Set<ClassElement> modifiedClasses = emitterTask.typeTestRegistry
106 .computeClassesModifiedByEmitRuntimeTypeSupport(); 109 .computeClassesModifiedByEmitRuntimeTypeSupport();
107 110
(...skipping 15 matching lines...) Expand all
123 } else if (modifiedClasses.contains(classElement)) { 126 } else if (modifiedClasses.contains(classElement)) {
124 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer 127 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer
125 // adds information to a class prototype or constructor. 128 // adds information to a class prototype or constructor.
126 needed = true; 129 needed = true;
127 } else if (extensionPoints.containsKey(cls)) { 130 } else if (extensionPoints.containsKey(cls)) {
128 needed = true; 131 needed = true;
129 } 132 }
130 if (cls.isNative && 133 if (cls.isNative &&
131 native.nativeTagsForcedNonLeaf(classElement)) { 134 native.nativeTagsForcedNonLeaf(classElement)) {
132 needed = true; 135 needed = true;
133 nonleafClasses.add(cls); 136 nonLeafClasses.add(cls);
134 } 137 }
135 138
136 if (needed || neededClasses.contains(cls)) { 139 if (needed || neededClasses.contains(cls)) {
137 neededClasses.add(cls); 140 neededClasses.add(cls);
138 neededClasses.add(cls.superclass); 141 neededClasses.add(cls.superclass);
139 nonleafClasses.add(cls.superclass); 142 nonLeafClasses.add(cls.superclass);
140 } 143 }
141 } 144 }
142 145
143 // Collect all the tags that map to each native class. 146 // Collect all the tags that map to each native class.
144 147
145 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); 148 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>();
146 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); 149 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>();
147 150
148 for (Class cls in classes) { 151 for (Class cls in classes) {
149 if (!cls.isNative) continue; 152 if (!cls.isNative) continue;
150 List<String> nativeTags = native.nativeTagsOfClass(cls.element); 153 List<String> nativeTags = native.nativeTagsOfClass(cls.element);
151 154
152 if (nonleafClasses.contains(cls) || 155 if (nonLeafClasses.contains(cls) ||
153 extensionPoints.containsKey(cls)) { 156 extensionPoints.containsKey(cls)) {
154 nonleafTags 157 nonleafTags
155 .putIfAbsent(cls, () => new Set<String>()) 158 .putIfAbsent(cls, () => new Set<String>())
156 .addAll(nativeTags); 159 .addAll(nativeTags);
157 } else { 160 } else {
158 Class sufficingInterceptor = cls; 161 Class sufficingInterceptor = cls;
159 while (!neededClasses.contains(sufficingInterceptor)) { 162 while (!neededClasses.contains(sufficingInterceptor)) {
160 sufficingInterceptor = sufficingInterceptor.superclass; 163 sufficingInterceptor = sufficingInterceptor.superclass;
161 } 164 }
162 if (sufficingInterceptor == objectClass) { 165 if (sufficingInterceptor == objectClass) {
163 sufficingInterceptor = jsInterceptorClass; 166 sufficingInterceptor = jsInterceptorClass;
164 } 167 }
165 leafTags 168 leafTags
166 .putIfAbsent(sufficingInterceptor, () => new Set<String>()) 169 .putIfAbsent(sufficingInterceptor, () => new Set<String>())
167 .addAll(nativeTags); 170 .addAll(nativeTags);
168 } 171 }
169 } 172 }
170 173
174 void fillNativeInfo(Class cls) {
175 assert(cls.nativeLeafTags == null &&
176 cls.nativeNonLeafTags == null &&
177 cls.nativeExtensions == null);
178 cls.nativeLeafTags = leafTags[cls].toList(growable: false);
179 cls.nativeNonLeafTags = nonleafTags[cls].toList(growable: false);
180 cls.nativeExtensions = extensionPoints[cls];
181 }
171 // Add properties containing the information needed to construct maps used 182 // Add properties containing the information needed to construct maps used
172 // by getNativeInterceptor and custom elements. 183 // by getNativeInterceptor and custom elements.
173 if (compiler.enqueuer.codegen.nativeEnqueuer 184 if (compiler.enqueuer.codegen.nativeEnqueuer
174 .hasInstantiatedNativeClasses()) { 185 .hasInstantiatedNativeClasses()) {
175 void generateClassInfo(Class cls) { 186 fillNativeInfo(jsInterceptorClass);
176 // Property has the form:
177 //
178 // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...",
179 //
180 // If there is no data following a semicolon, the semicolon can be
181 // omitted.
182
183 String formatTags(Iterable<String> tags) {
184 if (tags == null) return '';
185 return (tags.toList()..sort()).join('|');
186 }
187
188 List<Class> extensions = extensionPoints[cls];
189
190 String leafStr = formatTags(leafTags[cls]);
191 String nonleafStr = formatTags(nonleafTags[cls]);
192
193 StringBuffer sb = new StringBuffer(leafStr);
194 if (nonleafStr != '') {
195 sb..write(';')..write(nonleafStr);
196 }
197
198 String encoding = sb.toString();
199
200 if (cls.isNative || encoding != '' || extensions != null) {
201 List<jsAst.Literal> parts = <jsAst.Literal>[js.stringPart(encoding)];
202 if (extensions != null) {
203 parts..add(js.stringPart(';'))
204 ..addAll(
205 js.joinLiterals(extensions.map((Class cls) => cls.name),
206 js.stringPart('|')));
207 }
208 assert(cls.nativeInfo == null);
209 cls.nativeInfo = js.concatenateStrings(parts, addQuotes: true);
210 }
211 }
212 generateClassInfo(jsInterceptorClass);
213 for (Class cls in classes) { 187 for (Class cls in classes) {
214 if (!cls.isNative || neededClasses.contains(cls)) { 188 if (!cls.isNative || neededClasses.contains(cls)) {
215 generateClassInfo(cls); 189 fillNativeInfo(cls);
216 } 190 }
217 } 191 }
218 } 192 }
219 193
220 // TODO(sra): Issue #13731- this is commented out as part of custom 194 // TODO(sra): Issue #13731- this is commented out as part of custom
221 // element constructor work. 195 // element constructor work.
222 // (floitsch: was run on every native class.) 196 // (floitsch: was run on every native class.)
223 //assert(!classElement.hasBackendMembers); 197 //assert(!classElement.hasBackendMembers);
224 198
225 return classes 199 return classes
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
360 // satisfy a check against [element], in which case an interceptor must be 334 // satisfy a check against [element], in which case an interceptor must be
361 // used. We should also use an interceptor if the check can't be satisfied 335 // used. We should also use an interceptor if the check can't be satisfied
362 // by a native class in case we get a native instance that tries to spoof 336 // by a native class in case we get a native instance that tries to spoof
363 // the type info. i.e the criteria for whether or not to use an interceptor 337 // the type info. i.e the criteria for whether or not to use an interceptor
364 // is whether the receiver can be native, not the type of the test. 338 // is whether the receiver can be native, not the type of the test.
365 if (element == null || !element.isClass) return false; 339 if (element == null || !element.isClass) return false;
366 ClassElement cls = element; 340 ClassElement cls = element;
367 if (Elements.isNativeOrExtendsNative(cls)) return true; 341 if (Elements.isNativeOrExtendsNative(cls)) return true;
368 return isSupertypeOfNativeClass(element); 342 return isSupertypeOfNativeClass(element);
369 } 343 }
370 344 }
371 /// Returns a JavaScript template that fills the embedded globals referenced
372 /// by [interceptorsByTagAccess] and [leafTagsAccess].
373 ///
374 /// This code must be invoked for every class that has a native info before
375 /// the program starts.
376 ///
377 /// The [infoAccess] parameter must evaluate to an expression that contains
378 /// the info (as a JavaScript string).
379 ///
380 /// The [constructorAccess] parameter must evaluate to an expression that
381 /// contains the constructor of the class. The constructor's prototype must
382 /// be set up.
383 ///
384 /// The [subclassReadGenerator] function must evaluate to a JS expression
385 /// that returns a reference to the constructor (with evaluated prototype)
386 /// of the given JS expression.
387 ///
388 /// The [interceptorsByTagAccess] must point to the embedded global
389 /// [embeddedNames.INTERCEPTORS_BY_TAG] and must be initialized with an empty
390 /// JS Object (used as a map).
391 ///
392 /// Similarly, the [leafTagsAccess] must point to the embedded global
393 /// [embeddedNames.LEAF_TAGS] and must be initialized with an empty JS Object
394 /// (used as a map).
395 ///
396 /// Both variables are passed in (instead of creating the access here) to
397 /// make sure the caller is aware of these globals.
398 jsAst.Statement buildNativeInfoHandler(
399 jsAst.Expression infoAccess,
400 jsAst.Expression constructorAccess,
401 jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
402 jsAst.Expression interceptorsByTagAccess,
403 jsAst.Expression leafTagsAccess) {
404 jsAst.Expression subclassRead =
405 subclassReadGenerator(js('subclasses[i]', []));
406 return js.statement('''
407 // The native info looks like this:
408 //
409 // HtmlElement: {
410 // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton"
411 //
412 // The first two semicolon-separated parts contain dispatch tags, the
413 // third contains the JavaScript names for classes.
414 //
415 // The tags indicate that JavaScript objects with the dispatch tags
416 // (usually constructor names) HTMLDivElement, HTMLAnchorElement and
417 // HTMLElement all map to the Dart native class named HtmlElement.
418 // The first set is for effective leaf nodes in the hierarchy, the
419 // second set is non-leaf nodes.
420 //
421 // The third part contains the JavaScript names of Dart classes that
422 // extend the native class. Here, FancyButton extends HtmlElement, so
423 // the runtime needs to know that window.HTMLElement.prototype is the
424 // prototype that needs to be extended in creating the custom element.
425 //
426 // The information is used to build tables referenced by
427 // getNativeInterceptor and custom element support.
428 {
429 var nativeSpec = #info.split(";");
430 if (nativeSpec[0]) {
431 var tags = nativeSpec[0].split("|");
432 for (var i = 0; i < tags.length; i++) {
433 #interceptorsByTagAccess[tags[i]] = #constructor;
434 #leafTagsAccess[tags[i]] = true;
435 }
436 }
437 if (nativeSpec[1]) {
438 tags = nativeSpec[1].split("|");
439 if (#allowNativesSubclassing) {
440 if (nativeSpec[2]) {
441 var subclasses = nativeSpec[2].split("|");
442 for (var i = 0; i < subclasses.length; i++) {
443 var subclass = #subclassRead;
444 subclass.#nativeSuperclassTagName = tags[0];
445 }
446 }
447 for (i = 0; i < tags.length; i++) {
448 #interceptorsByTagAccess[tags[i]] = #constructor;
449 #leafTagsAccess[tags[i]] = false;
450 }
451 }
452 }
453 }
454 ''', {'info': infoAccess,
455 'constructor': constructorAccess,
456 'subclassRead': subclassRead,
457 'interceptorsByTagAccess': interceptorsByTagAccess,
458 'leafTagsAccess': leafTagsAccess,
459 'nativeSuperclassTagName': embeddedNames.NATIVE_SUPERCLASS_TAG_NAME,
460 'allowNativesSubclassing': true});
461 }
462 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698