OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // Script that generates different approaches to initialize classes in |
| 6 // JavaScript. |
| 7 // Also benchmarks the approaches. |
| 8 |
| 9 import 'dart:io'; |
| 10 import 'dart:async'; |
| 11 |
| 12 class Config { |
| 13 /// Number of classes that should be generated. |
| 14 final int nbClasses; |
| 15 |
| 16 /// Number of methods per class. |
| 17 final int nbMethodsPerClass; |
| 18 |
| 19 /// Should the JavaScript classes share a common super class? |
| 20 /// |
| 21 /// Currently unused for Dart, since it always has a common super class |
| 22 /// anyways. |
| 23 // TODO(floitsch): also create a common super class in Dart? |
| 24 final bool shareCommonSuperclass; |
| 25 |
| 26 /// Assign unique names to the methods or let them share the same one? |
| 27 /// |
| 28 /// Independent of this flag, the `callAll` and `instantiatePrevious` method |
| 29 /// names are the same for all classes. |
| 30 final bool sameMethodNames; |
| 31 |
| 32 /// Adds a `print` statement to the method. |
| 33 final bool shouldPrintInMethod; |
| 34 |
| 35 /// Adds while loops to the method body. |
| 36 /// |
| 37 /// This has the effect that dart2js won't be able to inline the method and |
| 38 /// controls the size of the method bodies. |
| 39 final int nbWhileLoopsInBody; |
| 40 |
| 41 /// Should the JavaScript output be wrapped into an anonymous function? |
| 42 /// |
| 43 /// When enabled wraps the program with the following pattern: |
| 44 /// `(function() { <program> })()`. |
| 45 final bool shouldWrapProgram; |
| 46 |
| 47 /// Adds a `callAll` method that invokes all other methods of the class. |
| 48 /// |
| 49 /// This is necessary for dart2js to avoid tree-shaking. |
| 50 /// Should probably always be on (except for presentations to demonstrate that |
| 51 /// dart2js knows how to tree-shake). |
| 52 /// |
| 53 /// This method counts towards the [nbMethodsPerClass] limit. |
| 54 final bool shouldEmitCallAllMethods; |
| 55 |
| 56 /// Adds an `instantiatePrevious` method that instantiates the previous class. |
| 57 /// |
| 58 /// A "previous" class is the class that was generated before the current |
| 59 /// class. The first class returns `null`. |
| 60 final bool shouldEmitInstantiatePreviousMethod; |
| 61 |
| 62 /// Makes sure that the dart2js tree-shaker doesn't remove classes. |
| 63 /// |
| 64 /// When set to `-1`, all classes are kept alive. |
| 65 final int fakeInstantiateClass; |
| 66 |
| 67 /// Defines the percent of classes that are dynamically instantiated. |
| 68 final int instantiateClassesPercent; |
| 69 |
| 70 Config({this.nbClasses, this.nbMethodsPerClass, |
| 71 this.shareCommonSuperclass, this.sameMethodNames, |
| 72 this.shouldPrintInMethod, this.nbWhileLoopsInBody, |
| 73 this.shouldWrapProgram, this.shouldEmitCallAllMethods, |
| 74 this.shouldEmitInstantiatePreviousMethod, |
| 75 this.fakeInstantiateClass, this.instantiateClassesPercent}); |
| 76 } |
| 77 |
| 78 String get d8Path { |
| 79 Uri scriptPath = Platform.script; |
| 80 String d8Executable = "../../../third_party/d8/"; |
| 81 if (Platform.isWindows) { |
| 82 d8Executable += "windows/d8.exe"; |
| 83 } else if (Platform.isMacOS) { |
| 84 d8Executable += "macos/d8"; |
| 85 } else if (Platform.isLinux) { |
| 86 d8Executable += "linux/d8"; |
| 87 } else { |
| 88 return null; |
| 89 } |
| 90 return scriptPath.resolve(d8Executable).path; |
| 91 } |
| 92 |
| 93 String get jsShellPath { |
| 94 Uri scriptPath = Platform.script; |
| 95 if (!Platform.isLinux) { |
| 96 return null; |
| 97 } |
| 98 return scriptPath.resolve("../../../tools/testing/bin/jsshell").path; |
| 99 } |
| 100 |
| 101 String get dart2jsPath { |
| 102 Uri scriptPath = Platform.script; |
| 103 return scriptPath.resolve("../../../sdk/bin/dart2js").path; |
| 104 } |
| 105 |
| 106 abstract class ClassGenerator { |
| 107 final StringBuffer buffer = new StringBuffer(); |
| 108 // By convention all methods should take one argument with this name. |
| 109 final String argumentName = "x"; |
| 110 final String callOtherMethodsName = "callAll"; |
| 111 final String instantiatePreviousMethodName = "instantiatePrevious"; |
| 112 |
| 113 final Config config; |
| 114 |
| 115 ClassGenerator(this.config); |
| 116 |
| 117 int get nbClasses => config.nbClasses; |
| 118 int get nbMethodsPerClass => config.nbMethodsPerClass; |
| 119 bool get shouldPrintInMethod => config.shouldPrintInMethod; |
| 120 bool get sameMethodNames => config.sameMethodNames; |
| 121 int get nbWhileLoopsInMethod => config.nbWhileLoopsInBody; |
| 122 bool get shareCommonSuperclass => config.shareCommonSuperclass; |
| 123 bool get shouldEmitCallAllMethods => config.shouldEmitCallAllMethods; |
| 124 bool get shouldEmitInstantiatePreviousMethod => |
| 125 config.shouldEmitInstantiatePreviousMethod; |
| 126 int get fakeInstantiateClass => config.fakeInstantiateClass; |
| 127 int get instantiateClassesPercent => config.instantiateClassesPercent; |
| 128 |
| 129 Future measure(String filePrefix) async { |
| 130 String fileName = await generateRawJs(filePrefix); |
| 131 if (fileName == null) return; |
| 132 Directory dir = Directory.systemTemp.createTempSync('classes'); |
| 133 try { |
| 134 File measuring = new File("${dir.path}/measuring.js"); |
| 135 IOSink sink = measuring.openWrite(); |
| 136 sink.writeln("var start = new Date();"); |
| 137 await sink.addStream(new File(fileName).openRead()); |
| 138 sink.writeln("print(new Date() - start)"); |
| 139 String command; |
| 140 List<String> args; |
| 141 bool runJsShell = false; |
| 142 if (runJsShell) { |
| 143 command = jsShellPath; |
| 144 print("Running $command"); |
| 145 args = [measuring.path]; |
| 146 } else { |
| 147 command = d8Path; |
| 148 print("Running $command"); |
| 149 args = ["--harmony-sloppy", measuring.path]; |
| 150 } |
| 151 print("Running: $fileName"); |
| 152 int nbRuns = 10; |
| 153 int sum = 0; |
| 154 int sumSw = 0; |
| 155 Stopwatch watch = new Stopwatch(); |
| 156 for (int i = 0; i < nbRuns; i++) { |
| 157 watch.reset(); |
| 158 watch.start(); |
| 159 ProcessResult result = await Process.run(command, args); |
| 160 if (result.exitCode != 0) { |
| 161 print("run failed"); |
| 162 print(result.stdout); |
| 163 print(result.stderr); |
| 164 } |
| 165 int elapsed = watch.elapsedMilliseconds; |
| 166 print(" output: ${result.stdout.trim()} ($elapsed)"); |
| 167 sum += int.parse(result.stdout, onError: (str) => 0); |
| 168 sumSw += elapsed; |
| 169 } |
| 170 int mean = sum == 0 ? 0 : sum ~/ nbRuns; |
| 171 int meanSw = sumSw == 0 ? 0 : sumSw ~/ nbRuns; |
| 172 print(" mean: $mean ($meanSw)"); |
| 173 } finally { |
| 174 dir.deleteSync(recursive: true); |
| 175 } |
| 176 } |
| 177 |
| 178 Future<String> generateRawJs(String filePrefix); |
| 179 |
| 180 String buildFileName(String filePrefix, String extension) { |
| 181 // TODO(floitsch): store other config info in the file name. |
| 182 return "$filePrefix.$nbClasses.$nbMethodsPerClass." |
| 183 "$instantiateClassesPercent.$description.$extension"; |
| 184 } |
| 185 |
| 186 String writeFile(String filePrefix) { |
| 187 buffer.clear(); |
| 188 emitClasses(); // Output is stored in `buffer`. |
| 189 |
| 190 String fileName = buildFileName(filePrefix, fileExtension); |
| 191 new File(fileName).writeAsStringSync(buffer.toString()); |
| 192 print("wrote: $fileName"); |
| 193 return fileName; |
| 194 } |
| 195 |
| 196 void writeln(x) => buffer.writeln(x); |
| 197 |
| 198 String classIdToName(int id) { |
| 199 if (id < 0) id = nbClasses + id; |
| 200 return "Class$id"; |
| 201 } |
| 202 |
| 203 /// [id] is per class. |
| 204 String methodIdToName(int id, int classId) { |
| 205 if (sameMethodNames) return "method$id"; |
| 206 return "method${classId}_$id"; |
| 207 } |
| 208 |
| 209 // Must work for Dart and JS. |
| 210 void emitMethodBody(int methodId, int classId) { |
| 211 writeln("{"); |
| 212 if (shouldPrintInMethod) { |
| 213 writeln("print('class: $classId, method: $methodId');"); |
| 214 } |
| 215 if (nbWhileLoopsInMethod > 0) { |
| 216 writeln("var sum = 0;"); |
| 217 for (int i = 0; i < nbWhileLoopsInMethod; i++) { |
| 218 writeln("for (var i = 0; i < $argumentName; i++) {"); |
| 219 writeln(" sum++;"); |
| 220 writeln("}"); |
| 221 } |
| 222 writeln("return sum;"); |
| 223 } |
| 224 writeln("}"); |
| 225 } |
| 226 |
| 227 // Must work for Dart and JS. |
| 228 void emitCallOtherMethodsBody(List<int> methodIds, int classId) { |
| 229 writeln("{"); |
| 230 writeln("var sum = 0;"); |
| 231 for (int methodId in methodIds) { |
| 232 String methodName = methodIdToName(methodId, classId); |
| 233 writeln("sum += this.$methodName($argumentName);"); |
| 234 } |
| 235 writeln("return sum;"); |
| 236 writeln("}"); |
| 237 } |
| 238 |
| 239 // Must work for Dart and JS. |
| 240 void emitInstantiatePrevious(int classId) { |
| 241 writeln("{"); |
| 242 if (classId == 0) { |
| 243 writeln("return null;"); |
| 244 } else { |
| 245 String previousClass = classIdToName(classId - 1); |
| 246 writeln("return new $previousClass();"); |
| 247 } |
| 248 writeln("}"); |
| 249 } |
| 250 |
| 251 /// Should write the class using [writeln]. |
| 252 void emitClasses(); |
| 253 |
| 254 String get description; |
| 255 String get fileExtension; |
| 256 } |
| 257 |
| 258 abstract class JavaScriptClassGenerator extends ClassGenerator { |
| 259 bool get wrapProgram => config.shouldWrapProgram; |
| 260 final String methodsObjectName = "methods"; |
| 261 |
| 262 JavaScriptClassGenerator(Config config) : super(config); |
| 263 |
| 264 Future<String> generateRawJs(String filePrefix) => |
| 265 new Future.value(writeFile(filePrefix)); |
| 266 |
| 267 void emitUtilityFunctions(); |
| 268 void emitClass(int classId, int superclassId); |
| 269 |
| 270 void emitClasses() { |
| 271 if (wrapProgram) writeln("(function() {"); |
| 272 writeln("var $methodsObjectName;"); |
| 273 emitUtilityFunctions(); |
| 274 for (int i = 0; i < nbClasses; i++) { |
| 275 int superclassId = shareCommonSuperclass && i != 0 ? 0 : null; |
| 276 emitClass(i, superclassId); |
| 277 } |
| 278 |
| 279 if (fakeInstantiateClass != null) { |
| 280 String className = classIdToName(fakeInstantiateClass); |
| 281 writeln(""" |
| 282 if (new Date() == 42) { |
| 283 var o = new $className(); |
| 284 do { |
| 285 o.$callOtherMethodsName(99); |
| 286 o = o.$instantiatePreviousMethodName(); |
| 287 } while(o != null); |
| 288 }"""); |
| 289 } |
| 290 if (instantiateClassesPercent != null) { |
| 291 int targetClassId = ((nbClasses - 1) * instantiateClassesPercent) ~/ 100; |
| 292 String targetClassName = classIdToName(targetClassId); |
| 293 writeln(""" |
| 294 var o = new $targetClassName(); |
| 295 do { |
| 296 o = o.$instantiatePreviousMethodName(); |
| 297 } while(o != null); |
| 298 """); |
| 299 } |
| 300 if (wrapProgram) writeln("})();"); |
| 301 } |
| 302 |
| 303 String get fileExtension => "js"; |
| 304 } |
| 305 |
| 306 enum PrototypeApproach { |
| 307 tmpFunction, |
| 308 internalProto, |
| 309 objectCreate |
| 310 } |
| 311 class PlainJavaScriptClassGenerator extends JavaScriptClassGenerator { |
| 312 final PrototypeApproach prototypeApproach; |
| 313 final bool shouldInlineInherit; |
| 314 final bool useMethodsObject; |
| 315 |
| 316 PlainJavaScriptClassGenerator(Config config, |
| 317 {this.prototypeApproach, |
| 318 this.shouldInlineInherit, |
| 319 this.useMethodsObject}) |
| 320 : super(config) { |
| 321 if (prototypeApproach == null) { |
| 322 throw "Must provide prototype approach"; |
| 323 } |
| 324 if (shouldInlineInherit == null) { |
| 325 throw "Must provide inlining approach"; |
| 326 } |
| 327 if (useMethodsObject == null) { |
| 328 throw "Must provide object-proto approach"; |
| 329 } |
| 330 if (shouldInlineInherit && |
| 331 prototypeApproach == PrototypeApproach.tmpFunction) { |
| 332 throw "Can't inline tmp-function approach"; |
| 333 } |
| 334 } |
| 335 |
| 336 void emitInherit(cls, superclassId) { |
| 337 if (superclassId == null && !useMethodsObject) return; |
| 338 String sup = (superclassId == null) ? "null" : classIdToName(superclassId); |
| 339 if (!shouldInlineInherit) { |
| 340 if (useMethodsObject) { |
| 341 writeln("inherit($cls, $sup, $methodsObjectName);"); |
| 342 } else { |
| 343 writeln("inherit($cls, $sup);"); |
| 344 } |
| 345 return; |
| 346 } |
| 347 switch (prototypeApproach) { |
| 348 case PrototypeApproach.tmpFunction: |
| 349 throw "Should not happen"; |
| 350 break; |
| 351 case PrototypeApproach.internalProto: |
| 352 if (useMethodsObject) { |
| 353 writeln("$cls.prototype = $methodsObjectName;"); |
| 354 } |
| 355 if (superclassId != null) { |
| 356 writeln("$cls.prototype.__proto__ = $sup.prototype;"); |
| 357 } |
| 358 break; |
| 359 case PrototypeApproach.objectCreate: |
| 360 if (useMethodsObject) { |
| 361 if (superclassId == null) { |
| 362 writeln("$cls.prototype = $methodsObjectName;"); |
| 363 } else { |
| 364 writeln("$cls.prototype = Object.create($sup.prototype);"); |
| 365 writeln("copyProperties($methodsObjectName, $cls.prototype);"); |
| 366 } |
| 367 } else { |
| 368 writeln("$cls.prototype = Object.create($sup.prototype);"); |
| 369 } |
| 370 break; |
| 371 } |
| 372 } |
| 373 |
| 374 void emitUtilityFunctions() { |
| 375 switch (prototypeApproach) { |
| 376 case PrototypeApproach.internalProto: |
| 377 if (useMethodsObject) { |
| 378 writeln(''' |
| 379 function inherit(cls, sup, methods) { |
| 380 cls.prototype = methods; |
| 381 if (sup != null) { |
| 382 cls.prototype.__proto__ = sup.prototype; |
| 383 } |
| 384 } |
| 385 '''); |
| 386 |
| 387 } else { |
| 388 writeln(''' |
| 389 function inherit(cls, sup) { |
| 390 cls.prototype.__proto__ = sup.prototype; |
| 391 } |
| 392 '''); |
| 393 } |
| 394 break; |
| 395 case PrototypeApproach.tmpFunction: |
| 396 if (useMethodsObject) { |
| 397 writeln(''' |
| 398 function inherit(cls, sup, methods) { |
| 399 if (sup != null) { |
| 400 function tmp() {} |
| 401 tmp.prototype = sup.prototype; |
| 402 var proto = new tmp(); |
| 403 proto.constructor = cls; |
| 404 cls.prototype = proto; |
| 405 } |
| 406 copyProperties(methods, cls.prototype); |
| 407 }'''); |
| 408 } else { |
| 409 writeln(''' |
| 410 function inherit(cls, sup) { |
| 411 function tmp() {} |
| 412 tmp.prototype = sup.prototype; |
| 413 var proto = new tmp(); |
| 414 proto.constructor = cls; |
| 415 cls.prototype = proto; |
| 416 }'''); |
| 417 } |
| 418 break; |
| 419 case PrototypeApproach.objectCreate: |
| 420 if (useMethodsObject) { |
| 421 writeln(''' |
| 422 function inherit(cls, sup, methods) { |
| 423 if (sup == null) { |
| 424 cls.prototype = methods; |
| 425 } else { |
| 426 cls.prototype = Object.create(sup.prototype); |
| 427 copyProperties(methods, cls.prototype); |
| 428 } |
| 429 } |
| 430 '''); |
| 431 } else { |
| 432 writeln(''' |
| 433 function inherit(cls, sup) { |
| 434 cls.prototype = Object.create(sup.prototype); |
| 435 } |
| 436 '''); |
| 437 } |
| 438 break; |
| 439 } |
| 440 writeln(""" |
| 441 function copyProperties(from, to) { |
| 442 var props = Object.keys(from); |
| 443 for (var i = 0; i < props.length; i++) { |
| 444 var p = props[i]; |
| 445 to[p] = from[p]; |
| 446 } |
| 447 }"""); |
| 448 } |
| 449 |
| 450 void emitMethod(int classId, String methodName, Function bodyEmitter, |
| 451 {bool emitArgument: true}) { |
| 452 String argumentString = emitArgument ? argumentName : ""; |
| 453 if (useMethodsObject) { |
| 454 writeln("$methodName: function($argumentString)"); |
| 455 bodyEmitter(); |
| 456 writeln(","); |
| 457 } else { |
| 458 String className = classIdToName(classId); |
| 459 String proto = "$className.prototype"; |
| 460 writeln("$proto.$methodName = function($argumentString)"); |
| 461 bodyEmitter(); |
| 462 } |
| 463 } |
| 464 |
| 465 /// Returns the methods object, if we use an object. |
| 466 void emitMethods(int classId) { |
| 467 List<int> methodIds = []; |
| 468 int nbGenericMethods = nbMethodsPerClass; |
| 469 if (useMethodsObject) { |
| 470 writeln("$methodsObjectName = {"); |
| 471 } |
| 472 if (shouldEmitCallAllMethods) nbGenericMethods--; |
| 473 for (int j = 0; j < nbGenericMethods; j++) { |
| 474 String methodName = methodIdToName(j, classId); |
| 475 emitMethod(classId, methodName, () => emitMethodBody(j, classId)); |
| 476 methodIds.add(j); |
| 477 } |
| 478 if (shouldEmitCallAllMethods) { |
| 479 emitMethod(classId, |
| 480 callOtherMethodsName, |
| 481 () => emitCallOtherMethodsBody(methodIds, classId)); |
| 482 } |
| 483 if (shouldEmitInstantiatePreviousMethod) { |
| 484 emitMethod(classId, |
| 485 instantiatePreviousMethodName, |
| 486 () => emitInstantiatePrevious(classId), |
| 487 emitArgument: false); |
| 488 } |
| 489 if (useMethodsObject) { |
| 490 writeln("};"); |
| 491 } |
| 492 } |
| 493 |
| 494 void emitClass(int classId, int superclassId) { |
| 495 String className = classIdToName(classId); |
| 496 writeln("function $className() {}"); |
| 497 switch (prototypeApproach) { |
| 498 case PrototypeApproach.objectCreate: |
| 499 if (useMethodsObject) { |
| 500 emitMethods(classId); |
| 501 emitInherit(className, superclassId); |
| 502 } else { |
| 503 emitInherit(className, superclassId); |
| 504 emitMethods(classId); |
| 505 } |
| 506 break; |
| 507 case PrototypeApproach.tmpFunction: |
| 508 if (useMethodsObject) { |
| 509 emitMethods(classId); |
| 510 emitInherit(className, superclassId); |
| 511 } else { |
| 512 emitInherit(className, superclassId); |
| 513 emitMethods(classId); |
| 514 } |
| 515 break; |
| 516 case PrototypeApproach.internalProto: |
| 517 emitMethods(classId); |
| 518 emitInherit(className, superclassId); |
| 519 break; |
| 520 } |
| 521 } |
| 522 |
| 523 String get description { |
| 524 String protoApproachDescription; |
| 525 switch (prototypeApproach) { |
| 526 case PrototypeApproach.objectCreate: |
| 527 protoApproachDescription = "objectCreate"; |
| 528 break; |
| 529 case PrototypeApproach.tmpFunction: |
| 530 protoApproachDescription = "tmpFunction"; |
| 531 break; |
| 532 case PrototypeApproach.internalProto: |
| 533 protoApproachDescription = "internalProto"; |
| 534 break; |
| 535 } |
| 536 String inline = shouldInlineInherit ? "inl" : "noInl"; |
| 537 String objectProto = useMethodsObject ? "obj" : "noObj"; |
| 538 return "plain_${protoApproachDescription}_${inline}_$objectProto"; |
| 539 } |
| 540 } |
| 541 |
| 542 class Es6ClassGenerator extends JavaScriptClassGenerator { |
| 543 Es6ClassGenerator(Config config) : super(config); |
| 544 |
| 545 void emitUtilityFunctions() {} |
| 546 |
| 547 void emitClass(int classId, int superclassId) { |
| 548 String className = classIdToName(classId); |
| 549 if (superclassId != null) { |
| 550 String superclassName = classIdToName(superclassId); |
| 551 buffer.writeln("class $className extends $superclassName {"); |
| 552 } else { |
| 553 buffer.writeln("class $className {"); |
| 554 } |
| 555 List<int> methodIds = []; |
| 556 int nbGenericMethods = nbMethodsPerClass; |
| 557 if (shouldEmitCallAllMethods) nbGenericMethods--; |
| 558 for (int j = 0; j < nbGenericMethods; j++) { |
| 559 String methodName = methodIdToName(j, classId); |
| 560 writeln("$methodName($argumentName) "); |
| 561 emitMethodBody(j, classId); |
| 562 methodIds.add(j); |
| 563 } |
| 564 if (shouldEmitCallAllMethods) { |
| 565 writeln("$callOtherMethodsName($argumentName)"); |
| 566 emitCallOtherMethodsBody(methodIds, classId); |
| 567 } |
| 568 if (shouldEmitInstantiatePreviousMethod) { |
| 569 writeln("$instantiatePreviousMethodName()"); |
| 570 emitInstantiatePrevious(classId); |
| 571 } |
| 572 writeln("}"); |
| 573 } |
| 574 |
| 575 String get description => "es6"; |
| 576 } |
| 577 |
| 578 class DartClassGenerator extends ClassGenerator { |
| 579 final bool shouldUseNewEmitter; |
| 580 |
| 581 DartClassGenerator(Config config, {this.shouldUseNewEmitter: false}) |
| 582 : super(config); |
| 583 |
| 584 void emitClasses() { |
| 585 // TODO(flo): instantiateAndCallPrevious |
| 586 for (int i = 0; i < nbClasses; i++) { |
| 587 String className = classIdToName(i); |
| 588 writeln("class $className {"); |
| 589 List<int> methodIds = []; |
| 590 int nbGenericMethods = nbMethodsPerClass; |
| 591 if (shouldEmitCallAllMethods) nbGenericMethods--; |
| 592 for (int j = 0; j < nbGenericMethods; j++) { |
| 593 String methodName = methodIdToName(j, i); |
| 594 writeln("$methodName($argumentName)"); |
| 595 emitMethodBody(j, i); |
| 596 methodIds.add(j); |
| 597 } |
| 598 if (shouldEmitCallAllMethods) { |
| 599 writeln("$callOtherMethodsName($argumentName)"); |
| 600 emitCallOtherMethodsBody(methodIds, i); |
| 601 } |
| 602 if (shouldEmitInstantiatePreviousMethod) { |
| 603 writeln("$instantiatePreviousMethodName()"); |
| 604 emitInstantiatePrevious(i); |
| 605 } |
| 606 writeln("}"); |
| 607 } |
| 608 writeln("main() {"); |
| 609 if (fakeInstantiateClass != null) { |
| 610 String className = classIdToName(fakeInstantiateClass); |
| 611 writeln(""" |
| 612 if (new DateTime.now().millisecondsSinceEpoch == 42) { |
| 613 var o = new $className(); |
| 614 do { |
| 615 o.$callOtherMethodsName(99); |
| 616 o = o.$instantiatePreviousMethodName(); |
| 617 } while(o != null); |
| 618 }"""); |
| 619 } |
| 620 if (instantiateClassesPercent != null) { |
| 621 int targetClassId = ((nbClasses - 1) * instantiateClassesPercent) ~/ 100; |
| 622 String targetClassName = classIdToName(targetClassId); |
| 623 writeln(""" |
| 624 var o = new $targetClassName(); |
| 625 do { |
| 626 o = o.$instantiatePreviousMethodName(); |
| 627 } while(o != null); |
| 628 """); |
| 629 } |
| 630 writeln("}"); |
| 631 } |
| 632 |
| 633 Future<String> generateRawJs(String filePrefix) async { |
| 634 String dartFile = writeFile(filePrefix); |
| 635 String outFile = buildFileName(filePrefix, "js"); |
| 636 Map<String, String> env = {}; |
| 637 if (shouldUseNewEmitter) { |
| 638 env["DART_VM_OPTIONS"] = '-Ddart2js.use.new.emitter=true'; |
| 639 } |
| 640 print("compiling"); |
| 641 print("dart2jsPath: $dart2jsPath"); |
| 642 ProcessResult result = |
| 643 await Process.run(dart2jsPath, [dartFile, "--out=$outFile"], |
| 644 environment: env); |
| 645 if (result.exitCode != 0) { |
| 646 print("compilation failed"); |
| 647 print(result.stdout); |
| 648 print(result.stderr); |
| 649 return null; |
| 650 } |
| 651 print("compilation done"); |
| 652 return outFile; |
| 653 } |
| 654 |
| 655 Future measureDart(String filePrefix, { bool useSnapshot: false }) async { |
| 656 String dartFile = writeFile(filePrefix); |
| 657 String command = Platform.executable; |
| 658 Stopwatch watch = new Stopwatch(); |
| 659 Directory dir = Directory.systemTemp.createTempSync('snapshot'); |
| 660 try { |
| 661 String measuring = dartFile; |
| 662 if (useSnapshot) { |
| 663 print("creating snapshot"); |
| 664 measuring = new File("${dir.path}/measuring.snapshot").path; |
| 665 ProcessResult result = |
| 666 await Process.run(command, ["--snapshot=$measuring", dartFile]); |
| 667 if (result.exitCode != 0) { |
| 668 print("snapshot creation failed"); |
| 669 print(result.stdout); |
| 670 print(result.stderr); |
| 671 return; |
| 672 } |
| 673 } |
| 674 List<String> args = [measuring]; |
| 675 print("Running: $command ${args.join(' ')}"); |
| 676 int nbRuns = 10; |
| 677 int sum = 0; |
| 678 for (int i = 0; i < nbRuns; i++) { |
| 679 watch.reset(); |
| 680 watch.start(); |
| 681 ProcessResult result = await Process.run(command, args); |
| 682 int elapsedMilliseconds = watch.elapsedMilliseconds; |
| 683 if (result.exitCode != 0) { |
| 684 print("run failed"); |
| 685 print(result.stdout); |
| 686 print(result.stderr); |
| 687 return; |
| 688 } |
| 689 print(" measured time (including VM startup): $elapsedMilliseconds"); |
| 690 sum += elapsedMilliseconds; |
| 691 } |
| 692 if (sum != 0) { |
| 693 print(" mean: ${sum ~/ nbRuns}"); |
| 694 } |
| 695 } finally { |
| 696 dir.deleteSync(recursive: true); |
| 697 } |
| 698 } |
| 699 |
| 700 String get fileExtension => "dart"; |
| 701 String get description { |
| 702 if (shouldUseNewEmitter) return "dartNew"; |
| 703 return "dart"; |
| 704 } |
| 705 } |
| 706 |
| 707 main(List<String> arguments) async { |
| 708 String filePrefix = arguments.length > 0 |
| 709 ? arguments.first |
| 710 : Directory.systemTemp.uri.resolve("classes").path; |
| 711 |
| 712 Config config = new Config( |
| 713 nbClasses: 2000, |
| 714 nbMethodsPerClass: 20, |
| 715 fakeInstantiateClass: -1, |
| 716 instantiateClassesPercent: 20, |
| 717 shareCommonSuperclass: true, |
| 718 sameMethodNames: true, |
| 719 shouldPrintInMethod: true, |
| 720 nbWhileLoopsInBody: 1, |
| 721 shouldWrapProgram: true, |
| 722 shouldEmitCallAllMethods: true, |
| 723 shouldEmitInstantiatePreviousMethod: true |
| 724 ); |
| 725 |
| 726 var plain = new PlainJavaScriptClassGenerator(config, |
| 727 prototypeApproach: PrototypeApproach.tmpFunction, |
| 728 useMethodsObject: false, |
| 729 shouldInlineInherit: false); |
| 730 var plainProto = new PlainJavaScriptClassGenerator(config, |
| 731 prototypeApproach: PrototypeApproach.internalProto, |
| 732 useMethodsObject: false, |
| 733 shouldInlineInherit: false); |
| 734 var plainObjectCreate = new PlainJavaScriptClassGenerator(config, |
| 735 prototypeApproach: PrototypeApproach.objectCreate, |
| 736 useMethodsObject: false, |
| 737 shouldInlineInherit: false); |
| 738 var plainProtoInline = new PlainJavaScriptClassGenerator(config, |
| 739 prototypeApproach: PrototypeApproach.internalProto, |
| 740 useMethodsObject: false, |
| 741 shouldInlineInherit: true); |
| 742 var plainObjectCreateInline = new PlainJavaScriptClassGenerator(config, |
| 743 prototypeApproach: PrototypeApproach.objectCreate, |
| 744 useMethodsObject: false, |
| 745 shouldInlineInherit: true); |
| 746 var plainObj = new PlainJavaScriptClassGenerator(config, |
| 747 prototypeApproach: PrototypeApproach.tmpFunction, |
| 748 useMethodsObject: true, |
| 749 shouldInlineInherit: false); |
| 750 var plainProtoObj = new PlainJavaScriptClassGenerator(config, |
| 751 prototypeApproach: PrototypeApproach.internalProto, |
| 752 useMethodsObject: true, |
| 753 shouldInlineInherit: false); |
| 754 var plainObjectCreateObj = new PlainJavaScriptClassGenerator(config, |
| 755 prototypeApproach: PrototypeApproach.objectCreate, |
| 756 useMethodsObject: true, |
| 757 shouldInlineInherit: false); |
| 758 var plainProtoInlineObj = new PlainJavaScriptClassGenerator(config, |
| 759 prototypeApproach: PrototypeApproach.internalProto, |
| 760 useMethodsObject: true, |
| 761 shouldInlineInherit: true); |
| 762 var plainObjectCreateInlineObj = new PlainJavaScriptClassGenerator(config, |
| 763 prototypeApproach: PrototypeApproach.objectCreate, |
| 764 useMethodsObject: true, |
| 765 shouldInlineInherit: true); |
| 766 var es6 = new Es6ClassGenerator(config); |
| 767 var dart = new DartClassGenerator(config); |
| 768 var dartNew = new DartClassGenerator(config, shouldUseNewEmitter: true); |
| 769 |
| 770 await plain.measure(filePrefix); |
| 771 await plainProto.measure(filePrefix); |
| 772 await plainObjectCreate.measure(filePrefix); |
| 773 await plainProtoInline.measure(filePrefix); |
| 774 await plainObjectCreateInline.measure(filePrefix); |
| 775 await plainObj.measure(filePrefix); |
| 776 await plainProtoObj.measure(filePrefix); |
| 777 await plainObjectCreateObj.measure(filePrefix); |
| 778 await plainProtoInlineObj.measure(filePrefix); |
| 779 await plainObjectCreateInlineObj.measure(filePrefix); |
| 780 await es6.measure(filePrefix); |
| 781 await dartNew.measure(filePrefix); |
| 782 await dart.measure(filePrefix); |
| 783 await dart.measureDart(filePrefix); |
| 784 await dart.measureDart(filePrefix, useSnapshot: true); |
| 785 } |
OLD | NEW |