1#!/usr/bin/env python 2 3# Copyright 2016 the V8 project authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7# for py2/py3 compatibility 8from __future__ import print_function 9 10from collections import namedtuple 11import textwrap 12import sys 13 14SHARD_FILENAME_TEMPLATE = "test/mjsunit/compiler/inline-exception-{shard}.js" 15# Generates 2 files. Found by trial and error. 16SHARD_SIZE = 97 17 18PREAMBLE = """ 19 20// Copyright 2016 the V8 project authors. All rights reserved. 21// Use of this source code is governed by a BSD-style license that can be 22// found in the LICENSE file. 23 24// Flags: --allow-natives-syntax --no-always-opt 25 26// This test file was generated by tools/gen-inlining-tests.py . 27 28// Global variables 29var deopt = undefined; // either true or false 30var counter = 0; 31 32function resetState() { 33 counter = 0; 34} 35 36function warmUp(f) { 37 try { 38 f(); 39 } catch (ex) { 40 // ok 41 } 42 try { 43 f(); 44 } catch (ex) { 45 // ok 46 } 47} 48 49function resetOptAndAssertResultEquals(expected, f) { 50 warmUp(f); 51 resetState(); 52 // %DebugPrint(f); 53 eval("'dont optimize this function itself please, but do optimize f'"); 54 %OptimizeFunctionOnNextCall(f); 55 assertEquals(expected, f()); 56} 57 58function resetOptAndAssertThrowsWith(expected, f) { 59 warmUp(f); 60 resetState(); 61 // %DebugPrint(f); 62 eval("'dont optimize this function itself please, but do optimize f'"); 63 %OptimizeFunctionOnNextCall(f); 64 try { 65 var result = f(); 66 fail("resetOptAndAssertThrowsWith", 67 "exception: " + expected, 68 "result: " + result); 69 } catch (ex) { 70 assertEquals(expected, ex); 71 } 72} 73 74function increaseAndReturn15() { 75 if (deopt) %DeoptimizeFunction(f); 76 counter++; 77 return 15; 78} 79 80function increaseAndThrow42() { 81 if (deopt) %DeoptimizeFunction(f); 82 counter++; 83 throw 42; 84} 85 86function increaseAndReturn15_noopt_inner() { 87 if (deopt) %DeoptimizeFunction(f); 88 counter++; 89 return 15; 90} 91 92%NeverOptimizeFunction(increaseAndReturn15_noopt_inner); 93 94function increaseAndThrow42_noopt_inner() { 95 if (deopt) %DeoptimizeFunction(f); 96 counter++; 97 throw 42; 98} 99 100%NeverOptimizeFunction(increaseAndThrow42_noopt_inner); 101 102// Alternative 1 103 104function returnOrThrow(doReturn) { 105 if (doReturn) { 106 return increaseAndReturn15(); 107 } else { 108 return increaseAndThrow42(); 109 } 110} 111 112// Alternative 2 113 114function increaseAndReturn15_calls_noopt() { 115 return increaseAndReturn15_noopt_inner(); 116} 117 118function increaseAndThrow42_calls_noopt() { 119 return increaseAndThrow42_noopt_inner(); 120} 121 122// Alternative 3. 123// When passed either {increaseAndReturn15} or {increaseAndThrow42}, it acts 124// as the other one. 125function invertFunctionCall(f) { 126 var result; 127 try { 128 result = f(); 129 } catch (ex) { 130 return ex - 27; 131 } 132 throw result + 27; 133} 134 135// Alternative 4: constructor 136function increaseAndStore15Constructor() { 137 if (deopt) %DeoptimizeFunction(f); 138 ++counter; 139 this.x = 15; 140} 141 142function increaseAndThrow42Constructor() { 143 if (deopt) %DeoptimizeFunction(f); 144 ++counter; 145 this.x = 42; 146 throw this.x; 147} 148 149// Alternative 5: property 150var magic = {}; 151Object.defineProperty(magic, 'prop', { 152 get: function () { 153 if (deopt) %DeoptimizeFunction(f); 154 return 15 + 0 * ++counter; 155 }, 156 157 set: function(x) { 158 // argument should be 37 159 if (deopt) %DeoptimizeFunction(f); 160 counter -= 36 - x; // increments counter 161 throw 42; 162 } 163}) 164 165// Generate type feedback. 166 167assertEquals(15, increaseAndReturn15_calls_noopt()); 168assertThrowsEquals(function() { return increaseAndThrow42_noopt_inner() }, 42); 169 170assertEquals(15, (new increaseAndStore15Constructor()).x); 171assertThrowsEquals(function() { 172 return (new increaseAndThrow42Constructor()).x; 173 }, 174 42); 175 176function runThisShard() { 177 178""".strip() 179 180def booltuples(n): 181 """booltuples(2) yields 4 tuples: (False, False), (False, True), 182 (True, False), (True, True).""" 183 184 assert isinstance(n, int) 185 if n <= 0: 186 yield () 187 else: 188 for initial in booltuples(n-1): 189 yield initial + (False,) 190 yield initial + (True,) 191 192def fnname(flags): 193 assert len(FLAGLETTERS) == len(flags) 194 195 return "f_" + ''.join( 196 FLAGLETTERS[i] if b else '_' 197 for (i, b) in enumerate(flags)) 198 199NUM_TESTS_PRINTED = 0 200NUM_TESTS_IN_SHARD = 0 201 202def printtest(flags): 203 """Print a test case. Takes a couple of boolean flags, on which the 204 printed Javascript code depends.""" 205 206 assert all(isinstance(flag, bool) for flag in flags) 207 208 # The alternative flags are in reverse order so that if we take all possible 209 # tuples, ordered lexicographically from false to true, we get first the 210 # default, then alternative 1, then 2, etc. 211 ( 212 alternativeFn5, # use alternative #5 for returning/throwing: 213 # return/throw using property 214 alternativeFn4, # use alternative #4 for returning/throwing: 215 # return/throw using constructor 216 alternativeFn3, # use alternative #3 for returning/throwing: 217 # return/throw indirectly, based on function argument 218 alternativeFn2, # use alternative #2 for returning/throwing: 219 # return/throw indirectly in unoptimized code, 220 # no branching 221 alternativeFn1, # use alternative #1 for returning/throwing: 222 # return/throw indirectly, based on boolean arg 223 tryThrows, # in try block, call throwing function 224 tryReturns, # in try block, call returning function 225 tryFirstReturns, # in try block, returning goes before throwing 226 tryResultToLocal, # in try block, result goes to local variable 227 doCatch, # include catch block 228 catchReturns, # in catch block, return 229 catchWithLocal, # in catch block, modify or return the local variable 230 catchThrows, # in catch block, throw 231 doFinally, # include finally block 232 finallyReturns, # in finally block, return local variable 233 finallyThrows, # in finally block, throw 234 endReturnLocal, # at very end, return variable local 235 deopt, # deopt inside inlined function 236 ) = flags 237 238 # BASIC RULES 239 240 # Only one alternative can be applied at any time. 241 if (alternativeFn1 + alternativeFn2 + alternativeFn3 + alternativeFn4 242 + alternativeFn5 > 1): 243 return 244 245 # In try, return or throw, or both. 246 if not (tryReturns or tryThrows): return 247 248 # Either doCatch or doFinally. 249 if not doCatch and not doFinally: return 250 251 # Catch flags only make sense when catching 252 if not doCatch and (catchReturns or catchWithLocal or catchThrows): 253 return 254 255 # Finally flags only make sense when finallying 256 if not doFinally and (finallyReturns or finallyThrows): 257 return 258 259 # tryFirstReturns is only relevant when both tryReturns and tryThrows are 260 # true. 261 if tryFirstReturns and not (tryReturns and tryThrows): return 262 263 # From the try and finally block, we can return or throw, but not both. 264 if catchReturns and catchThrows: return 265 if finallyReturns and finallyThrows: return 266 267 # If at the end we return the local, we need to have touched it. 268 if endReturnLocal and not (tryResultToLocal or catchWithLocal): return 269 270 # PRUNING 271 272 anyAlternative = any([alternativeFn1, alternativeFn2, alternativeFn3, 273 alternativeFn4, alternativeFn5]) 274 specificAlternative = any([alternativeFn2, alternativeFn3]) 275 rareAlternative = not specificAlternative 276 277 # If try returns and throws, then don't catchWithLocal, endReturnLocal, or 278 # deopt, or do any alternative. 279 if (tryReturns and tryThrows and 280 (catchWithLocal or endReturnLocal or deopt or anyAlternative)): 281 return 282 # We don't do any alternative if we do a finally. 283 if doFinally and anyAlternative: return 284 # We only use the local variable if we do alternative #2 or #3. 285 if ((tryResultToLocal or catchWithLocal or endReturnLocal) and 286 not specificAlternative): 287 return 288 # We don't need to test deopting into a finally. 289 if doFinally and deopt: return 290 291 # We're only interested in alternative #2 if we have endReturnLocal, no 292 # catchReturns, and no catchThrows, and deopt. 293 if (alternativeFn2 and 294 (not endReturnLocal or catchReturns or catchThrows or not deopt)): 295 return 296 297 298 # Flag check succeeded. 299 300 trueFlagNames = [name for (name, value) in flags._asdict().items() if value] 301 flagsMsgLine = " // Variant flags: [{}]".format(', '.join(trueFlagNames)) 302 write(textwrap.fill(flagsMsgLine, subsequent_indent=' // ')) 303 write("") 304 305 if not anyAlternative: 306 fragments = { 307 'increaseAndReturn15': 'increaseAndReturn15()', 308 'increaseAndThrow42': 'increaseAndThrow42()', 309 } 310 elif alternativeFn1: 311 fragments = { 312 'increaseAndReturn15': 'returnOrThrow(true)', 313 'increaseAndThrow42': 'returnOrThrow(false)', 314 } 315 elif alternativeFn2: 316 fragments = { 317 'increaseAndReturn15': 'increaseAndReturn15_calls_noopt()', 318 'increaseAndThrow42': 'increaseAndThrow42_calls_noopt()', 319 } 320 elif alternativeFn3: 321 fragments = { 322 'increaseAndReturn15': 'invertFunctionCall(increaseAndThrow42)', 323 'increaseAndThrow42': 'invertFunctionCall(increaseAndReturn15)', 324 } 325 elif alternativeFn4: 326 fragments = { 327 'increaseAndReturn15': '(new increaseAndStore15Constructor()).x', 328 'increaseAndThrow42': '(new increaseAndThrow42Constructor()).x', 329 } 330 else: 331 assert alternativeFn5 332 fragments = { 333 'increaseAndReturn15': 'magic.prop /* returns 15 */', 334 'increaseAndThrow42': '(magic.prop = 37 /* throws 42 */)', 335 } 336 337 # As we print code, we also maintain what the result should be. Variable 338 # {result} can be one of three things: 339 # 340 # - None, indicating returning JS null 341 # - ("return", n) with n an integer 342 # - ("throw", n), with n an integer 343 344 result = None 345 # We also maintain what the counter should be at the end. 346 # The counter is reset just before f is called. 347 counter = 0 348 349 write( " f = function {} () {{".format(fnname(flags))) 350 write( " var local = 888;") 351 write( " deopt = {};".format("true" if deopt else "false")) 352 local = 888 353 write( " try {") 354 write( " counter++;") 355 counter += 1 356 resultTo = "local +=" if tryResultToLocal else "return" 357 if tryReturns and not (tryThrows and not tryFirstReturns): 358 write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments)) 359 if result == None: 360 counter += 1 361 if tryResultToLocal: 362 local += 19 363 else: 364 result = ("return", 19) 365 if tryThrows: 366 write( " {} 4 + {increaseAndThrow42};".format(resultTo, **fragments)) 367 if result == None: 368 counter += 1 369 result = ("throw", 42) 370 if tryReturns and tryThrows and not tryFirstReturns: 371 write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments)) 372 if result == None: 373 counter += 1 374 if tryResultToLocal: 375 local += 19 376 else: 377 result = ("return", 19) 378 write( " counter++;") 379 if result == None: 380 counter += 1 381 382 if doCatch: 383 write( " } catch (ex) {") 384 write( " counter++;") 385 if isinstance(result, tuple) and result[0] == 'throw': 386 counter += 1 387 if catchThrows: 388 write(" throw 2 + ex;") 389 if isinstance(result, tuple) and result[0] == "throw": 390 result = ('throw', 2 + result[1]) 391 elif catchReturns and catchWithLocal: 392 write(" return 2 + local;") 393 if isinstance(result, tuple) and result[0] == "throw": 394 result = ('return', 2 + local) 395 elif catchReturns and not catchWithLocal: 396 write(" return 2 + ex;"); 397 if isinstance(result, tuple) and result[0] == "throw": 398 result = ('return', 2 + result[1]) 399 elif catchWithLocal: 400 write(" local += ex;"); 401 if isinstance(result, tuple) and result[0] == "throw": 402 local += result[1] 403 result = None 404 counter += 1 405 else: 406 if isinstance(result, tuple) and result[0] == "throw": 407 result = None 408 counter += 1 409 write( " counter++;") 410 411 if doFinally: 412 write( " } finally {") 413 write( " counter++;") 414 counter += 1 415 if finallyThrows: 416 write(" throw 25;") 417 result = ('throw', 25) 418 elif finallyReturns: 419 write(" return 3 + local;") 420 result = ('return', 3 + local) 421 elif not finallyReturns and not finallyThrows: 422 write(" local += 2;") 423 local += 2 424 counter += 1 425 else: assert False # unreachable 426 write( " counter++;") 427 428 write( " }") 429 write( " counter++;") 430 if result == None: 431 counter += 1 432 if endReturnLocal: 433 write( " return 5 + local;") 434 if result == None: 435 result = ('return', 5 + local) 436 write( " }") 437 438 if result == None: 439 write( " resetOptAndAssertResultEquals(undefined, f);") 440 else: 441 tag, value = result 442 if tag == "return": 443 write( " resetOptAndAssertResultEquals({}, f);".format(value)) 444 else: 445 assert tag == "throw" 446 write( " resetOptAndAssertThrowsWith({}, f);".format(value)) 447 448 write( " assertEquals({}, counter);".format(counter)) 449 write( "") 450 451 global NUM_TESTS_PRINTED, NUM_TESTS_IN_SHARD 452 NUM_TESTS_PRINTED += 1 453 NUM_TESTS_IN_SHARD += 1 454 455FILE = None # to be initialised to an open file 456SHARD_NUM = 1 457 458def write(*args): 459 return print(*args, file=FILE) 460 461 462 463def rotateshard(): 464 global FILE, NUM_TESTS_IN_SHARD, SHARD_SIZE 465 if MODE != 'shard': 466 return 467 if FILE != None and NUM_TESTS_IN_SHARD < SHARD_SIZE: 468 return 469 if FILE != None: 470 finishshard() 471 assert FILE == None 472 FILE = open(SHARD_FILENAME_TEMPLATE.format(shard=SHARD_NUM), 'w') 473 write_shard_header() 474 NUM_TESTS_IN_SHARD = 0 475 476def finishshard(): 477 global FILE, SHARD_NUM, MODE 478 assert FILE 479 write_shard_footer() 480 if MODE == 'shard': 481 print("Wrote shard {}.".format(SHARD_NUM)) 482 FILE.close() 483 FILE = None 484 SHARD_NUM += 1 485 486 487def write_shard_header(): 488 if MODE == 'shard': 489 write("// Shard {}.".format(SHARD_NUM)) 490 write("") 491 write(PREAMBLE) 492 write("") 493 494def write_shard_footer(): 495 write("}") 496 write("%NeverOptimizeFunction(runThisShard);") 497 write("") 498 write("// {} tests in this shard.".format(NUM_TESTS_IN_SHARD)) 499 write("// {} tests up to here.".format(NUM_TESTS_PRINTED)) 500 write("") 501 write("runThisShard();") 502 503FLAGLETTERS="54321trflcrltfrtld" 504 505flagtuple = namedtuple('flagtuple', ( 506 "alternativeFn5", 507 "alternativeFn4", 508 "alternativeFn3", 509 "alternativeFn2", 510 "alternativeFn1", 511 "tryThrows", 512 "tryReturns", 513 "tryFirstReturns", 514 "tryResultToLocal", 515 "doCatch", 516 "catchReturns", 517 "catchWithLocal", 518 "catchThrows", 519 "doFinally", 520 "finallyReturns", 521 "finallyThrows", 522 "endReturnLocal", 523 "deopt" 524 )) 525 526emptyflags = flagtuple(*((False,) * len(flagtuple._fields))) 527f1 = emptyflags._replace(tryReturns=True, doCatch=True) 528 529# You can test function printtest with f1. 530 531allFlagCombinations = [ 532 flagtuple(*bools) 533 for bools in booltuples(len(flagtuple._fields)) 534] 535 536if __name__ == '__main__': 537 global MODE 538 if sys.argv[1:] == []: 539 MODE = 'stdout' 540 print("// Printing all shards together to stdout.") 541 print("") 542 write_shard_header() 543 FILE = sys.stdout 544 elif sys.argv[1:] == ['--shard-and-overwrite']: 545 MODE = 'shard' 546 else: 547 print("Usage:") 548 print("") 549 print(" python {}".format(sys.argv[0])) 550 print(" print all tests to standard output") 551 print(" python {} --shard-and-overwrite".format(sys.argv[0])) 552 print(" print all tests to {}".format(SHARD_FILENAME_TEMPLATE)) 553 554 print("") 555 print(sys.argv[1:]) 556 print("") 557 sys.exit(1) 558 559 rotateshard() 560 561 for flags in allFlagCombinations: 562 printtest(flags) 563 rotateshard() 564 565 finishshard() 566 567 if MODE == 'shard': 568 print("Total: {} tests.".format(NUM_TESTS_PRINTED)) 569