| 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 |