| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * A script to assist in documenting the difference between the dart:html API | |
| 7 * and the old DOM API. | |
| 8 */ | |
| 9 library html_diff; | |
| 10 | |
| 11 import 'dart:async'; | |
| 12 | |
| 13 import 'lib/metadata.dart'; | |
| 14 | |
| 15 // TODO(rnystrom): Use "package:" URL (#4968). | |
| 16 import '../../pkg/compiler/lib/src/mirrors/analyze.dart'; | |
| 17 import '../../pkg/compiler/lib/src/mirrors/source_mirrors.dart'; | |
| 18 import '../../pkg/compiler/lib/src/mirrors/mirrors_util.dart'; | |
| 19 import '../../pkg/compiler/lib/src/source_file_provider.dart'; | |
| 20 | |
| 21 // TODO(amouravski): There is currently magic that looks at dart:* libraries | |
| 22 // rather than the declared library names. This changed due to recent syntax | |
| 23 // changes. We should only need to look at the library 'html'. | |
| 24 final List<Uri> HTML_LIBRARY_URIS = [ | |
| 25 new Uri(scheme: 'dart', path: 'html'), | |
| 26 new Uri(scheme: 'dart', path: 'indexed_db'), | |
| 27 new Uri(scheme: 'dart', path: 'svg'), | |
| 28 new Uri(scheme: 'dart', path: 'web_audio')]; | |
| 29 | |
| 30 /** | |
| 31 * A class for computing a many-to-many mapping between the types and | |
| 32 * members in `dart:html` and the MDN DOM types. This mapping is | |
| 33 * based on two indicators: | |
| 34 * | |
| 35 * 1. Auto-detected wrappers. Most `dart:html` types correspond | |
| 36 * straightforwardly to a single `@DomName` type, and | |
| 37 * have the same name. In addition, most `dart:html` methods | |
| 38 * just call a single `@DomName` method. This class | |
| 39 * detects these simple correspondences automatically. | |
| 40 * | |
| 41 * 2. Manual annotations. When it's not clear which | |
| 42 * `@DomName` items a given `dart:html` item | |
| 43 * corresponds to, the `dart:html` item can be annotated in the | |
| 44 * documentation comments using the `@DomName` annotation. | |
| 45 * | |
| 46 * The `@DomName` annotations for types and members are of the form | |
| 47 * `@DomName NAME(, NAME)*`, where the `NAME`s refer to the | |
| 48 * `@DomName` types/members that correspond to the | |
| 49 * annotated `dart:html` type/member. `NAME`s on member annotations | |
| 50 * can refer to either fully-qualified member names (e.g. | |
| 51 * `Document.createElement`) or unqualified member names | |
| 52 * (e.g. `createElement`). Unqualified member names are assumed to | |
| 53 * refer to members of one of the corresponding `@DomName` | |
| 54 * types. | |
| 55 */ | |
| 56 class HtmlDiff { | |
| 57 /** | |
| 58 * A map from `dart:html` members to the corresponding fully qualified | |
| 59 * `@DomName` member(s). | |
| 60 */ | |
| 61 final Map<String, Set<String>> htmlToDom; | |
| 62 | |
| 63 /** A map from `dart:html` types to corresponding `@DomName` types. */ | |
| 64 final Map<String, Set<String>> htmlTypesToDom; | |
| 65 | |
| 66 /** If true, then print warning messages. */ | |
| 67 final bool _printWarnings; | |
| 68 | |
| 69 static LibraryMirror dom; | |
| 70 | |
| 71 HtmlDiff({bool printWarnings: false}) : | |
| 72 _printWarnings = printWarnings, | |
| 73 htmlToDom = new Map<String, Set<String>>(), | |
| 74 htmlTypesToDom = new Map<String, Set<String>>(); | |
| 75 | |
| 76 void warn(String s) { | |
| 77 if (_printWarnings) { | |
| 78 print('Warning: $s'); | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 /** | |
| 83 * Computes the `@DomName` to `dart:html` mapping, and | |
| 84 * places it in [htmlToDom] and [htmlTypesToDom]. Before this is run, dart2js | |
| 85 * should be initialized (via [parseOptions] and [initializeWorld]) and | |
| 86 * [HtmlDiff.initialize] should be called. | |
| 87 */ | |
| 88 Future run(Uri libraryRoot) { | |
| 89 var result = new Completer(); | |
| 90 var provider = new CompilerSourceFileProvider(); | |
| 91 var handler = new FormattingDiagnosticHandler(provider); | |
| 92 Future<MirrorSystem> analysis = analyze( | |
| 93 HTML_LIBRARY_URIS, libraryRoot, null, | |
| 94 provider.readStringFromUri, | |
| 95 handler.diagnosticHandler); | |
| 96 analysis.then((MirrorSystem mirrors) { | |
| 97 for (var libraryUri in HTML_LIBRARY_URIS) { | |
| 98 var library = mirrors.libraries[libraryUri]; | |
| 99 if (library == null) { | |
| 100 warn('Could not find $libraryUri'); | |
| 101 result.complete(false); | |
| 102 } | |
| 103 for (ClassMirror type in classesOf(library.declarations)) { | |
| 104 final domTypes = htmlToDomTypes(type); | |
| 105 if (domTypes.isEmpty) continue; | |
| 106 | |
| 107 htmlTypesToDom.putIfAbsent(qualifiedNameOf(type), | |
| 108 () => new Set()).addAll(domTypes); | |
| 109 | |
| 110 membersOf(type.declarations).forEach( | |
| 111 (m) => _addMemberDiff(m, domTypes, nameOf(library))); | |
| 112 } | |
| 113 } | |
| 114 result.complete(true); | |
| 115 }); | |
| 116 return result.future; | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * Records the `@DomName` to `dart:html` mapping for | |
| 121 * [htmlMember] (from `dart:html`). [domTypes] are the | |
| 122 * `@DomName` type values that correspond to [htmlMember]'s | |
| 123 * defining type. | |
| 124 */ | |
| 125 void _addMemberDiff(DeclarationMirror htmlMember, List<String> domTypes, | |
| 126 String libraryName) { | |
| 127 var domMembers = htmlToDomMembers(htmlMember, domTypes); | |
| 128 if (htmlMember == null && !domMembers.isEmpty) { | |
| 129 warn('$libraryName member ' | |
| 130 '${htmlMember.owner.simpleName}.' | |
| 131 '${htmlMember.simpleName} has no corresponding ' | |
| 132 '$libraryName member.'); | |
| 133 } | |
| 134 | |
| 135 if (htmlMember == null) return; | |
| 136 if (!domMembers.isEmpty) { | |
| 137 htmlToDom[qualifiedNameOf(htmlMember)] = domMembers; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * Returns the `@DomName` type values that correspond to | |
| 143 * [htmlType] from `dart:html`. This can be the empty list if no | |
| 144 * correspondence is found. | |
| 145 */ | |
| 146 List<String> htmlToDomTypes(ClassMirror htmlType) { | |
| 147 if (htmlType.simpleName == null) return <String>[]; | |
| 148 | |
| 149 final domNameMetadata = findMetadata(htmlType.metadata, 'DomName'); | |
| 150 if (domNameMetadata != null) { | |
| 151 var domNames = <String>[]; | |
| 152 var names = domNameMetadata.getField(symbolOf('name')); | |
| 153 for (var s in names.reflectee.split(',')) { | |
| 154 domNames.add(s.trim()); | |
| 155 } | |
| 156 | |
| 157 if (domNames.length == 1 && domNames[0] == 'none') return <String>[]; | |
| 158 return domNames; | |
| 159 } | |
| 160 return <String>[]; | |
| 161 } | |
| 162 | |
| 163 /** | |
| 164 * Returns the `@DomName` member values that correspond to | |
| 165 * [htmlMember] from `dart:html`. This can be the empty set if no | |
| 166 * correspondence is found. [domTypes] are the | |
| 167 * `@DomName` type values that correspond to [htmlMember]'s | |
| 168 * defining type. | |
| 169 */ | |
| 170 Set<String> htmlToDomMembers(DeclarationMirror htmlMember, | |
| 171 List<String> domTypes) { | |
| 172 if (htmlMember.isPrivate) return new Set(); | |
| 173 | |
| 174 final domNameMetadata = findMetadata(htmlMember.metadata, 'DomName'); | |
| 175 if (domNameMetadata != null) { | |
| 176 var domNames = <String>[]; | |
| 177 var names = domNameMetadata.getField(symbolOf('name')); | |
| 178 for (var s in names.reflectee.split(',')) { | |
| 179 domNames.add(s.trim()); | |
| 180 } | |
| 181 | |
| 182 if (domNames.length == 1 && domNames[0] == 'none') return new Set(); | |
| 183 final members = new Set(); | |
| 184 domNames.forEach((name) { | |
| 185 var nameMembers = _membersFromName(name, domTypes); | |
| 186 if (nameMembers.isEmpty) { | |
| 187 if (name.contains('.')) { | |
| 188 warn('no member $name'); | |
| 189 } else { | |
| 190 final options = <String>[]; | |
| 191 for (var t in domTypes) { | |
| 192 options.add('$t.$name'); | |
| 193 } | |
| 194 options.join(' or '); | |
| 195 warn('no member $options'); | |
| 196 } | |
| 197 } | |
| 198 members.addAll(nameMembers); | |
| 199 }); | |
| 200 return members; | |
| 201 } | |
| 202 | |
| 203 return new Set(); | |
| 204 } | |
| 205 | |
| 206 /** | |
| 207 * Returns the `@DomName` strings that are indicated by | |
| 208 * [name]. [name] can be either an unqualified member name | |
| 209 * (e.g. `createElement`), in which case it's treated as the name of | |
| 210 * a member of one of [defaultTypes], or a fully-qualified member | |
| 211 * name (e.g. `Document.createElement`), in which case it's treated as a | |
| 212 * member of the @DomName element (`Document` in this case). | |
| 213 */ | |
| 214 Set<String> _membersFromName(String name, List<String> defaultTypes) { | |
| 215 if (!name.contains('.', 0)) { | |
| 216 if (defaultTypes.isEmpty) { | |
| 217 warn('no default type for $name'); | |
| 218 return new Set(); | |
| 219 } | |
| 220 final members = new Set<String>(); | |
| 221 defaultTypes.forEach((t) { members.add('$t.$name'); }); | |
| 222 return members; | |
| 223 } | |
| 224 | |
| 225 if (name.split('.').length != 2) { | |
| 226 warn('invalid member name ${name}'); | |
| 227 return new Set(); | |
| 228 } | |
| 229 return new Set.from([name]); | |
| 230 } | |
| 231 } | |
| OLD | NEW |