| OLD | NEW |
| 1 #!/usr/bin/env dart | 1 #!/usr/bin/env dart |
| 2 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 2 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
| 4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
| 5 | 5 |
| 6 /// Command line tool to merge the SDK libraries and our patch files. | 6 /// Command line tool to merge the SDK libraries and our patch files. |
| 7 /// This is currently designed as an offline tool, but we could automate it. | 7 /// This is currently designed as an offline tool, but we could automate it. |
| 8 | 8 |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 import 'dart:math' as math; | 10 import 'dart:math' as math; |
| 11 | 11 |
| 12 import 'package:analyzer/analyzer.dart'; | 12 import 'package:analyzer/analyzer.dart'; |
| 13 import 'package:analyzer/src/generated/sdk.dart'; | 13 import 'package:analyzer/src/generated/sdk.dart'; |
| 14 import 'package:path/path.dart' as path; | 14 import 'package:path/path.dart' as path; |
| 15 | 15 |
| 16 void main(List<String> argv) { | 16 void main(List<String> argv) { |
| 17 if (argv.length < 2) { | 17 var base = path.fromUri(Platform.script); |
| 18 var self = path.relative(path.fromUri(Platform.script)); | 18 var dartDir = path.dirname(path.dirname(path.absolute(base))); |
| 19 var toolDir = path.relative(path.dirname(path.fromUri(Platform.script))); | |
| 20 | 19 |
| 21 var inputExample = path.join(toolDir, 'input_sdk'); | 20 if (argv.length != 4 || |
| 21 !argv.isEmpty && argv.first != 'vm' && argv.first != 'ddc') { |
| 22 var self = path.relative(base); |
| 23 print('Usage: $self MODE SDK_DIR PATCH_DIR OUTPUT_DIR'); |
| 24 print('MODE must be one of ddc or vm.'); |
| 25 |
| 26 var toolDir = path.relative(path.dirname(base)); |
| 27 var sdkExample = path.join(toolDir, 'input_sdk'); |
| 28 var patchExample = path.join(sdkExample, 'patch'); |
| 22 var outExample = | 29 var outExample = |
| 23 path.relative(path.normalize(path.join('gen', 'patched_sdk'))); | 30 path.relative(path.normalize(path.join('gen', 'patched_sdk'))); |
| 31 print('For example:'); |
| 32 print('\$ $self ddc $sdkExample $patchExample $outExample'); |
| 24 | 33 |
| 25 print('Usage: $self INPUT_DIR OUTPUT_DIR'); | 34 var repositoryDir = path.relative(path.dirname(path.dirname(base))); |
| 26 print('For example:'); | 35 sdkExample = path.relative(path.join(repositoryDir, 'sdk')); |
| 27 print('\$ $self $inputExample $outExample'); | 36 patchExample = path.relative(path.join(repositoryDir, 'out', 'DebugX64', |
| 37 'obj', 'gen', 'patch')); |
| 38 outExample = path.relative(path.join(repositoryDir, 'out', 'DebugX64', |
| 39 'obj', 'gen', 'patched_sdk')); |
| 40 print('or:'); |
| 41 print('\$ $self vm $sdkExample $patchExample $outExample'); |
| 42 |
| 28 exit(1); | 43 exit(1); |
| 29 } | 44 } |
| 30 | 45 |
| 31 var input = argv[0]; | 46 var mode = argv[0]; |
| 47 var input = argv[1]; |
| 32 var sdkLibIn = path.join(input, 'lib'); | 48 var sdkLibIn = path.join(input, 'lib'); |
| 33 var patchIn = path.join(input, 'patch'); | 49 var patchIn = argv[2]; |
| 50 var sdkOut = path.join(argv[3], 'lib'); |
| 51 |
| 34 var privateIn = path.join(input, 'private'); | 52 var privateIn = path.join(input, 'private'); |
| 35 var sdkOut = path.join(argv[1], 'lib'); | |
| 36 | |
| 37 var INTERNAL_PATH = '_internal/compiler/js_lib/'; | 53 var INTERNAL_PATH = '_internal/compiler/js_lib/'; |
| 38 | 54 |
| 39 // Copy libraries.dart and version | 55 // Copy and patch libraries.dart and version |
| 40 var libContents = new File(path.join(sdkLibIn, '_internal', 'libraries.dart')) | 56 var libContents = new File(path.join(sdkLibIn, '_internal', |
| 41 .readAsStringSync(); | 57 'sdk_library_metadata', 'lib', 'libraries.dart')).readAsStringSync(); |
| 42 _writeSync(path.join(sdkOut, '_internal', 'libraries.dart'), libContents); | 58 var patchedLibContents = libContents; |
| 59 if (mode == 'vm') { |
| 60 libContents = libContents.replaceAll( |
| 61 ' libraries = const {', |
| 62 ''' libraries = const { |
| 63 |
| 64 "_builtin": const LibraryInfo( |
| 65 "_builtin/_builtin.dart", |
| 66 categories: "Client,Server", |
| 67 implementation: true, |
| 68 documented: false, |
| 69 platforms: VM_PLATFORM), |
| 70 |
| 71 "profiler": const LibraryInfo( |
| 72 "profiler/profiler.dart", |
| 73 maturity: Maturity.DEPRECATED, |
| 74 documented: false), |
| 75 |
| 76 "_vmservice": const LibraryInfo( |
| 77 "vmservice/vmservice.dart", |
| 78 implementation: true, |
| 79 documented: false, |
| 80 platforms: VM_PLATFORM), |
| 81 |
| 82 "vmservice_io": const LibraryInfo( |
| 83 "vmservice_io/vmservice_io.dart", |
| 84 implementation: true, |
| 85 documented: false, |
| 86 platforms: VM_PLATFORM), |
| 87 |
| 88 '''); |
| 89 } |
| 43 _writeSync( | 90 _writeSync( |
| 44 path.join( | 91 path.join( |
| 45 sdkOut, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart'), | 92 sdkOut, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart'), |
| 46 libContents); | 93 libContents); |
| 47 _writeSync(path.join(sdkOut, '..', 'version'), | 94 if (mode == 'ddc') { |
| 48 new File(path.join(sdkLibIn, '..', 'version')).readAsStringSync()); | 95 _writeSync(path.join(sdkOut, '..', 'version'), |
| 96 new File(path.join(sdkLibIn, '..', 'version')).readAsStringSync()); |
| 97 } |
| 49 | 98 |
| 50 // Parse libraries.dart | 99 // Parse libraries.dart |
| 51 var sdkLibraries = _getSdkLibraries(libContents); | 100 var sdkLibraries = _getSdkLibraries(libContents); |
| 52 | 101 |
| 53 // Enumerate core libraries and apply patches | 102 // Enumerate core libraries and apply patches |
| 54 for (SdkLibrary library in sdkLibraries) { | 103 for (SdkLibrary library in sdkLibraries) { |
| 55 // TODO(jmesserly): analyzer does not handle the default case of | 104 // TODO(jmesserly): analyzer does not handle the default case of |
| 56 // "both platforms" correctly, and treats it as being supported on neither. | 105 // "both platforms" correctly, and treats it as being supported on neither. |
| 57 // So instead we skip explicitly marked as VM libs. | 106 // So instead we skip explicitly marked as either VM or dart2js libs. |
| 58 if (library.isVmLibrary) continue; | 107 if (mode == 'ddc' ? libary.isVmLibrary : library.isDart2JsLibrary) { |
| 108 continue; |
| 109 } |
| 59 | 110 |
| 60 var libraryOut = path.join(sdkLibIn, library.path); | 111 var libraryOut = path.join(sdkLibIn, library.path); |
| 61 var libraryIn; | 112 var libraryIn; |
| 62 if (library.path.contains(INTERNAL_PATH)) { | 113 if (mode == 'vm' && library.path.contains('typed_data.dart')) { |
| 114 // dart:typed_data is unlike the other libraries in the SDK. The VM does |
| 115 // not apply a patch to the base SDK implementation of the library. |
| 116 // Instead, the VM provides a replacement implementation and ignores the |
| 117 // sources in the SDK. |
| 118 libraryIn = |
| 119 path.join(dartDir, 'runtime', 'lib', 'typed_data.dart'); |
| 120 } else if (mode == 'ddc' && library.path.contains(INTERNAL_PATH)) { |
| 63 libraryIn = | 121 libraryIn = |
| 64 path.join(privateIn, library.path.replaceAll(INTERNAL_PATH, '')); | 122 path.join(privateIn, library.path.replaceAll(INTERNAL_PATH, '')); |
| 65 } else { | 123 } else { |
| 66 libraryIn = libraryOut; | 124 libraryIn = libraryOut; |
| 67 } | 125 } |
| 68 | 126 |
| 69 var libraryFile = new File(libraryIn); | 127 var libraryFile = new File(libraryIn); |
| 70 if (libraryFile.existsSync()) { | 128 if (libraryFile.existsSync()) { |
| 71 var outPaths = <String>[libraryOut]; | 129 var outPaths = <String>[libraryOut]; |
| 72 var libraryContents = libraryFile.readAsStringSync(); | 130 var libraryContents = libraryFile.readAsStringSync(); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 needsUpdate = true; | 170 needsUpdate = true; |
| 113 break; | 171 break; |
| 114 } | 172 } |
| 115 } | 173 } |
| 116 | 174 |
| 117 if (needsUpdate) { | 175 if (needsUpdate) { |
| 118 var contents = <String>[libraryContents]; | 176 var contents = <String>[libraryContents]; |
| 119 contents.addAll(partFiles.map((f) => f.readAsStringSync())); | 177 contents.addAll(partFiles.map((f) => f.readAsStringSync())); |
| 120 if (patchExists) { | 178 if (patchExists) { |
| 121 var patchContents = patchFile.readAsStringSync(); | 179 var patchContents = patchFile.readAsStringSync(); |
| 122 contents = _patchLibrary(contents, patchContents); | 180 contents = _patchLibrary( |
| 181 patchFile.path, contents, patchContents); |
| 123 } | 182 } |
| 124 | 183 |
| 125 for (var i = 0; i < outPaths.length; i++) { | 184 for (var i = 0; i < outPaths.length; i++) { |
| 185 if (path.basename(outPaths[i]) == 'internal.dart') { |
| 186 contents[i] += ''' |
| 187 |
| 188 /// Marks a function as an external implementation ("native" in the Dart VM). |
| 189 /// |
| 190 /// Provides a backend-specific String that can be used to identify the |
| 191 /// function's implementation |
| 192 class ExternalName { |
| 193 final String name; |
| 194 const ExternalName(this.name); |
| 195 } |
| 196 '''; |
| 197 } |
| 198 |
| 126 _writeSync(outPaths[i], contents[i]); | 199 _writeSync(outPaths[i], contents[i]); |
| 127 } | 200 } |
| 128 } | 201 } |
| 129 } | 202 } |
| 130 } | 203 } |
| 204 if (mode == 'vm') { |
| 205 |
| 206 for (var tuple in [['_builtin', 'builtin.dart']]) { |
| 207 var vmLibrary = tuple[0]; |
| 208 var dartFile = tuple[1]; |
| 209 |
| 210 // The "dart:_builtin" library is only available for the DartVM. |
| 211 var builtinLibraryIn = path.join(dartDir, 'runtime', 'bin', dartFile); |
| 212 var builtinLibraryOut = path.join(sdkOut, vmLibrary, '${vmLibrary}.dart'); |
| 213 _writeSync(builtinLibraryOut, new File(builtinLibraryIn).readAsStringSync(
)); |
| 214 } |
| 215 |
| 216 for (var file in ['loader.dart', 'server.dart', 'vmservice_io.dart']) { |
| 217 var libraryIn = path.join(dartDir, 'runtime', 'bin', 'vmservice', file); |
| 218 var libraryOut = path.join(sdkOut, 'vmservice_io', file); |
| 219 _writeSync(libraryOut, new File(libraryIn).readAsStringSync()); |
| 220 } |
| 221 } |
| 131 } | 222 } |
| 132 | 223 |
| 133 /// Writes a file, creating the directory if needed. | 224 /// Writes a file, creating the directory if needed. |
| 134 void _writeSync(String filePath, String contents) { | 225 void _writeSync(String filePath, String contents) { |
| 135 var outDir = new Directory(path.dirname(filePath)); | 226 var outDir = new Directory(path.dirname(filePath)); |
| 136 if (!outDir.existsSync()) outDir.createSync(recursive: true); | 227 if (!outDir.existsSync()) outDir.createSync(recursive: true); |
| 137 | 228 |
| 138 new File(filePath).writeAsStringSync(contents); | 229 new File(filePath).writeAsStringSync(contents); |
| 139 } | 230 } |
| 140 | 231 |
| 141 /// Merges dart:* library code with code from *_patch.dart file. | 232 /// Merges dart:* library code with code from *_patch.dart file. |
| 142 /// | 233 /// |
| 143 /// Takes a list of the library's parts contents, with the main library contents | 234 /// Takes a list of the library's parts contents, with the main library contents |
| 144 /// first in the list, and the contents of the patch file. | 235 /// first in the list, and the contents of the patch file. |
| 145 /// | 236 /// |
| 146 /// The result will have `@patch` implementations merged into the correct place | 237 /// The result will have `@patch` implementations merged into the correct place |
| 147 /// (e.g. the class or top-level function declaration) and all other | 238 /// (e.g. the class or top-level function declaration) and all other |
| 148 /// declarations introduced by the patch will be placed into the main library | 239 /// declarations introduced by the patch will be placed into the main library |
| 149 /// file. | 240 /// file. |
| 150 /// | 241 /// |
| 151 /// This is purely a syntactic transformation. Unlike dart2js patch files, there | 242 /// This is purely a syntactic transformation. Unlike dart2js patch files, there |
| 152 /// is no semantic meaning given to the *_patch files, and they do not magically | 243 /// is no semantic meaning given to the *_patch files, and they do not magically |
| 153 /// get their own library scope, etc. | 244 /// get their own library scope, etc. |
| 154 /// | 245 /// |
| 155 /// Editorializing: the dart2js approach requires a Dart front end such as | 246 /// Editorializing: the dart2js approach requires a Dart front end such as |
| 156 /// package:analyzer to semantically model a feature beyond what is specified | 247 /// package:analyzer to semantically model a feature beyond what is specified |
| 157 /// in the Dart language. Since this feature is only for the convenience of | 248 /// in the Dart language. Since this feature is only for the convenience of |
| 158 /// writing the dart:* libraries, and not a tool given to Dart developers, it | 249 /// writing the dart:* libraries, and not a tool given to Dart developers, it |
| 159 /// seems like a non-ideal situation. Instead we keep the preprocessing simple. | 250 /// seems like a non-ideal situation. Instead we keep the preprocessing simple. |
| 160 List<String> _patchLibrary(List<String> partsContents, String patchContents) { | 251 List<String> _patchLibrary(String name, |
| 252 List<String> partsContents, |
| 253 String patchContents) { |
| 161 var results = <StringEditBuffer>[]; | 254 var results = <StringEditBuffer>[]; |
| 162 | 255 |
| 163 // Parse the patch first. We'll need to extract bits of this as we go through | 256 // Parse the patch first. We'll need to extract bits of this as we go through |
| 164 // the other files. | 257 // the other files. |
| 165 var patchFinder = new PatchFinder.parseAndVisit(patchContents); | 258 final patchFinder = new PatchFinder.parseAndVisit(name, patchContents); |
| 166 | 259 |
| 167 // Merge `external` declarations with the corresponding `@patch` code. | 260 // Merge `external` declarations with the corresponding `@patch` code. |
| 168 for (var partContent in partsContents) { | 261 for (var partContent in partsContents) { |
| 169 var partEdits = new StringEditBuffer(partContent); | 262 var partEdits = new StringEditBuffer(partContent); |
| 170 var partUnit = parseCompilationUnit(partContent); | 263 var partUnit = parseCompilationUnit(partContent); |
| 171 partUnit.accept(new PatchApplier(partEdits, patchFinder)); | 264 partUnit.accept(new PatchApplier(partEdits, patchFinder)); |
| 172 results.add(partEdits); | 265 results.add(partEdits); |
| 173 } | 266 } |
| 174 return new List<String>.from(results.map((e) => e.toString())); | 267 return new List<String>.from(results.map((e) => e.toString())); |
| 175 } | 268 } |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 } | 364 } |
| 272 | 365 |
| 273 class PatchFinder extends GeneralizingAstVisitor { | 366 class PatchFinder extends GeneralizingAstVisitor { |
| 274 final String contents; | 367 final String contents; |
| 275 final CompilationUnit unit; | 368 final CompilationUnit unit; |
| 276 | 369 |
| 277 final Map patches = <String, Declaration>{}; | 370 final Map patches = <String, Declaration>{}; |
| 278 final Map mergeMembers = <String, List<ClassMember>>{}; | 371 final Map mergeMembers = <String, List<ClassMember>>{}; |
| 279 final List mergeDeclarations = <CompilationUnitMember>[]; | 372 final List mergeDeclarations = <CompilationUnitMember>[]; |
| 280 | 373 |
| 281 PatchFinder.parseAndVisit(String contents) | 374 PatchFinder.parseAndVisit(String name, String contents) |
| 282 : contents = contents, | 375 : contents = contents, |
| 283 unit = parseCompilationUnit(contents) { | 376 unit = parseCompilationUnit(contents, name: name) { |
| 284 visitCompilationUnit(unit); | 377 visitCompilationUnit(unit); |
| 285 } | 378 } |
| 286 | 379 |
| 287 @override | 380 @override |
| 288 visitCompilationUnitMember(CompilationUnitMember node) { | 381 visitCompilationUnitMember(CompilationUnitMember node) { |
| 289 mergeDeclarations.add(node); | 382 mergeDeclarations.add(node); |
| 290 } | 383 } |
| 291 | 384 |
| 292 @override | 385 @override |
| 293 visitClassDeclaration(ClassDeclaration node) { | 386 visitClassDeclaration(ClassDeclaration node) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 321 visitFunctionBody(node) {} // skip method bodies | 414 visitFunctionBody(node) {} // skip method bodies |
| 322 } | 415 } |
| 323 | 416 |
| 324 String _qualifiedName(Declaration node) { | 417 String _qualifiedName(Declaration node) { |
| 325 var parent = node.parent; | 418 var parent = node.parent; |
| 326 var className = ''; | 419 var className = ''; |
| 327 if (parent is ClassDeclaration) { | 420 if (parent is ClassDeclaration) { |
| 328 className = parent.name.name + '.'; | 421 className = parent.name.name + '.'; |
| 329 } | 422 } |
| 330 var name = (node as dynamic).name; | 423 var name = (node as dynamic).name; |
| 331 return className + (name != null ? name.name : ''); | 424 name = (name != null ? name.name : ''); |
| 425 |
| 426 var accessor = ''; |
| 427 if (node is MethodDeclaration) { |
| 428 if (node.isGetter) accessor = 'get:'; |
| 429 else if (node.isSetter) accessor = 'set:'; |
| 430 } |
| 431 return className + accessor + name; |
| 332 } | 432 } |
| 333 | 433 |
| 334 bool _isPatch(AnnotatedNode node) => node.metadata.any(_isPatchAnnotation); | 434 bool _isPatch(AnnotatedNode node) => node.metadata.any(_isPatchAnnotation); |
| 335 | 435 |
| 336 bool _isPatchAnnotation(Annotation m) => | 436 bool _isPatchAnnotation(Annotation m) => |
| 337 m.name.name == 'patch' && m.constructorName == null && m.arguments == null; | 437 m.name.name == 'patch' && m.constructorName == null && m.arguments == null; |
| 338 | 438 |
| 339 /// Editable string buffer. | 439 /// Editable string buffer. |
| 340 /// | 440 /// |
| 341 /// Applies a series of edits (insertions, removals, replacements) using | 441 /// Applies a series of edits (insertions, removals, replacements) using |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 if (diff != 0) return diff; | 527 if (diff != 0) return diff; |
| 428 return end - other.end; | 528 return end - other.end; |
| 429 } | 529 } |
| 430 } | 530 } |
| 431 | 531 |
| 432 List<SdkLibrary> _getSdkLibraries(String contents) { | 532 List<SdkLibrary> _getSdkLibraries(String contents) { |
| 433 var libraryBuilder = new SdkLibrariesReader_LibraryBuilder(true); | 533 var libraryBuilder = new SdkLibrariesReader_LibraryBuilder(true); |
| 434 parseCompilationUnit(contents).accept(libraryBuilder); | 534 parseCompilationUnit(contents).accept(libraryBuilder); |
| 435 return libraryBuilder.librariesMap.sdkLibraries; | 535 return libraryBuilder.librariesMap.sdkLibraries; |
| 436 } | 536 } |
| OLD | NEW |