| 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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 class NativeEmitter { | 7 class NativeEmitter { |
| 8 | 8 |
| 9 CodeEmitterTask emitter; | 9 CodeEmitterTask emitter; |
| 10 CodeBuffer nativeBuffer; | 10 CodeBuffer nativeBuffer; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 | 24 |
| 25 // Caches the native methods that are overridden by a native class. | 25 // Caches the native methods that are overridden by a native class. |
| 26 // Note that the method that overrides does not have to be native: | 26 // Note that the method that overrides does not have to be native: |
| 27 // it's the overridden method that must make sure it will dispatch | 27 // it's the overridden method that must make sure it will dispatch |
| 28 // to its subclass if it sees an instance whose class is a subclass. | 28 // to its subclass if it sees an instance whose class is a subclass. |
| 29 Set<FunctionElement> overriddenMethods; | 29 Set<FunctionElement> overriddenMethods; |
| 30 | 30 |
| 31 // Caches the methods that have a native body. | 31 // Caches the methods that have a native body. |
| 32 Set<FunctionElement> nativeMethods; | 32 Set<FunctionElement> nativeMethods; |
| 33 | 33 |
| 34 // Caches the methods that redirect to a JS method. | |
| 35 Map<FunctionElement, String> redirectingMethods; | |
| 36 | |
| 37 // Do we need the native emitter to take care of handling | 34 // Do we need the native emitter to take care of handling |
| 38 // noSuchMethod for us? This flag is set to true in the emitter if | 35 // noSuchMethod for us? This flag is set to true in the emitter if |
| 39 // it finds any native class that needs noSuchMethod handling. | 36 // it finds any native class that needs noSuchMethod handling. |
| 40 bool handleNoSuchMethod = false; | 37 bool handleNoSuchMethod = false; |
| 41 | 38 |
| 42 NativeEmitter(this.emitter) | 39 NativeEmitter(this.emitter) |
| 43 : classesWithDynamicDispatch = new Set<ClassElement>(), | 40 : classesWithDynamicDispatch = new Set<ClassElement>(), |
| 44 nativeClasses = new Set<ClassElement>(), | 41 nativeClasses = new Set<ClassElement>(), |
| 45 subtypes = new Map<ClassElement, List<ClassElement>>(), | 42 subtypes = new Map<ClassElement, List<ClassElement>>(), |
| 46 directSubtypes = new Map<ClassElement, List<ClassElement>>(), | 43 directSubtypes = new Map<ClassElement, List<ClassElement>>(), |
| 47 overriddenMethods = new Set<FunctionElement>(), | 44 overriddenMethods = new Set<FunctionElement>(), |
| 48 nativeMethods = new Set<FunctionElement>(), | 45 nativeMethods = new Set<FunctionElement>(), |
| 49 redirectingMethods = new Map<FunctionElement, String>(), | |
| 50 nativeBuffer = new CodeBuffer(); | 46 nativeBuffer = new CodeBuffer(); |
| 51 | 47 |
| 52 Compiler get compiler => emitter.compiler; | 48 Compiler get compiler => emitter.compiler; |
| 53 JavaScriptBackend get backend => compiler.backend; | 49 JavaScriptBackend get backend => compiler.backend; |
| 54 | 50 |
| 55 void addRedirectingMethod(FunctionElement element, String name) { | |
| 56 redirectingMethods[element] = name; | |
| 57 } | |
| 58 | |
| 59 String get dynamicName { | 51 String get dynamicName { |
| 60 Element element = compiler.findHelper( | 52 Element element = compiler.findHelper( |
| 61 const SourceString('dynamicFunction')); | 53 const SourceString('dynamicFunction')); |
| 62 return backend.namer.isolateAccess(element); | 54 return backend.namer.isolateAccess(element); |
| 63 } | 55 } |
| 64 | 56 |
| 65 String get dynamicSetMetadataName { | 57 String get dynamicSetMetadataName { |
| 66 Element element = compiler.findHelper( | 58 Element element = compiler.findHelper( |
| 67 const SourceString('dynamicSetMetadata')); | 59 const SourceString('dynamicSetMetadata')); |
| 68 return backend.namer.isolateAccess(element); | 60 return backend.namer.isolateAccess(element); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 if (method !== '') { | 100 if (method !== '') { |
| 109 if (hasOwnProperty.call(desc, method)) { | 101 if (hasOwnProperty.call(desc, method)) { |
| 110 $dynamicName(method)[cls] = desc[method]; | 102 $dynamicName(method)[cls] = desc[method]; |
| 111 } | 103 } |
| 112 } | 104 } |
| 113 } | 105 } |
| 114 }"""; | 106 }"""; |
| 115 } | 107 } |
| 116 | 108 |
| 117 void generateNativeLiteral(ClassElement classElement) { | 109 void generateNativeLiteral(ClassElement classElement) { |
| 118 String quotedNative = classElement.nativeName.slowToString(); | 110 String quotedNative = classElement.nativeTagInfo.slowToString(); |
| 119 String nativeCode = quotedNative.substring(2, quotedNative.length - 1); | 111 String nativeCode = quotedNative.substring(2, quotedNative.length - 1); |
| 120 String className = backend.namer.getName(classElement); | 112 String className = backend.namer.getName(classElement); |
| 121 nativeBuffer.add(className); | 113 nativeBuffer.add(className); |
| 122 nativeBuffer.add(' = '); | 114 nativeBuffer.add(' = '); |
| 123 nativeBuffer.add(nativeCode); | 115 nativeBuffer.add(nativeCode); |
| 124 nativeBuffer.add(';\n'); | 116 nativeBuffer.add(';\n'); |
| 125 | 117 |
| 126 void defineInstanceMember(String name, CodeBuffer value) { | 118 void defineInstanceMember(String name, CodeBuffer value) { |
| 127 nativeBuffer.add("$className.$name = $value;\n"); | 119 nativeBuffer.add("$className.$name = $value;\n"); |
| 128 } | 120 } |
| 129 | 121 |
| 130 classElement.implementation.forEachMember((_, Element member) { | 122 classElement.implementation.forEachMember((_, Element member) { |
| 131 if (member.isInstanceMember()) { | 123 if (member.isInstanceMember()) { |
| 132 emitter.addInstanceMember(member, defineInstanceMember); | 124 emitter.addInstanceMember(member, defineInstanceMember); |
| 133 } | 125 } |
| 134 }); | 126 }); |
| 135 } | 127 } |
| 136 | 128 |
| 137 bool isNativeLiteral(String quotedName) { | 129 bool isNativeLiteral(String quotedName) { |
| 138 return identical(quotedName[1], '='); | 130 return identical(quotedName[1], '='); |
| 139 } | 131 } |
| 140 | 132 |
| 141 bool isNativeGlobal(String quotedName) { | 133 bool isNativeGlobal(String quotedName) { |
| 142 return identical(quotedName[1], '@'); | 134 return identical(quotedName[1], '@'); |
| 143 } | 135 } |
| 144 | 136 |
| 145 String toNativeName(ClassElement cls) { | 137 String toNativeTag(ClassElement cls) { |
| 146 String quotedName = cls.nativeName.slowToString(); | 138 String quotedName = cls.nativeTagInfo.slowToString(); |
| 147 if (isNativeGlobal(quotedName)) { | 139 if (isNativeGlobal(quotedName)) { |
| 148 // Global object, just be like the other types for now. | 140 // Global object, just be like the other types for now. |
| 149 return quotedName.substring(3, quotedName.length - 1); | 141 return quotedName.substring(3, quotedName.length - 1); |
| 150 } else { | 142 } else { |
| 151 return quotedName.substring(2, quotedName.length - 1); | 143 return quotedName.substring(2, quotedName.length - 1); |
| 152 } | 144 } |
| 153 } | 145 } |
| 154 | 146 |
| 155 void generateNativeClass(ClassElement classElement) { | 147 void generateNativeClass(ClassElement classElement) { |
| 156 nativeClasses.add(classElement); | 148 nativeClasses.add(classElement); |
| 157 | 149 |
| 158 assert(classElement.backendMembers.isEmpty); | 150 assert(classElement.backendMembers.isEmpty); |
| 159 String quotedName = classElement.nativeName.slowToString(); | 151 String quotedName = classElement.nativeTagInfo.slowToString(); |
| 160 if (isNativeLiteral(quotedName)) { | 152 if (isNativeLiteral(quotedName)) { |
| 161 generateNativeLiteral(classElement); | 153 generateNativeLiteral(classElement); |
| 162 // The native literal kind needs to be dealt with specially when | 154 // The native literal kind needs to be dealt with specially when |
| 163 // generating code for it. | 155 // generating code for it. |
| 164 return; | 156 return; |
| 165 } | 157 } |
| 166 | 158 |
| 167 CodeBuffer fieldBuffer = new CodeBuffer(); | 159 CodeBuffer fieldBuffer = new CodeBuffer(); |
| 168 CodeBuffer getterSetterBuffer = new CodeBuffer(); | 160 CodeBuffer getterSetterBuffer = new CodeBuffer(); |
| 169 CodeBuffer methodBuffer = new CodeBuffer(); | 161 CodeBuffer methodBuffer = new CodeBuffer(); |
| 170 | 162 |
| 171 emitter.emitClassFields(classElement, fieldBuffer, false); | 163 emitter.emitClassFields(classElement, fieldBuffer, false); |
| 172 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false); | 164 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false); |
| 173 emitter.emitInstanceMembers(classElement, methodBuffer, false); | 165 emitter.emitInstanceMembers(classElement, methodBuffer, false); |
| 174 | 166 |
| 175 if (methodBuffer.isEmpty | 167 if (methodBuffer.isEmpty |
| 176 && fieldBuffer.isEmpty | 168 && fieldBuffer.isEmpty |
| 177 && getterSetterBuffer.isEmpty) { | 169 && getterSetterBuffer.isEmpty) { |
| 178 return; | 170 return; |
| 179 } | 171 } |
| 180 | 172 |
| 181 String nativeName = toNativeName(classElement); | 173 String nativeTag = toNativeTag(classElement); |
| 182 nativeBuffer.add("$defineNativeClassName('$nativeName', "); | 174 nativeBuffer.add("$defineNativeClassName('$nativeTag', "); |
| 183 nativeBuffer.add('{'); | 175 nativeBuffer.add('{'); |
| 184 bool firstInMap = true; | 176 bool firstInMap = true; |
| 185 if (!fieldBuffer.isEmpty) { | 177 if (!fieldBuffer.isEmpty) { |
| 186 firstInMap = false; | 178 firstInMap = false; |
| 187 nativeBuffer.add(fieldBuffer); | 179 nativeBuffer.add(fieldBuffer); |
| 188 } | 180 } |
| 189 if (!getterSetterBuffer.isEmpty) { | 181 if (!getterSetterBuffer.isEmpty) { |
| 190 if (!firstInMap) nativeBuffer.add(","); | 182 if (!firstInMap) nativeBuffer.add(","); |
| 191 firstInMap = false; | 183 firstInMap = false; |
| 192 nativeBuffer.add("\n "); | 184 nativeBuffer.add("\n "); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 250 // The target JS function may check arguments.length so we need to | 242 // The target JS function may check arguments.length so we need to |
| 251 // make sure not to pass any unspecified optional arguments to it. | 243 // make sure not to pass any unspecified optional arguments to it. |
| 252 // For example, for the following Dart method: | 244 // For example, for the following Dart method: |
| 253 // foo([x, y, z]); | 245 // foo([x, y, z]); |
| 254 // The call: | 246 // The call: |
| 255 // foo(y: 1) | 247 // foo(y: 1) |
| 256 // must be turned into a JS call to: | 248 // must be turned into a JS call to: |
| 257 // foo(null, y). | 249 // foo(null, y). |
| 258 | 250 |
| 259 ClassElement classElement = member.enclosingElement; | 251 ClassElement classElement = member.enclosingElement; |
| 260 String nativeTagInfo = classElement.nativeName.slowToString(); | 252 //String nativeTagInfo = classElement.nativeName.slowToString(); |
| 253 String nativeTagInfo = classElement.nativeTagInfo.slowToString(); |
| 261 | 254 |
| 262 List<js.Statement> statements = <js.Statement>[]; | 255 List<js.Statement> statements = <js.Statement>[]; |
| 263 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); | 256 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); |
| 264 | 257 |
| 265 String target; | 258 String target; |
| 266 List<js.Expression> arguments; | 259 List<js.Expression> arguments; |
| 267 | 260 |
| 268 if (!nativeMethods.contains(member)) { | 261 if (!nativeMethods.contains(member)) { |
| 269 // When calling a method that has a native body, we call it with our | 262 // When calling a method that has a native body, we call it with our |
| 270 // calling conventions. | 263 // calling conventions. |
| 271 target = backend.namer.getName(member); | 264 target = backend.namer.getName(member); |
| 272 arguments = argumentsBuffer; | 265 arguments = argumentsBuffer; |
| 273 } else { | 266 } else { |
| 274 // When calling a JS method, we call it with the native name, and only the | 267 // When calling a JS method, we call it with the native name, and only the |
| 275 // arguments up until the last one provided. | 268 // arguments up until the last one provided. |
| 276 target = redirectingMethods[member]; | 269 target = member.nativeName(); |
| 277 if (target == null) target = member.name.slowToString(); | |
| 278 arguments = argumentsBuffer.getRange( | 270 arguments = argumentsBuffer.getRange( |
| 279 0, indexOfLastOptionalArgumentInParameters + 1); | 271 0, indexOfLastOptionalArgumentInParameters + 1); |
| 280 } | 272 } |
| 281 statements.add( | 273 statements.add( |
| 282 new js.Return( | 274 new js.Return( |
| 283 new js.VariableUse('this').dot(target).callWith(arguments))); | 275 new js.VariableUse('this').dot(target).callWith(arguments))); |
| 284 | 276 |
| 285 if (isNativeLiteral(nativeTagInfo) || !overriddenMethods.contains(member)) { | 277 if (isNativeLiteral(nativeTagInfo) || !overriddenMethods.contains(member)) { |
| 286 // Call the method directly. | 278 // Call the method directly. |
| 287 return statements; | 279 return statements; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 388 // Expression to compute tags string for a class. The expression will | 380 // Expression to compute tags string for a class. The expression will |
| 389 // initially be a string or expression building a string, but may be | 381 // initially be a string or expression building a string, but may be |
| 390 // replaced with a variable reference to the common substring. | 382 // replaced with a variable reference to the common substring. |
| 391 Map<ClassElement, js.Expression> tagDefns = | 383 Map<ClassElement, js.Expression> tagDefns = |
| 392 new Map<ClassElement, js.Expression>(); | 384 new Map<ClassElement, js.Expression>(); |
| 393 | 385 |
| 394 js.Expression makeExpression(ClassElement classElement) { | 386 js.Expression makeExpression(ClassElement classElement) { |
| 395 // Expression fragments for this set of cls keys. | 387 // Expression fragments for this set of cls keys. |
| 396 List<js.Expression> expressions = <js.Expression>[]; | 388 List<js.Expression> expressions = <js.Expression>[]; |
| 397 // TODO: Remove if cls is abstract. | 389 // TODO: Remove if cls is abstract. |
| 398 List<String> subtags = [toNativeName(classElement)]; | 390 List<String> subtags = [toNativeTag(classElement)]; |
| 399 void walk(ClassElement cls) { | 391 void walk(ClassElement cls) { |
| 400 for (final ClassElement subclass in getDirectSubclasses(cls)) { | 392 for (final ClassElement subclass in getDirectSubclasses(cls)) { |
| 401 ClassElement tag = subclass; | 393 ClassElement tag = subclass; |
| 402 js.Expression existing = tagDefns[tag]; | 394 js.Expression existing = tagDefns[tag]; |
| 403 if (existing == null) { | 395 if (existing == null) { |
| 404 // [subclass] is still within the subtree between dispatch classes. | 396 // [subclass] is still within the subtree between dispatch classes. |
| 405 subtags.add(toNativeName(tag)); | 397 subtags.add(toNativeTag(tag)); |
| 406 walk(subclass); | 398 walk(subclass); |
| 407 } else { | 399 } else { |
| 408 // [subclass] is one of the preorderDispatchClasses, so CSE this | 400 // [subclass] is one of the preorderDispatchClasses, so CSE this |
| 409 // reference with the previous reference. | 401 // reference with the previous reference. |
| 410 if (existing is js.VariableUse && | 402 if (existing is js.VariableUse && |
| 411 varDefns.containsKey(existing.name)) { | 403 varDefns.containsKey(existing.name)) { |
| 412 // We end up here if the subclasses have a DAG structure. We | 404 // We end up here if the subclasses have a DAG structure. We |
| 413 // don't have DAGs yet, but if the dispatch is used for mixins | 405 // don't have DAGs yet, but if the dispatch is used for mixins |
| 414 // that will be a possibility. | 406 // that will be a possibility. |
| 415 // Re-use the previously created temporary variable. | 407 // Re-use the previously created temporary variable. |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 465 } | 457 } |
| 466 | 458 |
| 467 // [table] is a list of lists, each inner list of the form: | 459 // [table] is a list of lists, each inner list of the form: |
| 468 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] | 460 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] |
| 469 // E.g. | 461 // E.g. |
| 470 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] | 462 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] |
| 471 js.Expression table = | 463 js.Expression table = |
| 472 new js.ArrayInitializer.from( | 464 new js.ArrayInitializer.from( |
| 473 preorderDispatchClasses.map((cls) => | 465 preorderDispatchClasses.map((cls) => |
| 474 new js.ArrayInitializer.from([ | 466 new js.ArrayInitializer.from([ |
| 475 new js.LiteralString("'${toNativeName(cls)}'"), | 467 new js.LiteralString("'${toNativeTag(cls)}'"), |
| 476 tagDefns[cls]]))); | 468 tagDefns[cls]]))); |
| 477 | 469 |
| 478 // $.dynamicSetMetadata(table); | 470 // $.dynamicSetMetadata(table); |
| 479 statements.add( | 471 statements.add( |
| 480 new js.ExpressionStatement( | 472 new js.ExpressionStatement( |
| 481 new js.Call( | 473 new js.Call( |
| 482 new js.VariableUse(dynamicSetMetadataName), | 474 new js.VariableUse(dynamicSetMetadataName), |
| 483 [table]))); | 475 [table]))); |
| 484 | 476 |
| 485 // (function(){statements})(); | 477 // (function(){statements})(); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 if (!first) targetBuffer.add(",\n"); | 566 if (!first) targetBuffer.add(",\n"); |
| 575 targetBuffer.add(" $name: $function"); | 567 targetBuffer.add(" $name: $function"); |
| 576 first = false; | 568 first = false; |
| 577 }); | 569 }); |
| 578 targetBuffer.add("\n});\n\n"); | 570 targetBuffer.add("\n});\n\n"); |
| 579 } | 571 } |
| 580 targetBuffer.add(nativeBuffer); | 572 targetBuffer.add(nativeBuffer); |
| 581 targetBuffer.add('\n'); | 573 targetBuffer.add('\n'); |
| 582 } | 574 } |
| 583 } | 575 } |
| OLD | NEW |