| 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 /** | 5 /** |
| 6 * A script to assist in documenting the difference between the dart:html API | 6 * A script to assist in documenting the difference between the dart:html API |
| 7 * and the old DOM API. | 7 * and the old DOM API. |
| 8 */ | 8 */ |
| 9 #library('html_diff'); | 9 #library('html_diff'); |
| 10 | 10 |
| 11 #import('dart:coreimpl'); | 11 #import('dart:coreimpl'); |
| 12 #import('../../frog/lang.dart'); | 12 #import('../../frog/lang.dart'); |
| 13 #import('../../frog/file_system_vm.dart'); | 13 #import('../../frog/file_system_vm.dart'); |
| 14 #import('../../frog/file_system.dart'); | 14 #import('../../frog/file_system.dart'); |
| 15 #import('../../lib/dartdoc/dartdoc.dart'); | 15 #import('../../lib/dartdoc/dartdoc.dart'); |
| 16 | 16 |
| 17 /** | 17 /** |
| 18 * A class for computing a many-to-many mapping between the types and members in | 18 * A class for computing a many-to-many mapping between the types and members in |
| 19 * `dart:dom` and `dart:html`. This mapping is based on two indicators: | 19 * `dart:dom` and `dart:html`. This mapping is based on two indicators: |
| 20 * | 20 * |
| 21 * 1. Auto-detected wrappers. Most `dart:html` types correspond | 21 * 1. Auto-detected wrappers. Most `dart:html` types correspond |
| 22 * straightforwardly to a single `dart:dom` type, and have the same name. | 22 * straightforwardly to a single `dart:dom` type, and have the same name. |
| 23 * In addition, most `dart:htmlimpl` methods just call a single `dart:dom` | 23 * In addition, most `dart:html` methods just call a single `dart:dom` |
| 24 * method. This class detects these simple correspondences automatically. | 24 * method. This class detects these simple correspondences automatically. |
| 25 * | 25 * |
| 26 * 2. Manual annotations. When it's not clear which `dart:dom` items a given | 26 * 2. Manual annotations. When it's not clear which `dart:dom` items a given |
| 27 * `dart:html` item corresponds to, the `dart:htmlimpl` item can be | 27 * `dart:html` item corresponds to, the `dart:html` item can be |
| 28 * annotated in the documentation comments using the `@domName` annotation. | 28 * annotated in the documentation comments using the `@domName` annotation. |
| 29 * | 29 * |
| 30 * The `@domName` annotations for types and members are of the form `@domName | 30 * The `@domName` annotations for types and members are of the form `@domName |
| 31 * NAME(, NAME)*`, where the `NAME`s refer to the `dart:dom` types/members that | 31 * NAME(, NAME)*`, where the `NAME`s refer to the `dart:dom` types/members that |
| 32 * correspond to the annotated `dart:htmlimpl` type/member. `NAME`s on member | 32 * correspond to the annotated `dart:html` type/member. `NAME`s on member |
| 33 * annotations can refer to either fully-qualified member names (e.g. | 33 * annotations can refer to either fully-qualified member names (e.g. |
| 34 * `Document.createElement`) or unqualified member names (e.g. `createElement`). | 34 * `Document.createElement`) or unqualified member names (e.g. `createElement`). |
| 35 * Unqualified member names are assumed to refer to members of one of the | 35 * Unqualified member names are assumed to refer to members of one of the |
| 36 * corresponding `dart:dom` types. | 36 * corresponding `dart:dom` types. |
| 37 */ | 37 */ |
| 38 class HtmlDiff { | 38 class HtmlDiff { |
| 39 /** A map from `dart:dom` members to corresponding `dart:html` members. */ | 39 /** A map from `dart:dom` members to corresponding `dart:html` members. */ |
| 40 final Map<Member, Set<Member>> domToHtml; | 40 final Map<Member, Set<Member>> domToHtml; |
| 41 | 41 |
| 42 /** A map from `dart:html` members to corresponding `dart:dom` members. */ | 42 /** A map from `dart:html` members to corresponding `dart:dom` members. */ |
| 43 final Map<Member, Set<Member>> htmlToDom; | 43 final Map<Member, Set<Member>> htmlToDom; |
| 44 | 44 |
| 45 /** A map from `dart:dom` types to corresponding `dart:html` types. */ | 45 /** A map from `dart:dom` types to corresponding `dart:html` types. */ |
| 46 final Map<Type, Set<Type>> domTypesToHtml; | 46 final Map<Type, Set<Type>> domTypesToHtml; |
| 47 | 47 |
| 48 /** A map from `dart:html` types to corresponding `dart:dom` types. */ | 48 /** A map from `dart:html` types to corresponding `dart:dom` types. */ |
| 49 final Map<Type, Set<Type>> htmlTypesToDom; | 49 final Map<Type, Set<Type>> htmlTypesToDom; |
| 50 | 50 |
| 51 final CommentMap comments; | 51 final CommentMap comments; |
| 52 | 52 |
| 53 static Library dom; | 53 static Library dom; |
| 54 | 54 |
| 55 /** | 55 /** |
| 56 * Perform static initialization of [world]. This should be run before | 56 * Perform static initialization of [world]. This should be run before |
| 57 * calling [HtmlDiff.run]. | 57 * calling [HtmlDiff.run]. |
| 58 */ | 58 */ |
| 59 static void initialize() { | 59 static void initialize() { |
| 60 world.processDartScript('dart:htmlimpl'); | 60 world.processDartScript('dart:html'); |
| 61 world.resolveAll(); | 61 world.resolveAll(); |
| 62 dom = world.libraries['dart:dom']; | 62 dom = world.libraries['dart:dom']; |
| 63 } | 63 } |
| 64 | 64 |
| 65 HtmlDiff() : | 65 HtmlDiff() : |
| 66 domToHtml = new Map<Member, Set<Member>>(), | 66 domToHtml = new Map<Member, Set<Member>>(), |
| 67 htmlToDom = new Map<Member, Set<Member>>(), | 67 htmlToDom = new Map<Member, Set<Member>>(), |
| 68 domTypesToHtml = new Map<Type, Set<Type>>(), | 68 domTypesToHtml = new Map<Type, Set<Type>>(), |
| 69 htmlTypesToDom = new Map<Type, Set<Type>>(), | 69 htmlTypesToDom = new Map<Type, Set<Type>>(), |
| 70 comments = new CommentMap(); | 70 comments = new CommentMap(); |
| 71 | 71 |
| 72 /** | 72 /** |
| 73 * Computes the `dart:dom` to `dart:html` mapping, and places it in | 73 * Computes the `dart:dom` to `dart:html` mapping, and places it in |
| 74 * [domToHtml], [htmlToDom], [domTypesToHtml], and [htmlTypesToDom]. Before | 74 * [domToHtml], [htmlToDom], [domTypesToHtml], and [htmlTypesToDom]. Before |
| 75 * this is run, Frog should be initialized (via [parseOptions] and | 75 * this is run, Frog should be initialized (via [parseOptions] and |
| 76 * [initializeWorld]) and [HtmlDiff.initialize] should be called. | 76 * [initializeWorld]) and [HtmlDiff.initialize] should be called. |
| 77 */ | 77 */ |
| 78 void run() { | 78 void run() { |
| 79 final htmlLib = world.libraries['dart:htmlimpl']; | 79 final htmlLib = world.libraries['dart:html']; |
| 80 for (var implType in htmlLib.types.getValues()) { | 80 for (var implType in htmlLib.types.getValues()) { |
| 81 final domTypes = htmlToDomTypes(implType); | 81 final domTypes = htmlToDomTypes(implType); |
| 82 final htmlType = htmlImplToHtmlType(implType); | 82 final htmlType = htmlImplToHtmlType(implType); |
| 83 if (htmlType == null) continue; | 83 if (htmlType == null) continue; |
| 84 | 84 |
| 85 htmlTypesToDom.putIfAbsent(htmlType, () => new Set()).addAll(domTypes); | 85 htmlTypesToDom.putIfAbsent(htmlType, () => new Set()).addAll(domTypes); |
| 86 domTypes.forEach((t) => | 86 domTypes.forEach((t) => |
| 87 domTypesToHtml.putIfAbsent(t, () => new Set()).add(htmlType)); | 87 domTypesToHtml.putIfAbsent(t, () => new Set()).add(htmlType)); |
| 88 | 88 |
| 89 final members = new List.from(implType.members.getValues()); | 89 final members = new List.from(implType.members.getValues()); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 110 var htmlName = htmlMember.name; | 110 var htmlName = htmlMember.name; |
| 111 if (htmlName.startsWith('get:') || htmlName.startsWith('set:')) { | 111 if (htmlName.startsWith('get:') || htmlName.startsWith('set:')) { |
| 112 htmlName = htmlName.substring(4); | 112 htmlName = htmlName.substring(4); |
| 113 } | 113 } |
| 114 | 114 |
| 115 return domTypeName == htmlTypeName && domName == htmlName; | 115 return domTypeName == htmlTypeName && domName == htmlName; |
| 116 } | 116 } |
| 117 | 117 |
| 118 /** | 118 /** |
| 119 * Records the `dart:dom` to `dart:html` mapping for [implMember] (from | 119 * Records the `dart:dom` to `dart:html` mapping for [implMember] (from |
| 120 * `dart:htmlimpl`). [domTypes] are the `dart:dom` [Type]s that correspond to | 120 * `dart:html`). [domTypes] are the `dart:dom` [Type]s that correspond to |
| 121 * [implMember]'s defining [Type]. | 121 * [implMember]'s defining [Type]. |
| 122 */ | 122 */ |
| 123 void _addMemberDiff(Member implMember, List<Type> domTypes) { | 123 void _addMemberDiff(Member implMember, List<Type> domTypes) { |
| 124 if (implMember.isProperty) { | 124 if (implMember.isProperty) { |
| 125 if (implMember.canGet) _addMemberDiff(implMember.getter, domTypes); | 125 if (implMember.canGet) _addMemberDiff(implMember.getter, domTypes); |
| 126 if (implMember.canSet) _addMemberDiff(implMember.setter, domTypes); | 126 if (implMember.canSet) _addMemberDiff(implMember.setter, domTypes); |
| 127 } | 127 } |
| 128 | 128 |
| 129 var domMembers = htmlToDomMembers(implMember, domTypes); | 129 var domMembers = htmlToDomMembers(implMember, domTypes); |
| 130 var htmlMember = htmlImplToHtmlMember(implMember); | 130 var htmlMember = htmlImplToHtmlMember(implMember); |
| 131 if (htmlMember == null && !domMembers.isEmpty()) { | 131 if (htmlMember == null && !domMembers.isEmpty()) { |
| 132 print('Warning: dart:htmlimpl member ${implMember.declaringType.name}.' + | 132 print('Warning: dart:html member ${implMember.declaringType.name}.' + |
| 133 '${implMember.name} has no corresponding dart:html member.'); | 133 '${implMember.name} has no corresponding dart:html member.'); |
| 134 } | 134 } |
| 135 | 135 |
| 136 if (htmlMember == null) return; | 136 if (htmlMember == null) return; |
| 137 if (!domMembers.isEmpty()) htmlToDom[htmlMember] = domMembers; | 137 if (!domMembers.isEmpty()) htmlToDom[htmlMember] = domMembers; |
| 138 domMembers.forEach((m) => | 138 domMembers.forEach((m) => |
| 139 domToHtml.putIfAbsent(m, () => new Set()).add(htmlMember)); | 139 domToHtml.putIfAbsent(m, () => new Set()).add(htmlMember)); |
| 140 } | 140 } |
| 141 | 141 |
| 142 /** | 142 /** |
| 143 * Returns the `dart:html` [Type] that corresponds to [implType] from | 143 * Returns the `dart:html` [Type] that corresponds to [implType] from |
| 144 * `dart:htmlimpl`, or `null` if there is no such correspondence. | 144 * `dart:html`, or `null` if there is no such correspondence. |
| 145 */ | 145 */ |
| 146 Type htmlImplToHtmlType(Type implType) { | 146 Type htmlImplToHtmlType(Type implType) { |
| 147 if (implType == null || implType.isTop || implType.interfaces.isEmpty() || | 147 if (implType == null || implType.isTop || implType.interfaces.isEmpty() || |
| 148 implType.interfaces[0].library.name != 'html') { | 148 implType.interfaces[0].library.name != 'html') { |
| 149 return null; | 149 return null; |
| 150 } | 150 } |
| 151 | 151 |
| 152 return implType.interfaces[0]; | 152 return implType.interfaces[0]; |
| 153 } | 153 } |
| 154 | 154 |
| 155 /** | 155 /** |
| 156 * Returns the `dart:html` [Member] that corresponds to [implMember] from | 156 * Returns the `dart:html` [Member] that corresponds to [implMember] from |
| 157 * `dart:htmlimpl`, or `null` if there is no such correspondence. | 157 * `dart:html`, or `null` if there is no such correspondence. |
| 158 */ | 158 */ |
| 159 Member htmlImplToHtmlMember(Member implMember) { | 159 Member htmlImplToHtmlMember(Member implMember) { |
| 160 var htmlType = htmlImplToHtmlType(implMember.declaringType); | 160 var htmlType = htmlImplToHtmlType(implMember.declaringType); |
| 161 if (htmlType == null) return null; | 161 if (htmlType == null) return null; |
| 162 | 162 |
| 163 bool getter, setter; | 163 bool getter, setter; |
| 164 if (implMember.isConstructor || implMember.isFactory) { | 164 if (implMember.isConstructor || implMember.isFactory) { |
| 165 var constructor = htmlType.constructors[implMember.name]; | 165 var constructor = htmlType.constructors[implMember.name]; |
| 166 if (constructor != null) return constructor; | 166 if (constructor != null) return constructor; |
| 167 | 167 |
| 168 // Look for a factory constructor whose type and name matches the member. | 168 // Look for a factory constructor whose type and name matches the member. |
| 169 return htmlType.factories.getFactoriesFor(implMember.name)[ | 169 return htmlType.factories.getFactoriesFor(implMember.name)[ |
| 170 implMember.constructorName]; | 170 implMember.constructorName]; |
| 171 } else if ((getter = implMember.name.startsWith('get:')) || | 171 } else if ((getter = implMember.name.startsWith('get:')) || |
| 172 (setter = implMember.name.startsWith('set:'))) { | 172 (setter = implMember.name.startsWith('set:'))) { |
| 173 // Use getMember to follow interface inheritance chains. | 173 // Use getMember to follow interface inheritance chains. |
| 174 var htmlProperty = htmlType.getMember(implMember.name.substring(4)); | 174 var htmlProperty = htmlType.getMember(implMember.name.substring(4)); |
| 175 if (htmlProperty != null) { | 175 if (htmlProperty != null) { |
| 176 return getter ? htmlProperty.getter : htmlProperty.setter; | 176 return getter ? htmlProperty.getter : htmlProperty.setter; |
| 177 } else { | 177 } else { |
| 178 return null; | 178 return null; |
| 179 } | 179 } |
| 180 } else { | 180 } else { |
| 181 return htmlType.getMember(implMember.name); | 181 return htmlType.getMember(implMember.name); |
| 182 } | 182 } |
| 183 } | 183 } |
| 184 | 184 |
| 185 /** | 185 /** |
| 186 * Returns the `dart:dom` [Type]s that correspond to [htmlType] from | 186 * Returns the `dart:dom` [Type]s that correspond to [htmlType] from |
| 187 * `dart:htmlimpl`. This can be the empty list if no correspondence is found. | 187 * `dart:html`. This can be the empty list if no correspondence is found. |
| 188 */ | 188 */ |
| 189 List<Type> htmlToDomTypes(Type htmlType) { | 189 List<Type> htmlToDomTypes(Type htmlType) { |
| 190 if (htmlType.name == null) return []; | 190 if (htmlType.name == null) return []; |
| 191 final tags = _getTags(comments.find(htmlType.span)); | 191 final tags = _getTags(comments.find(htmlType.span)); |
| 192 | 192 |
| 193 if (tags.containsKey('domName')) { | 193 if (tags.containsKey('domName')) { |
| 194 var domNames = map(tags['domName'].split(','), (s) => s.trim()); | 194 var domNames = map(tags['domName'].split(','), (s) => s.trim()); |
| 195 if (domNames.length == 1 && domNames[0] == 'none') return []; | 195 if (domNames.length == 1 && domNames[0] == 'none') return []; |
| 196 return map(domNames, (domName) { | 196 return map(domNames, (domName) { |
| 197 // DOMWindow is Chrome-specific, so we don't use it in our annotations. | 197 // DOMWindow is Chrome-specific, so we don't use it in our annotations. |
| 198 if (domName == 'Window') domName = 'DOMWindow'; | 198 if (domName == 'Window') domName = 'DOMWindow'; |
| 199 final domType = dom.types[domName]; | 199 final domType = dom.types[domName]; |
| 200 if (domType == null) print('Warning: no dart:dom type named $domName'); | 200 if (domType == null) print('Warning: no dart:dom type named $domName'); |
| 201 return domType; | 201 return domType; |
| 202 }); | 202 }); |
| 203 } else { | 203 } else { |
| 204 if (!htmlType.name.endsWith('WrappingImplementation')) return []; | 204 if (!htmlType.name.endsWith('WrappingImplementation')) return []; |
| 205 final domName = htmlType.name.replaceFirst('WrappingImplementation', ''); | 205 final domName = htmlType.name.replaceFirst('WrappingImplementation', ''); |
| 206 var domType = dom.types[domName]; | 206 var domType = dom.types[domName]; |
| 207 if (domType == null && domName.endsWith('Element')) { | 207 if (domType == null && domName.endsWith('Element')) { |
| 208 domType = dom.types['HTML$domName']; | 208 domType = dom.types['HTML$domName']; |
| 209 } | 209 } |
| 210 if (domType == null) domType = dom.types['WebKit$domName']; | 210 if (domType == null) domType = dom.types['WebKit$domName']; |
| 211 if (domType == null) { | 211 if (domType == null) { |
| 212 print('Warning: no dart:dom type matches dart:htmlimpl ' + | 212 print('Warning: no dart:dom type matches dart:html ' + |
| 213 htmlType.name); | 213 htmlType.name); |
| 214 return []; | 214 return []; |
| 215 } | 215 } |
| 216 return [domType]; | 216 return [domType]; |
| 217 } | 217 } |
| 218 } | 218 } |
| 219 | 219 |
| 220 /** | 220 /** |
| 221 * Returns the `dart:dom` [Member]s that correspond to [htmlMember] from | 221 * Returns the `dart:dom` [Member]s that correspond to [htmlMember] from |
| 222 * `dart:htmlimpl`. This can be the empty set if no correspondence is found. | 222 * `dart:html`. This can be the empty set if no correspondence is found. |
| 223 * [domTypes] are the `dart:dom` [Type]s that correspond to [implMember]'s | 223 * [domTypes] are the `dart:dom` [Type]s that correspond to [implMember]'s |
| 224 * defining [Type]. | 224 * defining [Type]. |
| 225 */ | 225 */ |
| 226 Set<Member> htmlToDomMembers(Member htmlMember, List<Type> domTypes) { | 226 Set<Member> htmlToDomMembers(Member htmlMember, List<Type> domTypes) { |
| 227 if (htmlMember.isPrivate || htmlMember is! MethodMember) return new Set(); | 227 if (htmlMember.isPrivate || htmlMember is! MethodMember) return new Set(); |
| 228 final tags = _getTags(comments.find(htmlMember.span)); | 228 final tags = _getTags(comments.find(htmlMember.span)); |
| 229 if (tags.containsKey('domName')) { | 229 if (tags.containsKey('domName')) { |
| 230 final domNames = map(tags['domName'].split(','), (s) => s.trim()); | 230 final domNames = map(tags['domName'].split(','), (s) => s.trim()); |
| 231 if (domNames.length == 1 && domNames[0] == 'none') return new Set(); | 231 if (domNames.length == 1 && domNames[0] == 'none') return new Set(); |
| 232 final members = new Set(); | 232 final members = new Set(); |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 Map<String, String> _getTags(String comment) { | 394 Map<String, String> _getTags(String comment) { |
| 395 if (comment == null) return const <String>{}; | 395 if (comment == null) return const <String>{}; |
| 396 final re = new RegExp("@([a-zA-Z]+) ([^;]+)(?:;|\$)"); | 396 final re = new RegExp("@([a-zA-Z]+) ([^;]+)(?:;|\$)"); |
| 397 final tags = <String>{}; | 397 final tags = <String>{}; |
| 398 for (var m in re.allMatches(comment.trim())) { | 398 for (var m in re.allMatches(comment.trim())) { |
| 399 tags[m[1]] = m[2]; | 399 tags[m[1]] = m[2]; |
| 400 } | 400 } |
| 401 return tags; | 401 return tags; |
| 402 } | 402 } |
| 403 } | 403 } |
| OLD | NEW |