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

Side by Side Diff: lib/compiler/implementation/native_emitter.dart

Issue 10854066: Start moving the JavaScript backend related code into a separate library (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add missing import Created 8 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
« no previous file with comments | « lib/compiler/implementation/leg.dart ('k') | lib/compiler/implementation/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 CodeEmitterTask emitter;
8 CodeBuffer nativeBuffer;
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 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 native methods that are overridden by a native class.
24 // Note that the method that overrides does not have to be native:
25 // it's the overridden method that must make sure it will dispatch
26 // to its subclass if it sees an instance whose class is a subclass.
27 Set<FunctionElement> overriddenMethods;
28
29 // Caches the methods that have a native body.
30 Set<FunctionElement> nativeMethods;
31
32 // Caches the methods that redirect to a JS method.
33 Map<FunctionElement, String> redirectingMethods;
34
35 // Do we need the native emitter to take care of handling
36 // noSuchMethod for us? This flag is set to true in the emitter if
37 // it finds any native class that needs noSuchMethod handling.
38 bool handleNoSuchMethod = false;
39
40 NativeEmitter(this.emitter)
41 : classesWithDynamicDispatch = new Set<ClassElement>(),
42 nativeClasses = new Set<ClassElement>(),
43 subtypes = new Map<ClassElement, List<ClassElement>>(),
44 directSubtypes = new Map<ClassElement, List<ClassElement>>(),
45 overriddenMethods = new Set<FunctionElement>(),
46 nativeMethods = new Set<FunctionElement>(),
47 redirectingMethods = new Map<FunctionElement, String>(),
48 nativeBuffer = new CodeBuffer();
49
50 Compiler get compiler() => emitter.compiler;
51
52 void addRedirectingMethod(FunctionElement element, String name) {
53 redirectingMethods[element] = name;
54 }
55
56 String get dynamicName() {
57 Element element = compiler.findHelper(
58 const SourceString('dynamicFunction'));
59 return compiler.namer.isolateAccess(element);
60 }
61
62 String get dynamicSetMetadataName() {
63 Element element = compiler.findHelper(
64 const SourceString('dynamicSetMetadata'));
65 return compiler.namer.isolateAccess(element);
66 }
67
68 String get typeNameOfName() {
69 Element element = compiler.findHelper(
70 const SourceString('getTypeNameOf'));
71 return compiler.namer.isolateAccess(element);
72 }
73
74 String get defPropName() {
75 Element element = compiler.findHelper(
76 const SourceString('defineProperty'));
77 return compiler.namer.isolateAccess(element);
78 }
79
80 String get toStringHelperName() {
81 Element element = compiler.findHelper(
82 const SourceString('toStringForNativeObject'));
83 return compiler.namer.isolateAccess(element);
84 }
85
86 String get defineNativeClassName()
87 => '${compiler.namer.CURRENT_ISOLATE}.\$defineNativeClass';
88
89 String get defineNativeClassFunction() {
90 return """
91 function(cls, fields, methods) {
92 var generateGetterSetter = ${emitter.generateGetterSetterFunction};
93 for (var i = 0; i < fields.length; i++) {
94 generateGetterSetter(fields[i], methods);
95 }
96 for (var method in methods) {
97 $dynamicName(method)[cls] = methods[method];
98 }
99 }""";
100 }
101
102 void generateNativeLiteral(ClassElement classElement) {
103 String quotedNative = classElement.nativeName.slowToString();
104 String nativeCode = quotedNative.substring(2, quotedNative.length - 1);
105 String className = compiler.namer.getName(classElement);
106 nativeBuffer.add(className);
107 nativeBuffer.add(' = ');
108 nativeBuffer.add(nativeCode);
109 nativeBuffer.add(';\n');
110
111 void defineInstanceMember(String name, CodeBuffer value) {
112 nativeBuffer.add("$className.$name = $value;\n");
113 }
114
115 for (Element member in classElement.members) {
116 if (member.isInstanceMember()) {
117 emitter.addInstanceMember(member, defineInstanceMember);
118 }
119 }
120 }
121
122 bool isNativeLiteral(String quotedName) {
123 return quotedName[1] === '=';
124 }
125
126 bool isNativeGlobal(String quotedName) {
127 return quotedName[1] === '@';
128 }
129
130 String toNativeName(ClassElement cls) {
131 String quotedName = cls.nativeName.slowToString();
132 if (isNativeGlobal(quotedName)) {
133 // Global object, just be like the other types for now.
134 return quotedName.substring(3, quotedName.length - 1);
135 } else {
136 return quotedName.substring(2, quotedName.length - 1);
137 }
138 }
139
140 void generateNativeClass(ClassElement classElement) {
141 nativeClasses.add(classElement);
142
143 assert(classElement.backendMembers.isEmpty());
144 String quotedName = classElement.nativeName.slowToString();
145 if (isNativeLiteral(quotedName)) {
146 generateNativeLiteral(classElement);
147 // The native literal kind needs to be dealt with specially when
148 // generating code for it.
149 return;
150 }
151
152 CodeBuffer fieldBuffer = new CodeBuffer();
153 emitter.emitClassFields(classElement, fieldBuffer);
154
155 CodeBuffer methodBuffer = new CodeBuffer();
156 emitter.emitInstanceMembers(classElement, methodBuffer, false);
157
158 if (methodBuffer.isEmpty() && fieldBuffer.isEmpty()) return;
159
160 String nativeName = toNativeName(classElement);
161 nativeBuffer.add("$defineNativeClassName('$nativeName', [");
162 nativeBuffer.add(fieldBuffer);
163 nativeBuffer.add('], {');
164 nativeBuffer.add(methodBuffer);
165 nativeBuffer.add('\n});\n\n');
166
167 classesWithDynamicDispatch.add(classElement);
168 }
169
170 List<ClassElement> getDirectSubclasses(ClassElement cls) {
171 List<ClassElement> result = directSubtypes[cls];
172 return result === null ? const<ClassElement>[] : result;
173 }
174
175 void potentiallyConvertDartClosuresToJs(CodeBuffer code,
176 FunctionElement member,
177 List<String> argumentsBuffer) {
178 FunctionSignature parameters = member.computeSignature(compiler);
179 Element converter =
180 compiler.findHelper(const SourceString('convertDartClosureToJS'));
181 String closureConverter = compiler.namer.isolateAccess(converter);
182 parameters.forEachParameter((Element parameter) {
183 String name = parameter.name.slowToString();
184 // If [name] is not in [argumentsBuffer], then the parameter is
185 // an optional parameter that was not provided for that stub.
186 if (argumentsBuffer.indexOf(name) == -1) return;
187 Type type = parameter.computeType(compiler);
188 if (type is FunctionType) {
189 int arity = type.computeArity();
190 code.add(' $name = $closureConverter($name, $arity);\n');
191 }
192 });
193 }
194
195 String generateParameterStub(Element member,
196 String invocationName,
197 String stubParameters,
198 List<String> argumentsBuffer,
199 int indexOfLastOptionalArgumentInParameters,
200 CodeBuffer buffer) {
201 // The target JS function may check arguments.length so we need to
202 // make sure not to pass any unspecified optional arguments to it.
203 // For example, for the following Dart method:
204 // foo([x, y, z]);
205 // The call:
206 // foo(y: 1)
207 // must be turned into a JS call to:
208 // foo(null, y).
209
210 List<String> nativeArgumentsBuffer = argumentsBuffer.getRange(
211 0, indexOfLastOptionalArgumentInParameters + 1);
212
213 ClassElement classElement = member.enclosingElement;
214 String nativeName = classElement.nativeName.slowToString();
215 String nativeArguments = Strings.join(nativeArgumentsBuffer, ",");
216
217 CodeBuffer code = new CodeBuffer();
218 potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer);
219
220 if (!nativeMethods.contains(member)) {
221 // When calling a method that has a native body, we call it
222 // with our calling conventions.
223 String arguments = Strings.join(argumentsBuffer, ",");
224 code.add(' return this.${compiler.namer.getName(member)}($arguments)');
225 } else {
226 // When calling a JS method, we call it with the native name.
227 String name = redirectingMethods[member];
228 if (name === null) name = member.name.slowToString();
229 code.add(' return this.$name($nativeArguments);');
230 }
231
232 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) {
233 // Call the method directly.
234 buffer.add(code.toString());
235 } else {
236 native.generateMethodWithPrototypeCheck(
237 compiler, buffer, invocationName, code.toString(), stubParameters);
238 }
239 }
240
241 void emitDynamicDispatchMetadata() {
242 if (classesWithDynamicDispatch.isEmpty()) return;
243 int length = classesWithDynamicDispatch.length;
244 nativeBuffer.add('// $length dynamic classes.\n');
245
246 // Build a pre-order traversal over all the classes and their subclasses.
247 Set<ClassElement> seen = new Set<ClassElement>();
248 List<ClassElement> classes = <ClassElement>[];
249 void visit(ClassElement cls) {
250 if (seen.contains(cls)) return;
251 seen.add(cls);
252 for (final ClassElement subclass in getDirectSubclasses(cls)) {
253 visit(subclass);
254 }
255 classes.add(cls);
256 }
257 for (final ClassElement classElement in classesWithDynamicDispatch) {
258 visit(classElement);
259 }
260
261 Collection<ClassElement> dispatchClasses = classes.filter(
262 (cls) => !getDirectSubclasses(cls).isEmpty() &&
263 classesWithDynamicDispatch.contains(cls));
264
265 nativeBuffer.add('// ${classes.length} classes\n');
266 Collection<ClassElement> classesThatHaveSubclasses = classes.filter(
267 (ClassElement t) => !getDirectSubclasses(t).isEmpty());
268 nativeBuffer.add('// ${classesThatHaveSubclasses.length} !leaf\n');
269
270 // Generate code that builds the map from cls tags used in dynamic dispatch
271 // to the set of cls tags of classes that extend (TODO: or implement) those
272 // classes. The set is represented as a string of tags joined with '|'.
273 // This is easily split into an array of tags, or converted into a regexp.
274 //
275 // To reduce the size of the sets, subsets are CSE-ed out into variables.
276 // The sets could be much smaller if we could make assumptions about the
277 // cls tags of other classes (which are constructor names or part of the
278 // result of Object.protocls.toString). For example, if objects that are
279 // Dart objects could be easily excluded, then we might be able to simplify
280 // the test, replacing dozens of HTMLxxxElement classes with the regexp
281 // /HTML.*Element/.
282
283 // Temporary variables for common substrings.
284 List<String> varNames = <String>[];
285 // var -> expression
286 Map<String, String> varDefns = <String>{};
287 // tag -> expression (a string or a variable)
288 Map<ClassElement, String> tagDefns = new Map<ClassElement, String>();
289
290 String makeExpression(ClassElement classElement) {
291 // Expression fragments for this set of cls keys.
292 List<String> expressions = <String>[];
293 // TODO: Remove if cls is abstract.
294 List<String> subtags = [toNativeName(classElement)];
295 void walk(ClassElement cls) {
296 for (final ClassElement subclass in getDirectSubclasses(cls)) {
297 ClassElement tag = subclass;
298 String existing = tagDefns[tag];
299 if (existing == null) {
300 subtags.add(toNativeName(tag));
301 walk(subclass);
302 } else {
303 if (varDefns.containsKey(existing)) {
304 expressions.add(existing);
305 } else {
306 String varName = 'v${varNames.length}/*${tag}*/';
307 varNames.add(varName);
308 varDefns[varName] = existing;
309 tagDefns[tag] = varName;
310 expressions.add(varName);
311 }
312 }
313 }
314 }
315 walk(classElement);
316 String constantPart = "'${Strings.join(subtags, '|')}'";
317 if (constantPart != "''") expressions.add(constantPart);
318 String expression;
319 if (expressions.length == 1) {
320 expression = expressions[0];
321 } else {
322 expression = "[${Strings.join(expressions, ',')}].join('|')";
323 }
324 return expression;
325 }
326
327 for (final ClassElement classElement in dispatchClasses) {
328 tagDefns[classElement] = makeExpression(classElement);
329 }
330
331 // Write out a thunk that builds the metadata.
332 if (!tagDefns.isEmpty()) {
333 nativeBuffer.add('(function(){\n');
334
335 for (final String varName in varNames) {
336 nativeBuffer.add(' var ${varName} = ${varDefns[varName]};\n');
337 }
338
339 nativeBuffer.add(' var table = [\n');
340 nativeBuffer.add(
341 ' // [dynamic-dispatch-tag, '
342 'tags of classes implementing dynamic-dispatch-tag]');
343 bool needsComma = false;
344 List<String> entries = <String>[];
345 for (final ClassElement cls in dispatchClasses) {
346 String clsName = toNativeName(cls);
347 entries.add("\n ['$clsName', ${tagDefns[cls]}]");
348 }
349 nativeBuffer.add(Strings.join(entries, ','));
350 nativeBuffer.add('];\n');
351 nativeBuffer.add('$dynamicSetMetadataName(table);\n');
352
353 nativeBuffer.add('})();\n');
354 }
355 }
356
357 bool isSupertypeOfNativeClass(Element element) {
358 if (element.isTypeVariable()) {
359 compiler.cancel("Is check for type variable", element: element);
360 return false;
361 }
362 if (element.computeType(compiler) is FunctionType) return false;
363
364 if (!element.isClass()) {
365 compiler.cancel("Is check does not handle element", element: element);
366 return false;
367 }
368
369 return subtypes[element] !== null;
370 }
371
372 bool requiresNativeIsCheck(Element element) {
373 if (!element.isClass()) return false;
374 ClassElement cls = element;
375 if (cls.isNative()) return true;
376 return isSupertypeOfNativeClass(element);
377 }
378
379 void emitIsChecks(Map<String, String> objectProperties) {
380 for (Element type in compiler.codegenWorld.isChecks) {
381 if (!requiresNativeIsCheck(type)) continue;
382 String name = compiler.namer.operatorIs(type);
383 objectProperties[name] = 'function() { return false; }';
384 }
385 }
386
387 void assembleCode(CodeBuffer targetBuffer) {
388 if (nativeClasses.isEmpty()) return;
389 emitDynamicDispatchMetadata();
390 targetBuffer.add('$defineNativeClassName = '
391 '$defineNativeClassFunction;\n\n');
392
393 // Because of native classes, we have to generate some is checks
394 // by calling a method, instead of accessing a property. So we
395 // attach to the JS Object prototype these methods that return
396 // false, and will be overridden by subclasses when they have to
397 // return true.
398 Map<String, String> objectProperties = new Map<String, String>();
399 emitIsChecks(objectProperties);
400
401 // In order to have the toString method on every native class,
402 // we must patch the JS Object prototype with a helper method.
403 String toStringName = compiler.namer.instanceMethodName(
404 null, const SourceString('toString'), 0);
405 objectProperties[toStringName] =
406 'function() { return $toStringHelperName(this); }';
407
408 // If the native emitter has been asked to take care of the
409 // noSuchMethod handlers, we do that now.
410 if (handleNoSuchMethod) {
411 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) {
412 objectProperties[name] = buffer.toString();
413 });
414 }
415
416 // If we have any properties to add to Object.prototype, we run
417 // through them and add them using defineProperty.
418 if (!objectProperties.isEmpty()) {
419 targetBuffer.add("(function(table) {\n"
420 " for (var key in table) {\n"
421 " $defPropName(Object.prototype, key, table[key]);\n"
422 " }\n"
423 "})({\n");
424 bool first = true;
425 objectProperties.forEach((String name, String function) {
426 if (!first) targetBuffer.add(",\n");
427 targetBuffer.add(" $name: $function");
428 first = false;
429 });
430 targetBuffer.add("\n});\n\n");
431 }
432 targetBuffer.add('$nativeBuffer');
433 targetBuffer.add('\n');
434 }
435 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/leg.dart ('k') | lib/compiler/implementation/native_handler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698