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

Side by Side Diff: frog/leg/native_emitter.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 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
« no previous file with comments | « frog/leg/namer.dart ('k') | frog/leg/native_handler.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 class NativeEmitter {
6
7 Compiler compiler;
8 StringBuffer buffer;
9
10 // Classes that participate in dynamic dispatch. These are the
11 // classes that contain used members.
12 Set<ClassElement> classesWithDynamicDispatch;
13
14 // Native classes found in the application.
15 Set<ClassElement> nativeClasses;
16
17 // Caches the direct native subtypes of a native class.
18 Map<ClassElement, List<ClassElement>> subtypes;
19
20 // Caches the native methods that are overridden by a native class.
21 // Note that the method that overrides does not have to be native:
22 // it's the overridden method that must make sure it will dispatch
23 // to its subclass if it sees an instance whose class is a subclass.
24 Set<FunctionElement> overriddenMethods;
25
26 NativeEmitter(this.compiler)
27 : classesWithDynamicDispatch = new Set<ClassElement>(),
28 nativeClasses = new Set<ClassElement>(),
29 subtypes = new Map<ClassElement, List<ClassElement>>(),
30 overriddenMethods = new Set<FunctionElement>(),
31 buffer = new StringBuffer();
32
33 String get dynamicName() {
34 Element element = compiler.findHelper(
35 const SourceString('dynamicFunction'));
36 return compiler.namer.isolateAccess(element);
37 }
38
39 String get dynamicSetMetadataName() {
40 Element element = compiler.findHelper(
41 const SourceString('dynamicSetMetadata'));
42 return compiler.namer.isolateAccess(element);
43 }
44
45 String get typeNameOfName() {
46 Element element = compiler.findHelper(
47 const SourceString('getTypeNameOf'));
48 return compiler.namer.isolateAccess(element);
49 }
50
51 String get defPropName() {
52 Element element = compiler.findHelper(
53 const SourceString('defineProperty'));
54 return compiler.namer.isolateAccess(element);
55 }
56
57 String get toStringHelperName() {
58 Element element = compiler.findHelper(
59 const SourceString('toStringForNativeObject'));
60 return compiler.namer.isolateAccess(element);
61 }
62
63 void generateNativeLiteral(ClassElement classElement) {
64 String quotedNative = classElement.nativeName.slowToString();
65 String nativeCode = quotedNative.substring(2, quotedNative.length - 1);
66 String className = compiler.namer.getName(classElement);
67 buffer.add(className);
68 buffer.add(' = ');
69 buffer.add(nativeCode);
70 buffer.add(';\n');
71
72 String attachTo(name) => "$className.$name";
73
74 for (Element member in classElement.members) {
75 if (member.isInstanceMember()) {
76 compiler.emitter.addInstanceMember(
77 member, attachTo, buffer, isNative: true);
78 }
79 }
80 }
81
82 bool isNativeLiteral(String quotedName) {
83 return quotedName[1] === '=';
84 }
85
86 bool isNativeGlobal(String quotedName) {
87 return quotedName[1] === '@';
88 }
89
90 String toNativeName(ClassElement cls) {
91 String quotedName = cls.nativeName.slowToString();
92 if (isNativeGlobal(quotedName)) {
93 // Global object, just be like the other types for now.
94 return quotedName.substring(3, quotedName.length - 1);
95 } else {
96 return quotedName.substring(2, quotedName.length - 1);
97 }
98 }
99
100 void generateNativeClass(ClassElement classElement) {
101 nativeClasses.add(classElement);
102
103 assert(classElement.backendMembers.isEmpty());
104 String quotedName = classElement.nativeName.slowToString();
105 if (isNativeLiteral(quotedName)) {
106 generateNativeLiteral(classElement);
107 // The native literal kind needs to be dealt with specially when
108 // generating code for it.
109 return;
110 }
111
112 String nativeName = toNativeName(classElement);
113 bool hasUsedSelectors = false;
114
115 String attachTo(String name) {
116 hasUsedSelectors = true;
117 return "$dynamicName('$name').$nativeName";
118 }
119
120 for (Element member in classElement.members) {
121 if (member.isInstanceMember()) {
122 compiler.emitter.addInstanceMember(
123 member, attachTo, buffer, isNative: true);
124 }
125 }
126
127 compiler.emitter.generateTypeTests(classElement, (Element other) {
128 assert(requiresNativeIsCheck(other));
129 buffer.add('${attachTo(compiler.namer.operatorIs(other))} = ');
130 buffer.add('function() { return true; };\n');
131 });
132
133 if (hasUsedSelectors) classesWithDynamicDispatch.add(classElement);
134 }
135
136 List<ClassElement> getDirectSubclasses(ClassElement cls) {
137 List<ClassElement> result = subtypes[cls];
138 if (result === null) result = const<ClassElement>[];
139 return result;
140 }
141
142 void emitParameterStub(Element member,
143 String invocationName,
144 String stubParameters,
145 List<String> argumentsBuffer,
146 int indexOfLastOptionalArgumentInParameters) {
147 // The target JS function may check arguments.length so we need to
148 // make sure not to pass any unspecified optional arguments to it.
149 // For example, for the following Dart method:
150 // foo([x, y, z]);
151 // The call:
152 // foo(y: 1)
153 // must be turned into a JS call to:
154 // foo(null, y).
155
156 List<String> nativeArgumentsBuffer = argumentsBuffer.getRange(
157 0, indexOfLastOptionalArgumentInParameters + 1);
158
159 ClassElement classElement = member.enclosingElement;
160 String nativeName = classElement.nativeName.slowToString();
161 String nativeArguments = Strings.join(nativeArgumentsBuffer, ",");
162
163 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) {
164 // Call the method directly.
165 buffer.add(' return this.${member.name.slowToString()}');
166 buffer.add('($nativeArguments)');
167 return;
168 }
169
170 // If the method is overridden, we must check if the prototype of
171 // 'this' has the method available. Otherwise, we may end up
172 // calling the method from the super class. If the method is not
173 // available, we make a direct call to
174 // Object.prototype.$invocationName. This method will patch the
175 // prototype of 'this' to the real method.
176
177 buffer.add(' if (Object.getPrototypeOf(this).hasOwnProperty(');
178 buffer.add("'$invocationName')) {\n");
179 buffer.add(' return this.${member.name.slowToString()}');
180 buffer.add('($nativeArguments)');
181 buffer.add('\n }\n');
182 buffer.add(' return Object.prototype.$invocationName.call(this');
183 buffer.add(stubParameters == '' ? '' : ', $stubParameters');
184 buffer.add(');');
185 }
186
187 void emitDynamicDispatchMetadata() {
188 if (classesWithDynamicDispatch.isEmpty()) return;
189 buffer.add('// ${classesWithDynamicDispatch.length} dynamic classes.\n');
190
191 // Build a pre-order traversal over all the classes and their subclasses.
192 Set<ClassElement> seen = new Set<ClassElement>();
193 List<ClassElement> classes = <ClassElement>[];
194 void visit(ClassElement cls) {
195 if (seen.contains(cls)) return;
196 seen.add(cls);
197 for (final ClassElement subclass in getDirectSubclasses(cls)) {
198 visit(subclass);
199 }
200 classes.add(cls);
201 }
202 for (final ClassElement cls in classesWithDynamicDispatch) {
203 visit(cls);
204 }
205
206 Collection<ClassElement> dispatchClasses = classes.filter(
207 (cls) => !getDirectSubclasses(cls).isEmpty() &&
208 classesWithDynamicDispatch.contains(cls));
209
210 buffer.add('// ${classes.length} classes\n');
211 Collection<ClassElement> classesThatHaveSubclasses = classes.filter(
212 (ClassElement t) => !getDirectSubclasses(t).isEmpty());
213 buffer.add('// ${classesThatHaveSubclasses.length} !leaf\n');
214
215 // Generate code that builds the map from cls tags used in dynamic dispatch
216 // to the set of cls tags of classes that extend (TODO: or implement) those
217 // classes. The set is represented as a string of tags joined with '|'.
218 // This is easily split into an array of tags, or converted into a regexp.
219 //
220 // To reduce the size of the sets, subsets are CSE-ed out into variables.
221 // The sets could be much smaller if we could make assumptions about the
222 // cls tags of other classes (which are constructor names or part of the
223 // result of Object.protocls.toString). For example, if objects that are
224 // Dart objects could be easily excluded, then we might be able to simplify
225 // the test, replacing dozens of HTMLxxxElement classes with the regexp
226 // /HTML.*Element/.
227
228 // Temporary variables for common substrings.
229 List<String> varNames = <String>[];
230 // var -> expression
231 Map<String, String> varDefns = <String>{};
232 // tag -> expression (a string or a variable)
233 Map<ClassElement, String> tagDefns = new Map<ClassElement, String>();
234
235 String makeExpression(ClassElement cls) {
236 // Expression fragments for this set of cls keys.
237 List<String> expressions = <String>[];
238 // TODO: Remove if cls is abstract.
239 List<String> subtags = [toNativeName(cls)];
240 void walk(ClassElement cls) {
241 for (final ClassElement subclass in getDirectSubclasses(cls)) {
242 ClassElement tag = subclass;
243 String existing = tagDefns[tag];
244 if (existing == null) {
245 subtags.add(toNativeName(tag));
246 walk(subclass);
247 } else {
248 if (varDefns.containsKey(existing)) {
249 expressions.add(existing);
250 } else {
251 String varName = 'v${varNames.length}/*${tag}*/';
252 varNames.add(varName);
253 varDefns[varName] = existing;
254 tagDefns[tag] = varName;
255 expressions.add(varName);
256 }
257 }
258 }
259 }
260 walk(cls);
261 String constantPart = "'${Strings.join(subtags, '|')}'";
262 if (constantPart != "''") expressions.add(constantPart);
263 String expression;
264 if (expressions.length == 1) {
265 expression = expressions[0];
266 } else {
267 expression = "[${Strings.join(expressions, ',')}].join('|')";
268 }
269 return expression;
270 }
271
272 for (final ClassElement cls in dispatchClasses) {
273 tagDefns[cls] = makeExpression(cls);
274 }
275
276 // Write out a thunk that builds the metadata.
277
278 if (!tagDefns.isEmpty()) {
279 buffer.add('(function(){\n');
280
281 for (final String varName in varNames) {
282 buffer.add(' var ${varName} = ${varDefns[varName]};\n');
283 }
284
285 buffer.add(' var table = [\n');
286 buffer.add(
287 ' // [dynamic-dispatch-tag, '
288 'tags of classes implementing dynamic-dispatch-tag]');
289 bool needsComma = false;
290 List<String> entries = <String>[];
291 for (final ClassElement cls in dispatchClasses) {
292 String clsName = toNativeName(cls);
293 entries.add("\n ['$clsName', ${tagDefns[cls]}]");
294 }
295 buffer.add(Strings.join(entries, ','));
296 buffer.add('];\n');
297 buffer.add('$dynamicSetMetadataName(table);\n');
298
299 buffer.add('})();\n');
300 }
301 }
302
303 bool isSupertypeOfNativeClass(Element element) {
304 if (element.isTypeVariable()) {
305 compiler.cancel("Is check for type variable", element: work.element);
306 return false;
307 }
308 if (element.computeType(compiler) is FunctionType) return false;
309
310 if (!element.isClass()) {
311 compiler.cancel("Is check does not handle element", element: element);
312 return false;
313 }
314
315 return subtypes[element] !== null;
316 }
317
318 bool requiresNativeIsCheck(Element element) {
319 if (!element.isClass()) return false;
320 ClassElement cls = element;
321 if (cls.isNative()) return true;
322 return isSupertypeOfNativeClass(element);
323 }
324
325 void emitIsChecks(StringBuffer buffer) {
326 for (Element type in compiler.universe.isChecks) {
327 if (!requiresNativeIsCheck(type)) continue;
328 String name = compiler.namer.operatorIs(type);
329 buffer.add("$defPropName(Object.prototype, '$name', ");
330 buffer.add('function() { return false; });\n');
331 }
332 }
333
334 void assembleCode(StringBuffer other) {
335 if (nativeClasses.isEmpty()) return;
336
337 // Because of native classes, we have to generate some is checks
338 // by calling a method, instead of accessing a property. So we
339 // attach to the JS Object prototype these methods that return
340 // false, and will be overridden by subclasses when they have to
341 // return true.
342 StringBuffer objectProperties = new StringBuffer();
343 emitIsChecks(objectProperties);
344
345 // In order to have the toString method on every native class,
346 // we must patch the JS Object prototype with a helper method.
347 String toStringName = compiler.namer.instanceMethodName(
348 null, const SourceString('toString'), 0);
349 objectProperties.add("$defPropName(Object.prototype, '$toStringName', ");
350 objectProperties.add(
351 'function() { return $toStringHelperName(this); });\n');
352
353 // Finally, emit the code in the main buffer.
354 other.add('(function() {\n$objectProperties$buffer\n})();\n');
355 }
356 }
OLDNEW
« no previous file with comments | « frog/leg/namer.dart ('k') | frog/leg/native_handler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698