OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 // Script that generates different approaches to initialize classes in | 5 // Script that generates different approaches to initialize classes in |
6 // JavaScript. | 6 // JavaScript. |
7 // Also benchmarks the approaches. | 7 // Also benchmarks the approaches. |
8 | 8 |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 import 'dart:async'; | 10 import 'dart:async'; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 final bool shouldEmitInstantiatePreviousMethod; | 60 final bool shouldEmitInstantiatePreviousMethod; |
61 | 61 |
62 /// Makes sure that the dart2js tree-shaker doesn't remove classes. | 62 /// Makes sure that the dart2js tree-shaker doesn't remove classes. |
63 /// | 63 /// |
64 /// When set to `-1`, all classes are kept alive. | 64 /// When set to `-1`, all classes are kept alive. |
65 final int fakeInstantiateClass; | 65 final int fakeInstantiateClass; |
66 | 66 |
67 /// Defines the percent of classes that are dynamically instantiated. | 67 /// Defines the percent of classes that are dynamically instantiated. |
68 final int instantiateClassesPercent; | 68 final int instantiateClassesPercent; |
69 | 69 |
70 Config({this.nbClasses, this.nbMethodsPerClass, | 70 Config( |
71 this.shareCommonSuperclass, this.sameMethodNames, | 71 {this.nbClasses, |
72 this.shouldPrintInMethod, this.nbWhileLoopsInBody, | 72 this.nbMethodsPerClass, |
73 this.shouldWrapProgram, this.shouldEmitCallAllMethods, | 73 this.shareCommonSuperclass, |
74 this.shouldEmitInstantiatePreviousMethod, | 74 this.sameMethodNames, |
75 this.fakeInstantiateClass, this.instantiateClassesPercent}); | 75 this.shouldPrintInMethod, |
| 76 this.nbWhileLoopsInBody, |
| 77 this.shouldWrapProgram, |
| 78 this.shouldEmitCallAllMethods, |
| 79 this.shouldEmitInstantiatePreviousMethod, |
| 80 this.fakeInstantiateClass, |
| 81 this.instantiateClassesPercent}); |
76 } | 82 } |
77 | 83 |
78 String get d8Path { | 84 String get d8Path { |
79 Uri scriptPath = Platform.script; | 85 Uri scriptPath = Platform.script; |
80 String d8Executable = "../../../third_party/d8/"; | 86 String d8Executable = "../../../third_party/d8/"; |
81 if (Platform.isWindows) { | 87 if (Platform.isWindows) { |
82 d8Executable += "windows/d8.exe"; | 88 d8Executable += "windows/d8.exe"; |
83 } else if (Platform.isMacOS) { | 89 } else if (Platform.isMacOS) { |
84 d8Executable += "macos/d8"; | 90 d8Executable += "macos/d8"; |
85 } else if (Platform.isLinux) { | 91 } else if (Platform.isLinux) { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 } finally { | 179 } finally { |
174 dir.deleteSync(recursive: true); | 180 dir.deleteSync(recursive: true); |
175 } | 181 } |
176 } | 182 } |
177 | 183 |
178 Future<String> generateRawJs(String filePrefix); | 184 Future<String> generateRawJs(String filePrefix); |
179 | 185 |
180 String buildFileName(String filePrefix, String extension) { | 186 String buildFileName(String filePrefix, String extension) { |
181 // TODO(floitsch): store other config info in the file name. | 187 // TODO(floitsch): store other config info in the file name. |
182 return "$filePrefix.$nbClasses.$nbMethodsPerClass." | 188 return "$filePrefix.$nbClasses.$nbMethodsPerClass." |
183 "$instantiateClassesPercent.$description.$extension"; | 189 "$instantiateClassesPercent.$description.$extension"; |
184 } | 190 } |
185 | 191 |
186 String writeFile(String filePrefix) { | 192 String writeFile(String filePrefix) { |
187 buffer.clear(); | 193 buffer.clear(); |
188 emitClasses(); // Output is stored in `buffer`. | 194 emitClasses(); // Output is stored in `buffer`. |
189 | 195 |
190 String fileName = buildFileName(filePrefix, fileExtension); | 196 String fileName = buildFileName(filePrefix, fileExtension); |
191 new File(fileName).writeAsStringSync(buffer.toString()); | 197 new File(fileName).writeAsStringSync(buffer.toString()); |
192 print("wrote: $fileName"); | 198 print("wrote: $fileName"); |
193 return fileName; | 199 return fileName; |
194 } | 200 } |
195 | 201 |
196 void writeln(x) => buffer.writeln(x); | 202 void writeln(x) => buffer.writeln(x); |
197 | 203 |
198 String classIdToName(int id) { | 204 String classIdToName(int id) { |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 o = o.$instantiatePreviousMethodName(); | 302 o = o.$instantiatePreviousMethodName(); |
297 } while(o != null); | 303 } while(o != null); |
298 """); | 304 """); |
299 } | 305 } |
300 if (wrapProgram) writeln("})();"); | 306 if (wrapProgram) writeln("})();"); |
301 } | 307 } |
302 | 308 |
303 String get fileExtension => "js"; | 309 String get fileExtension => "js"; |
304 } | 310 } |
305 | 311 |
306 enum PrototypeApproach { | 312 enum PrototypeApproach { tmpFunction, internalProto, objectCreate } |
307 tmpFunction, | 313 |
308 internalProto, | |
309 objectCreate | |
310 } | |
311 class PlainJavaScriptClassGenerator extends JavaScriptClassGenerator { | 314 class PlainJavaScriptClassGenerator extends JavaScriptClassGenerator { |
312 final PrototypeApproach prototypeApproach; | 315 final PrototypeApproach prototypeApproach; |
313 final bool shouldInlineInherit; | 316 final bool shouldInlineInherit; |
314 final bool useMethodsObject; | 317 final bool useMethodsObject; |
315 | 318 |
316 PlainJavaScriptClassGenerator(Config config, | 319 PlainJavaScriptClassGenerator(Config config, |
317 {this.prototypeApproach, | 320 {this.prototypeApproach, this.shouldInlineInherit, this.useMethodsObject}) |
318 this.shouldInlineInherit, | |
319 this.useMethodsObject}) | |
320 : super(config) { | 321 : super(config) { |
321 if (prototypeApproach == null) { | 322 if (prototypeApproach == null) { |
322 throw "Must provide prototype approach"; | 323 throw "Must provide prototype approach"; |
323 } | 324 } |
324 if (shouldInlineInherit == null) { | 325 if (shouldInlineInherit == null) { |
325 throw "Must provide inlining approach"; | 326 throw "Must provide inlining approach"; |
326 } | 327 } |
327 if (useMethodsObject == null) { | 328 if (useMethodsObject == null) { |
328 throw "Must provide object-proto approach"; | 329 throw "Must provide object-proto approach"; |
329 } | 330 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 case PrototypeApproach.internalProto: | 377 case PrototypeApproach.internalProto: |
377 if (useMethodsObject) { | 378 if (useMethodsObject) { |
378 writeln(''' | 379 writeln(''' |
379 function inherit(cls, sup, methods) { | 380 function inherit(cls, sup, methods) { |
380 cls.prototype = methods; | 381 cls.prototype = methods; |
381 if (sup != null) { | 382 if (sup != null) { |
382 cls.prototype.__proto__ = sup.prototype; | 383 cls.prototype.__proto__ = sup.prototype; |
383 } | 384 } |
384 } | 385 } |
385 '''); | 386 '''); |
386 | |
387 } else { | 387 } else { |
388 writeln(''' | 388 writeln(''' |
389 function inherit(cls, sup) { | 389 function inherit(cls, sup) { |
390 cls.prototype.__proto__ = sup.prototype; | 390 cls.prototype.__proto__ = sup.prototype; |
391 } | 391 } |
392 '''); | 392 '''); |
393 } | 393 } |
394 break; | 394 break; |
395 case PrototypeApproach.tmpFunction: | 395 case PrototypeApproach.tmpFunction: |
396 if (useMethodsObject) { | 396 if (useMethodsObject) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
441 function copyProperties(from, to) { | 441 function copyProperties(from, to) { |
442 var props = Object.keys(from); | 442 var props = Object.keys(from); |
443 for (var i = 0; i < props.length; i++) { | 443 for (var i = 0; i < props.length; i++) { |
444 var p = props[i]; | 444 var p = props[i]; |
445 to[p] = from[p]; | 445 to[p] = from[p]; |
446 } | 446 } |
447 }"""); | 447 }"""); |
448 } | 448 } |
449 | 449 |
450 void emitMethod(int classId, String methodName, Function bodyEmitter, | 450 void emitMethod(int classId, String methodName, Function bodyEmitter, |
451 {bool emitArgument: true}) { | 451 {bool emitArgument: true}) { |
452 String argumentString = emitArgument ? argumentName : ""; | 452 String argumentString = emitArgument ? argumentName : ""; |
453 if (useMethodsObject) { | 453 if (useMethodsObject) { |
454 writeln("$methodName: function($argumentString)"); | 454 writeln("$methodName: function($argumentString)"); |
455 bodyEmitter(); | 455 bodyEmitter(); |
456 writeln(","); | 456 writeln(","); |
457 } else { | 457 } else { |
458 String className = classIdToName(classId); | 458 String className = classIdToName(classId); |
459 String proto = "$className.prototype"; | 459 String proto = "$className.prototype"; |
460 writeln("$proto.$methodName = function($argumentString)"); | 460 writeln("$proto.$methodName = function($argumentString)"); |
461 bodyEmitter(); | 461 bodyEmitter(); |
462 } | 462 } |
463 } | 463 } |
464 | 464 |
465 /// Returns the methods object, if we use an object. | 465 /// Returns the methods object, if we use an object. |
466 void emitMethods(int classId) { | 466 void emitMethods(int classId) { |
467 List<int> methodIds = []; | 467 List<int> methodIds = []; |
468 int nbGenericMethods = nbMethodsPerClass; | 468 int nbGenericMethods = nbMethodsPerClass; |
469 if (useMethodsObject) { | 469 if (useMethodsObject) { |
470 writeln("$methodsObjectName = {"); | 470 writeln("$methodsObjectName = {"); |
471 } | 471 } |
472 if (shouldEmitCallAllMethods) nbGenericMethods--; | 472 if (shouldEmitCallAllMethods) nbGenericMethods--; |
473 for (int j = 0; j < nbGenericMethods; j++) { | 473 for (int j = 0; j < nbGenericMethods; j++) { |
474 String methodName = methodIdToName(j, classId); | 474 String methodName = methodIdToName(j, classId); |
475 emitMethod(classId, methodName, () => emitMethodBody(j, classId)); | 475 emitMethod(classId, methodName, () => emitMethodBody(j, classId)); |
476 methodIds.add(j); | 476 methodIds.add(j); |
477 } | 477 } |
478 if (shouldEmitCallAllMethods) { | 478 if (shouldEmitCallAllMethods) { |
479 emitMethod(classId, | 479 emitMethod(classId, callOtherMethodsName, |
480 callOtherMethodsName, | |
481 () => emitCallOtherMethodsBody(methodIds, classId)); | 480 () => emitCallOtherMethodsBody(methodIds, classId)); |
482 } | 481 } |
483 if (shouldEmitInstantiatePreviousMethod) { | 482 if (shouldEmitInstantiatePreviousMethod) { |
484 emitMethod(classId, | 483 emitMethod(classId, instantiatePreviousMethodName, |
485 instantiatePreviousMethodName, | |
486 () => emitInstantiatePrevious(classId), | 484 () => emitInstantiatePrevious(classId), |
487 emitArgument: false); | 485 emitArgument: false); |
488 } | 486 } |
489 if (useMethodsObject) { | 487 if (useMethodsObject) { |
490 writeln("};"); | 488 writeln("};"); |
491 } | 489 } |
492 } | 490 } |
493 | 491 |
494 void emitClass(int classId, int superclassId) { | 492 void emitClass(int classId, int superclassId) { |
495 String className = classIdToName(classId); | 493 String className = classIdToName(classId); |
496 writeln("function $className() {}"); | 494 writeln("function $className() {}"); |
497 switch (prototypeApproach) { | 495 switch (prototypeApproach) { |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
632 | 630 |
633 Future<String> generateRawJs(String filePrefix) async { | 631 Future<String> generateRawJs(String filePrefix) async { |
634 String dartFile = writeFile(filePrefix); | 632 String dartFile = writeFile(filePrefix); |
635 String outFile = buildFileName(filePrefix, "js"); | 633 String outFile = buildFileName(filePrefix, "js"); |
636 Map<String, String> env = {}; | 634 Map<String, String> env = {}; |
637 if (shouldUseNewEmitter) { | 635 if (shouldUseNewEmitter) { |
638 env["DART_VM_OPTIONS"] = '-Ddart2js.use.new.emitter=true'; | 636 env["DART_VM_OPTIONS"] = '-Ddart2js.use.new.emitter=true'; |
639 } | 637 } |
640 print("compiling"); | 638 print("compiling"); |
641 print("dart2jsPath: $dart2jsPath"); | 639 print("dart2jsPath: $dart2jsPath"); |
642 ProcessResult result = | 640 ProcessResult result = await Process |
643 await Process.run(dart2jsPath, [dartFile, "--out=$outFile"], | 641 .run(dart2jsPath, [dartFile, "--out=$outFile"], environment: env); |
644 environment: env); | |
645 if (result.exitCode != 0) { | 642 if (result.exitCode != 0) { |
646 print("compilation failed"); | 643 print("compilation failed"); |
647 print(result.stdout); | 644 print(result.stdout); |
648 print(result.stderr); | 645 print(result.stderr); |
649 return null; | 646 return null; |
650 } | 647 } |
651 print("compilation done"); | 648 print("compilation done"); |
652 return outFile; | 649 return outFile; |
653 } | 650 } |
654 | 651 |
655 Future measureDart(String filePrefix, { bool useSnapshot: false }) async { | 652 Future measureDart(String filePrefix, {bool useSnapshot: false}) async { |
656 String dartFile = writeFile(filePrefix); | 653 String dartFile = writeFile(filePrefix); |
657 String command = Platform.executable; | 654 String command = Platform.executable; |
658 Stopwatch watch = new Stopwatch(); | 655 Stopwatch watch = new Stopwatch(); |
659 Directory dir = Directory.systemTemp.createTempSync('snapshot'); | 656 Directory dir = Directory.systemTemp.createTempSync('snapshot'); |
660 try { | 657 try { |
661 String measuring = dartFile; | 658 String measuring = dartFile; |
662 if (useSnapshot) { | 659 if (useSnapshot) { |
663 print("creating snapshot"); | 660 print("creating snapshot"); |
664 measuring = new File("${dir.path}/measuring.snapshot").path; | 661 measuring = new File("${dir.path}/measuring.snapshot").path; |
665 ProcessResult result = | 662 ProcessResult result = |
666 await Process.run(command, ["--snapshot=$measuring", dartFile]); | 663 await Process.run(command, ["--snapshot=$measuring", dartFile]); |
667 if (result.exitCode != 0) { | 664 if (result.exitCode != 0) { |
668 print("snapshot creation failed"); | 665 print("snapshot creation failed"); |
669 print(result.stdout); | 666 print(result.stdout); |
670 print(result.stderr); | 667 print(result.stderr); |
671 return; | 668 return; |
672 } | 669 } |
673 } | 670 } |
674 List<String> args = [measuring]; | 671 List<String> args = [measuring]; |
675 print("Running: $command ${args.join(' ')}"); | 672 print("Running: $command ${args.join(' ')}"); |
676 int nbRuns = 10; | 673 int nbRuns = 10; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
713 nbClasses: 2000, | 710 nbClasses: 2000, |
714 nbMethodsPerClass: 20, | 711 nbMethodsPerClass: 20, |
715 fakeInstantiateClass: -1, | 712 fakeInstantiateClass: -1, |
716 instantiateClassesPercent: 20, | 713 instantiateClassesPercent: 20, |
717 shareCommonSuperclass: true, | 714 shareCommonSuperclass: true, |
718 sameMethodNames: true, | 715 sameMethodNames: true, |
719 shouldPrintInMethod: true, | 716 shouldPrintInMethod: true, |
720 nbWhileLoopsInBody: 1, | 717 nbWhileLoopsInBody: 1, |
721 shouldWrapProgram: true, | 718 shouldWrapProgram: true, |
722 shouldEmitCallAllMethods: true, | 719 shouldEmitCallAllMethods: true, |
723 shouldEmitInstantiatePreviousMethod: true | 720 shouldEmitInstantiatePreviousMethod: true); |
724 ); | |
725 | 721 |
726 var plain = new PlainJavaScriptClassGenerator(config, | 722 var plain = new PlainJavaScriptClassGenerator(config, |
727 prototypeApproach: PrototypeApproach.tmpFunction, | 723 prototypeApproach: PrototypeApproach.tmpFunction, |
728 useMethodsObject: false, | 724 useMethodsObject: false, |
729 shouldInlineInherit: false); | 725 shouldInlineInherit: false); |
730 var plainProto = new PlainJavaScriptClassGenerator(config, | 726 var plainProto = new PlainJavaScriptClassGenerator(config, |
731 prototypeApproach: PrototypeApproach.internalProto, | 727 prototypeApproach: PrototypeApproach.internalProto, |
732 useMethodsObject: false, | 728 useMethodsObject: false, |
733 shouldInlineInherit: false); | 729 shouldInlineInherit: false); |
734 var plainObjectCreate = new PlainJavaScriptClassGenerator(config, | 730 var plainObjectCreate = new PlainJavaScriptClassGenerator(config, |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
776 await plainProtoObj.measure(filePrefix); | 772 await plainProtoObj.measure(filePrefix); |
777 await plainObjectCreateObj.measure(filePrefix); | 773 await plainObjectCreateObj.measure(filePrefix); |
778 await plainProtoInlineObj.measure(filePrefix); | 774 await plainProtoInlineObj.measure(filePrefix); |
779 await plainObjectCreateInlineObj.measure(filePrefix); | 775 await plainObjectCreateInlineObj.measure(filePrefix); |
780 await es6.measure(filePrefix); | 776 await es6.measure(filePrefix); |
781 await dartNew.measure(filePrefix); | 777 await dartNew.measure(filePrefix); |
782 await dart.measure(filePrefix); | 778 await dart.measure(filePrefix); |
783 await dart.measureDart(filePrefix); | 779 await dart.measureDart(filePrefix); |
784 await dart.measureDart(filePrefix, useSnapshot: true); | 780 await dart.measureDart(filePrefix, useSnapshot: true); |
785 } | 781 } |
OLD | NEW |