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