OLD | NEW |
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 class NativeEmitter { | 5 class NativeEmitter { |
6 | 6 |
7 CodeEmitterTask emitter; | 7 CodeEmitterTask emitter; |
8 CodeBuffer nativeBuffer; | 8 CodeBuffer nativeBuffer; |
9 | 9 |
10 // Classes that participate in dynamic dispatch. These are the | 10 // Classes that participate in dynamic dispatch. These are the |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 // Global object, just be like the other types for now. | 146 // Global object, just be like the other types for now. |
147 return quotedName.substring(3, quotedName.length - 1); | 147 return quotedName.substring(3, quotedName.length - 1); |
148 } else { | 148 } else { |
149 return quotedName.substring(2, quotedName.length - 1); | 149 return quotedName.substring(2, quotedName.length - 1); |
150 } | 150 } |
151 } | 151 } |
152 | 152 |
153 void generateNativeClass(ClassElement classElement) { | 153 void generateNativeClass(ClassElement classElement) { |
154 nativeClasses.add(classElement); | 154 nativeClasses.add(classElement); |
155 | 155 |
156 assert(classElement.backendMembers.isEmpty()); | 156 assert(classElement.backendMembers.isEmpty); |
157 String quotedName = classElement.nativeName.slowToString(); | 157 String quotedName = classElement.nativeName.slowToString(); |
158 if (isNativeLiteral(quotedName)) { | 158 if (isNativeLiteral(quotedName)) { |
159 generateNativeLiteral(classElement); | 159 generateNativeLiteral(classElement); |
160 // The native literal kind needs to be dealt with specially when | 160 // The native literal kind needs to be dealt with specially when |
161 // generating code for it. | 161 // generating code for it. |
162 return; | 162 return; |
163 } | 163 } |
164 | 164 |
165 CodeBuffer fieldBuffer = new CodeBuffer(); | 165 CodeBuffer fieldBuffer = new CodeBuffer(); |
166 CodeBuffer getterSetterBuffer = new CodeBuffer(); | 166 CodeBuffer getterSetterBuffer = new CodeBuffer(); |
167 | 167 |
168 emitter.emitClassFields(classElement, fieldBuffer); | 168 emitter.emitClassFields(classElement, fieldBuffer); |
169 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, | 169 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, |
170 omitLeadingComma: true); | 170 omitLeadingComma: true); |
171 | 171 |
172 CodeBuffer methodBuffer = new CodeBuffer(); | 172 CodeBuffer methodBuffer = new CodeBuffer(); |
173 emitter.emitInstanceMembers(classElement, methodBuffer, false); | 173 emitter.emitInstanceMembers(classElement, methodBuffer, false); |
174 | 174 |
175 if (methodBuffer.isEmpty() | 175 if (methodBuffer.isEmpty |
176 && fieldBuffer.isEmpty() | 176 && fieldBuffer.isEmpty |
177 && getterSetterBuffer.isEmpty()) { | 177 && getterSetterBuffer.isEmpty) { |
178 return; | 178 return; |
179 } | 179 } |
180 | 180 |
181 String nativeName = toNativeName(classElement); | 181 String nativeName = toNativeName(classElement); |
182 nativeBuffer.add("$defineNativeClassName('$nativeName', "); | 182 nativeBuffer.add("$defineNativeClassName('$nativeName', "); |
183 nativeBuffer.add('{'); | 183 nativeBuffer.add('{'); |
184 bool firstInMap = true; | 184 bool firstInMap = true; |
185 if (!fieldBuffer.isEmpty()) { | 185 if (!fieldBuffer.isEmpty) { |
186 firstInMap = false; | 186 firstInMap = false; |
187 nativeBuffer.add(fieldBuffer); | 187 nativeBuffer.add(fieldBuffer); |
188 } | 188 } |
189 if (!getterSetterBuffer.isEmpty()) { | 189 if (!getterSetterBuffer.isEmpty) { |
190 if (!firstInMap) nativeBuffer.add(","); | 190 if (!firstInMap) nativeBuffer.add(","); |
191 firstInMap = false; | 191 firstInMap = false; |
192 nativeBuffer.add("\n "); | 192 nativeBuffer.add("\n "); |
193 nativeBuffer.add(getterSetterBuffer); | 193 nativeBuffer.add(getterSetterBuffer); |
194 } | 194 } |
195 if (!methodBuffer.isEmpty()) { | 195 if (!methodBuffer.isEmpty) { |
196 if (!firstInMap) nativeBuffer.add(","); | 196 if (!firstInMap) nativeBuffer.add(","); |
197 nativeBuffer.add(methodBuffer); | 197 nativeBuffer.add(methodBuffer); |
198 } | 198 } |
199 nativeBuffer.add('\n});\n\n'); | 199 nativeBuffer.add('\n});\n\n'); |
200 | 200 |
201 classesWithDynamicDispatch.add(classElement); | 201 classesWithDynamicDispatch.add(classElement); |
202 } | 202 } |
203 | 203 |
204 List<ClassElement> getDirectSubclasses(ClassElement cls) { | 204 List<ClassElement> getDirectSubclasses(ClassElement cls) { |
205 List<ClassElement> result = directSubtypes[cls]; | 205 List<ClassElement> result = directSubtypes[cls]; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { | 268 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { |
269 // Call the method directly. | 269 // Call the method directly. |
270 buffer.add(code.toString()); | 270 buffer.add(code.toString()); |
271 } else { | 271 } else { |
272 native.generateMethodWithPrototypeCheck( | 272 native.generateMethodWithPrototypeCheck( |
273 compiler, buffer, invocationName, code.toString(), stubParameters); | 273 compiler, buffer, invocationName, code.toString(), stubParameters); |
274 } | 274 } |
275 } | 275 } |
276 | 276 |
277 void emitDynamicDispatchMetadata() { | 277 void emitDynamicDispatchMetadata() { |
278 if (classesWithDynamicDispatch.isEmpty()) return; | 278 if (classesWithDynamicDispatch.isEmpty) return; |
279 int length = classesWithDynamicDispatch.length; | 279 int length = classesWithDynamicDispatch.length; |
280 nativeBuffer.add('// $length dynamic classes.\n'); | 280 nativeBuffer.add('// $length dynamic classes.\n'); |
281 | 281 |
282 // Build a pre-order traversal over all the classes and their subclasses. | 282 // Build a pre-order traversal over all the classes and their subclasses. |
283 Set<ClassElement> seen = new Set<ClassElement>(); | 283 Set<ClassElement> seen = new Set<ClassElement>(); |
284 List<ClassElement> classes = <ClassElement>[]; | 284 List<ClassElement> classes = <ClassElement>[]; |
285 void visit(ClassElement cls) { | 285 void visit(ClassElement cls) { |
286 if (seen.contains(cls)) return; | 286 if (seen.contains(cls)) return; |
287 seen.add(cls); | 287 seen.add(cls); |
288 for (final ClassElement subclass in getDirectSubclasses(cls)) { | 288 for (final ClassElement subclass in getDirectSubclasses(cls)) { |
289 visit(subclass); | 289 visit(subclass); |
290 } | 290 } |
291 classes.add(cls); | 291 classes.add(cls); |
292 } | 292 } |
293 for (final ClassElement classElement in classesWithDynamicDispatch) { | 293 for (final ClassElement classElement in classesWithDynamicDispatch) { |
294 visit(classElement); | 294 visit(classElement); |
295 } | 295 } |
296 | 296 |
297 Collection<ClassElement> dispatchClasses = classes.filter( | 297 Collection<ClassElement> dispatchClasses = classes.filter( |
298 (cls) => !getDirectSubclasses(cls).isEmpty() && | 298 (cls) => !getDirectSubclasses(cls).isEmpty && |
299 classesWithDynamicDispatch.contains(cls)); | 299 classesWithDynamicDispatch.contains(cls)); |
300 | 300 |
301 nativeBuffer.add('// ${classes.length} classes\n'); | 301 nativeBuffer.add('// ${classes.length} classes\n'); |
302 Collection<ClassElement> classesThatHaveSubclasses = classes.filter( | 302 Collection<ClassElement> classesThatHaveSubclasses = classes.filter( |
303 (ClassElement t) => !getDirectSubclasses(t).isEmpty()); | 303 (ClassElement t) => !getDirectSubclasses(t).isEmpty); |
304 nativeBuffer.add('// ${classesThatHaveSubclasses.length} !leaf\n'); | 304 nativeBuffer.add('// ${classesThatHaveSubclasses.length} !leaf\n'); |
305 | 305 |
306 // Generate code that builds the map from cls tags used in dynamic dispatch | 306 // Generate code that builds the map from cls tags used in dynamic dispatch |
307 // to the set of cls tags of classes that extend (TODO: or implement) those | 307 // to the set of cls tags of classes that extend (TODO: or implement) those |
308 // classes. The set is represented as a string of tags joined with '|'. | 308 // classes. The set is represented as a string of tags joined with '|'. |
309 // This is easily split into an array of tags, or converted into a regexp. | 309 // This is easily split into an array of tags, or converted into a regexp. |
310 // | 310 // |
311 // To reduce the size of the sets, subsets are CSE-ed out into variables. | 311 // To reduce the size of the sets, subsets are CSE-ed out into variables. |
312 // The sets could be much smaller if we could make assumptions about the | 312 // The sets could be much smaller if we could make assumptions about the |
313 // cls tags of other classes (which are constructor names or part of the | 313 // cls tags of other classes (which are constructor names or part of the |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
358 expression = "[${Strings.join(expressions, ',')}].join('|')"; | 358 expression = "[${Strings.join(expressions, ',')}].join('|')"; |
359 } | 359 } |
360 return expression; | 360 return expression; |
361 } | 361 } |
362 | 362 |
363 for (final ClassElement classElement in dispatchClasses) { | 363 for (final ClassElement classElement in dispatchClasses) { |
364 tagDefns[classElement] = makeExpression(classElement); | 364 tagDefns[classElement] = makeExpression(classElement); |
365 } | 365 } |
366 | 366 |
367 // Write out a thunk that builds the metadata. | 367 // Write out a thunk that builds the metadata. |
368 if (!tagDefns.isEmpty()) { | 368 if (!tagDefns.isEmpty) { |
369 nativeBuffer.add('(function(){\n'); | 369 nativeBuffer.add('(function(){\n'); |
370 | 370 |
371 for (final String varName in varNames) { | 371 for (final String varName in varNames) { |
372 nativeBuffer.add(' var ${varName} = ${varDefns[varName]};\n'); | 372 nativeBuffer.add(' var ${varName} = ${varDefns[varName]};\n'); |
373 } | 373 } |
374 | 374 |
375 nativeBuffer.add(' var table = [\n'); | 375 nativeBuffer.add(' var table = [\n'); |
376 nativeBuffer.add( | 376 nativeBuffer.add( |
377 ' // [dynamic-dispatch-tag, ' | 377 ' // [dynamic-dispatch-tag, ' |
378 'tags of classes implementing dynamic-dispatch-tag]'); | 378 'tags of classes implementing dynamic-dispatch-tag]'); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
419 void emitIsChecks(Map<String, String> objectProperties) { | 419 void emitIsChecks(Map<String, String> objectProperties) { |
420 for (Element element in emitter.checkedClasses) { | 420 for (Element element in emitter.checkedClasses) { |
421 if (!requiresNativeIsCheck(element)) continue; | 421 if (!requiresNativeIsCheck(element)) continue; |
422 if (element.isObject(compiler)) continue; | 422 if (element.isObject(compiler)) continue; |
423 String name = backend.namer.operatorIs(element); | 423 String name = backend.namer.operatorIs(element); |
424 objectProperties[name] = 'function() { return false; }'; | 424 objectProperties[name] = 'function() { return false; }'; |
425 } | 425 } |
426 } | 426 } |
427 | 427 |
428 void assembleCode(CodeBuffer targetBuffer) { | 428 void assembleCode(CodeBuffer targetBuffer) { |
429 if (nativeClasses.isEmpty()) return; | 429 if (nativeClasses.isEmpty) return; |
430 emitDynamicDispatchMetadata(); | 430 emitDynamicDispatchMetadata(); |
431 targetBuffer.add('$defineNativeClassName = ' | 431 targetBuffer.add('$defineNativeClassName = ' |
432 '$defineNativeClassFunction;\n\n'); | 432 '$defineNativeClassFunction;\n\n'); |
433 | 433 |
434 // Because of native classes, we have to generate some is checks | 434 // Because of native classes, we have to generate some is checks |
435 // by calling a method, instead of accessing a property. So we | 435 // by calling a method, instead of accessing a property. So we |
436 // attach to the JS Object prototype these methods that return | 436 // attach to the JS Object prototype these methods that return |
437 // false, and will be overridden by subclasses when they have to | 437 // false, and will be overridden by subclasses when they have to |
438 // return true. | 438 // return true. |
439 Map<String, String> objectProperties = new Map<String, String>(); | 439 Map<String, String> objectProperties = new Map<String, String>(); |
(...skipping 15 matching lines...) Expand all Loading... |
455 // If the native emitter has been asked to take care of the | 455 // If the native emitter has been asked to take care of the |
456 // noSuchMethod handlers, we do that now. | 456 // noSuchMethod handlers, we do that now. |
457 if (handleNoSuchMethod) { | 457 if (handleNoSuchMethod) { |
458 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) { | 458 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) { |
459 objectProperties[name] = buffer.toString(); | 459 objectProperties[name] = buffer.toString(); |
460 }); | 460 }); |
461 } | 461 } |
462 | 462 |
463 // If we have any properties to add to Object.prototype, we run | 463 // If we have any properties to add to Object.prototype, we run |
464 // through them and add them using defineProperty. | 464 // through them and add them using defineProperty. |
465 if (!objectProperties.isEmpty()) { | 465 if (!objectProperties.isEmpty) { |
466 targetBuffer.add("(function(table) {\n" | 466 targetBuffer.add("(function(table) {\n" |
467 " for (var key in table) {\n" | 467 " for (var key in table) {\n" |
468 " $defPropName(Object.prototype, key, table[key]);\n" | 468 " $defPropName(Object.prototype, key, table[key]);\n" |
469 " }\n" | 469 " }\n" |
470 "})({\n"); | 470 "})({\n"); |
471 bool first = true; | 471 bool first = true; |
472 objectProperties.forEach((String name, String function) { | 472 objectProperties.forEach((String name, String function) { |
473 if (!first) targetBuffer.add(",\n"); | 473 if (!first) targetBuffer.add(",\n"); |
474 targetBuffer.add(" $name: $function"); | 474 targetBuffer.add(" $name: $function"); |
475 first = false; | 475 first = false; |
476 }); | 476 }); |
477 targetBuffer.add("\n});\n\n"); | 477 targetBuffer.add("\n});\n\n"); |
478 } | 478 } |
479 targetBuffer.add(nativeBuffer); | 479 targetBuffer.add(nativeBuffer); |
480 targetBuffer.add('\n'); | 480 targetBuffer.add('\n'); |
481 } | 481 } |
482 } | 482 } |
OLD | NEW |