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