123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- #!/usr/bin/env python
- # Copyright 2016 the V8 project authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- # for py2/py3 compatibility
- from __future__ import print_function
- from collections import namedtuple
- import textwrap
- import sys
- SHARD_FILENAME_TEMPLATE = "test/mjsunit/compiler/inline-exception-{shard}.js"
- # Generates 2 files. Found by trial and error.
- SHARD_SIZE = 97
- PREAMBLE = """
- // Copyright 2016 the V8 project authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- // Flags: --allow-natives-syntax --no-always-opt
- // This test file was generated by tools/gen-inlining-tests.py .
- // Global variables
- var deopt = undefined; // either true or false
- var counter = 0;
- function resetState() {
- counter = 0;
- }
- function warmUp(f) {
- try {
- f();
- } catch (ex) {
- // ok
- }
- try {
- f();
- } catch (ex) {
- // ok
- }
- }
- function resetOptAndAssertResultEquals(expected, f) {
- warmUp(f);
- resetState();
- // %DebugPrint(f);
- eval("'dont optimize this function itself please, but do optimize f'");
- %OptimizeFunctionOnNextCall(f);
- assertEquals(expected, f());
- }
- function resetOptAndAssertThrowsWith(expected, f) {
- warmUp(f);
- resetState();
- // %DebugPrint(f);
- eval("'dont optimize this function itself please, but do optimize f'");
- %OptimizeFunctionOnNextCall(f);
- try {
- var result = f();
- fail("resetOptAndAssertThrowsWith",
- "exception: " + expected,
- "result: " + result);
- } catch (ex) {
- assertEquals(expected, ex);
- }
- }
- function increaseAndReturn15() {
- if (deopt) %DeoptimizeFunction(f);
- counter++;
- return 15;
- }
- function increaseAndThrow42() {
- if (deopt) %DeoptimizeFunction(f);
- counter++;
- throw 42;
- }
- function increaseAndReturn15_noopt_inner() {
- if (deopt) %DeoptimizeFunction(f);
- counter++;
- return 15;
- }
- %NeverOptimizeFunction(increaseAndReturn15_noopt_inner);
- function increaseAndThrow42_noopt_inner() {
- if (deopt) %DeoptimizeFunction(f);
- counter++;
- throw 42;
- }
- %NeverOptimizeFunction(increaseAndThrow42_noopt_inner);
- // Alternative 1
- function returnOrThrow(doReturn) {
- if (doReturn) {
- return increaseAndReturn15();
- } else {
- return increaseAndThrow42();
- }
- }
- // Alternative 2
- function increaseAndReturn15_calls_noopt() {
- return increaseAndReturn15_noopt_inner();
- }
- function increaseAndThrow42_calls_noopt() {
- return increaseAndThrow42_noopt_inner();
- }
- // Alternative 3.
- // When passed either {increaseAndReturn15} or {increaseAndThrow42}, it acts
- // as the other one.
- function invertFunctionCall(f) {
- var result;
- try {
- result = f();
- } catch (ex) {
- return ex - 27;
- }
- throw result + 27;
- }
- // Alternative 4: constructor
- function increaseAndStore15Constructor() {
- if (deopt) %DeoptimizeFunction(f);
- ++counter;
- this.x = 15;
- }
- function increaseAndThrow42Constructor() {
- if (deopt) %DeoptimizeFunction(f);
- ++counter;
- this.x = 42;
- throw this.x;
- }
- // Alternative 5: property
- var magic = {};
- Object.defineProperty(magic, 'prop', {
- get: function () {
- if (deopt) %DeoptimizeFunction(f);
- return 15 + 0 * ++counter;
- },
- set: function(x) {
- // argument should be 37
- if (deopt) %DeoptimizeFunction(f);
- counter -= 36 - x; // increments counter
- throw 42;
- }
- })
- // Generate type feedback.
- assertEquals(15, increaseAndReturn15_calls_noopt());
- assertThrowsEquals(function() { return increaseAndThrow42_noopt_inner() }, 42);
- assertEquals(15, (new increaseAndStore15Constructor()).x);
- assertThrowsEquals(function() {
- return (new increaseAndThrow42Constructor()).x;
- },
- 42);
- function runThisShard() {
- """.strip()
- def booltuples(n):
- """booltuples(2) yields 4 tuples: (False, False), (False, True),
- (True, False), (True, True)."""
- assert isinstance(n, int)
- if n <= 0:
- yield ()
- else:
- for initial in booltuples(n-1):
- yield initial + (False,)
- yield initial + (True,)
- def fnname(flags):
- assert len(FLAGLETTERS) == len(flags)
- return "f_" + ''.join(
- FLAGLETTERS[i] if b else '_'
- for (i, b) in enumerate(flags))
- NUM_TESTS_PRINTED = 0
- NUM_TESTS_IN_SHARD = 0
- def printtest(flags):
- """Print a test case. Takes a couple of boolean flags, on which the
- printed Javascript code depends."""
- assert all(isinstance(flag, bool) for flag in flags)
- # The alternative flags are in reverse order so that if we take all possible
- # tuples, ordered lexicographically from false to true, we get first the
- # default, then alternative 1, then 2, etc.
- (
- alternativeFn5, # use alternative #5 for returning/throwing:
- # return/throw using property
- alternativeFn4, # use alternative #4 for returning/throwing:
- # return/throw using constructor
- alternativeFn3, # use alternative #3 for returning/throwing:
- # return/throw indirectly, based on function argument
- alternativeFn2, # use alternative #2 for returning/throwing:
- # return/throw indirectly in unoptimized code,
- # no branching
- alternativeFn1, # use alternative #1 for returning/throwing:
- # return/throw indirectly, based on boolean arg
- tryThrows, # in try block, call throwing function
- tryReturns, # in try block, call returning function
- tryFirstReturns, # in try block, returning goes before throwing
- tryResultToLocal, # in try block, result goes to local variable
- doCatch, # include catch block
- catchReturns, # in catch block, return
- catchWithLocal, # in catch block, modify or return the local variable
- catchThrows, # in catch block, throw
- doFinally, # include finally block
- finallyReturns, # in finally block, return local variable
- finallyThrows, # in finally block, throw
- endReturnLocal, # at very end, return variable local
- deopt, # deopt inside inlined function
- ) = flags
- # BASIC RULES
- # Only one alternative can be applied at any time.
- if (alternativeFn1 + alternativeFn2 + alternativeFn3 + alternativeFn4
- + alternativeFn5 > 1):
- return
- # In try, return or throw, or both.
- if not (tryReturns or tryThrows): return
- # Either doCatch or doFinally.
- if not doCatch and not doFinally: return
- # Catch flags only make sense when catching
- if not doCatch and (catchReturns or catchWithLocal or catchThrows):
- return
- # Finally flags only make sense when finallying
- if not doFinally and (finallyReturns or finallyThrows):
- return
- # tryFirstReturns is only relevant when both tryReturns and tryThrows are
- # true.
- if tryFirstReturns and not (tryReturns and tryThrows): return
- # From the try and finally block, we can return or throw, but not both.
- if catchReturns and catchThrows: return
- if finallyReturns and finallyThrows: return
- # If at the end we return the local, we need to have touched it.
- if endReturnLocal and not (tryResultToLocal or catchWithLocal): return
- # PRUNING
- anyAlternative = any([alternativeFn1, alternativeFn2, alternativeFn3,
- alternativeFn4, alternativeFn5])
- specificAlternative = any([alternativeFn2, alternativeFn3])
- rareAlternative = not specificAlternative
- # If try returns and throws, then don't catchWithLocal, endReturnLocal, or
- # deopt, or do any alternative.
- if (tryReturns and tryThrows and
- (catchWithLocal or endReturnLocal or deopt or anyAlternative)):
- return
- # We don't do any alternative if we do a finally.
- if doFinally and anyAlternative: return
- # We only use the local variable if we do alternative #2 or #3.
- if ((tryResultToLocal or catchWithLocal or endReturnLocal) and
- not specificAlternative):
- return
- # We don't need to test deopting into a finally.
- if doFinally and deopt: return
- # We're only interested in alternative #2 if we have endReturnLocal, no
- # catchReturns, and no catchThrows, and deopt.
- if (alternativeFn2 and
- (not endReturnLocal or catchReturns or catchThrows or not deopt)):
- return
- # Flag check succeeded.
- trueFlagNames = [name for (name, value) in flags._asdict().items() if value]
- flagsMsgLine = " // Variant flags: [{}]".format(', '.join(trueFlagNames))
- write(textwrap.fill(flagsMsgLine, subsequent_indent=' // '))
- write("")
- if not anyAlternative:
- fragments = {
- 'increaseAndReturn15': 'increaseAndReturn15()',
- 'increaseAndThrow42': 'increaseAndThrow42()',
- }
- elif alternativeFn1:
- fragments = {
- 'increaseAndReturn15': 'returnOrThrow(true)',
- 'increaseAndThrow42': 'returnOrThrow(false)',
- }
- elif alternativeFn2:
- fragments = {
- 'increaseAndReturn15': 'increaseAndReturn15_calls_noopt()',
- 'increaseAndThrow42': 'increaseAndThrow42_calls_noopt()',
- }
- elif alternativeFn3:
- fragments = {
- 'increaseAndReturn15': 'invertFunctionCall(increaseAndThrow42)',
- 'increaseAndThrow42': 'invertFunctionCall(increaseAndReturn15)',
- }
- elif alternativeFn4:
- fragments = {
- 'increaseAndReturn15': '(new increaseAndStore15Constructor()).x',
- 'increaseAndThrow42': '(new increaseAndThrow42Constructor()).x',
- }
- else:
- assert alternativeFn5
- fragments = {
- 'increaseAndReturn15': 'magic.prop /* returns 15 */',
- 'increaseAndThrow42': '(magic.prop = 37 /* throws 42 */)',
- }
- # As we print code, we also maintain what the result should be. Variable
- # {result} can be one of three things:
- #
- # - None, indicating returning JS null
- # - ("return", n) with n an integer
- # - ("throw", n), with n an integer
- result = None
- # We also maintain what the counter should be at the end.
- # The counter is reset just before f is called.
- counter = 0
- write( " f = function {} () {{".format(fnname(flags)))
- write( " var local = 888;")
- write( " deopt = {};".format("true" if deopt else "false"))
- local = 888
- write( " try {")
- write( " counter++;")
- counter += 1
- resultTo = "local +=" if tryResultToLocal else "return"
- if tryReturns and not (tryThrows and not tryFirstReturns):
- write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments))
- if result == None:
- counter += 1
- if tryResultToLocal:
- local += 19
- else:
- result = ("return", 19)
- if tryThrows:
- write( " {} 4 + {increaseAndThrow42};".format(resultTo, **fragments))
- if result == None:
- counter += 1
- result = ("throw", 42)
- if tryReturns and tryThrows and not tryFirstReturns:
- write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments))
- if result == None:
- counter += 1
- if tryResultToLocal:
- local += 19
- else:
- result = ("return", 19)
- write( " counter++;")
- if result == None:
- counter += 1
- if doCatch:
- write( " } catch (ex) {")
- write( " counter++;")
- if isinstance(result, tuple) and result[0] == 'throw':
- counter += 1
- if catchThrows:
- write(" throw 2 + ex;")
- if isinstance(result, tuple) and result[0] == "throw":
- result = ('throw', 2 + result[1])
- elif catchReturns and catchWithLocal:
- write(" return 2 + local;")
- if isinstance(result, tuple) and result[0] == "throw":
- result = ('return', 2 + local)
- elif catchReturns and not catchWithLocal:
- write(" return 2 + ex;");
- if isinstance(result, tuple) and result[0] == "throw":
- result = ('return', 2 + result[1])
- elif catchWithLocal:
- write(" local += ex;");
- if isinstance(result, tuple) and result[0] == "throw":
- local += result[1]
- result = None
- counter += 1
- else:
- if isinstance(result, tuple) and result[0] == "throw":
- result = None
- counter += 1
- write( " counter++;")
- if doFinally:
- write( " } finally {")
- write( " counter++;")
- counter += 1
- if finallyThrows:
- write(" throw 25;")
- result = ('throw', 25)
- elif finallyReturns:
- write(" return 3 + local;")
- result = ('return', 3 + local)
- elif not finallyReturns and not finallyThrows:
- write(" local += 2;")
- local += 2
- counter += 1
- else: assert False # unreachable
- write( " counter++;")
- write( " }")
- write( " counter++;")
- if result == None:
- counter += 1
- if endReturnLocal:
- write( " return 5 + local;")
- if result == None:
- result = ('return', 5 + local)
- write( " }")
- if result == None:
- write( " resetOptAndAssertResultEquals(undefined, f);")
- else:
- tag, value = result
- if tag == "return":
- write( " resetOptAndAssertResultEquals({}, f);".format(value))
- else:
- assert tag == "throw"
- write( " resetOptAndAssertThrowsWith({}, f);".format(value))
- write( " assertEquals({}, counter);".format(counter))
- write( "")
- global NUM_TESTS_PRINTED, NUM_TESTS_IN_SHARD
- NUM_TESTS_PRINTED += 1
- NUM_TESTS_IN_SHARD += 1
- FILE = None # to be initialised to an open file
- SHARD_NUM = 1
- def write(*args):
- return print(*args, file=FILE)
- def rotateshard():
- global FILE, NUM_TESTS_IN_SHARD, SHARD_SIZE
- if MODE != 'shard':
- return
- if FILE != None and NUM_TESTS_IN_SHARD < SHARD_SIZE:
- return
- if FILE != None:
- finishshard()
- assert FILE == None
- FILE = open(SHARD_FILENAME_TEMPLATE.format(shard=SHARD_NUM), 'w')
- write_shard_header()
- NUM_TESTS_IN_SHARD = 0
- def finishshard():
- global FILE, SHARD_NUM, MODE
- assert FILE
- write_shard_footer()
- if MODE == 'shard':
- print("Wrote shard {}.".format(SHARD_NUM))
- FILE.close()
- FILE = None
- SHARD_NUM += 1
- def write_shard_header():
- if MODE == 'shard':
- write("// Shard {}.".format(SHARD_NUM))
- write("")
- write(PREAMBLE)
- write("")
- def write_shard_footer():
- write("}")
- write("%NeverOptimizeFunction(runThisShard);")
- write("")
- write("// {} tests in this shard.".format(NUM_TESTS_IN_SHARD))
- write("// {} tests up to here.".format(NUM_TESTS_PRINTED))
- write("")
- write("runThisShard();")
- FLAGLETTERS="54321trflcrltfrtld"
- flagtuple = namedtuple('flagtuple', (
- "alternativeFn5",
- "alternativeFn4",
- "alternativeFn3",
- "alternativeFn2",
- "alternativeFn1",
- "tryThrows",
- "tryReturns",
- "tryFirstReturns",
- "tryResultToLocal",
- "doCatch",
- "catchReturns",
- "catchWithLocal",
- "catchThrows",
- "doFinally",
- "finallyReturns",
- "finallyThrows",
- "endReturnLocal",
- "deopt"
- ))
- emptyflags = flagtuple(*((False,) * len(flagtuple._fields)))
- f1 = emptyflags._replace(tryReturns=True, doCatch=True)
- # You can test function printtest with f1.
- allFlagCombinations = [
- flagtuple(*bools)
- for bools in booltuples(len(flagtuple._fields))
- ]
- if __name__ == '__main__':
- global MODE
- if sys.argv[1:] == []:
- MODE = 'stdout'
- print("// Printing all shards together to stdout.")
- print("")
- write_shard_header()
- FILE = sys.stdout
- elif sys.argv[1:] == ['--shard-and-overwrite']:
- MODE = 'shard'
- else:
- print("Usage:")
- print("")
- print(" python {}".format(sys.argv[0]))
- print(" print all tests to standard output")
- print(" python {} --shard-and-overwrite".format(sys.argv[0]))
- print(" print all tests to {}".format(SHARD_FILENAME_TEMPLATE))
- print("")
- print(sys.argv[1:])
- print("")
- sys.exit(1)
- rotateshard()
- for flags in allFlagCombinations:
- printtest(flags)
- rotateshard()
- finishshard()
- if MODE == 'shard':
- print("Total: {} tests.".format(NUM_TESTS_PRINTED))
|