summaryrefslogtreecommitdiff
path: root/deps/v8/tools/bigint-tester.py
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/tools/bigint-tester.py')
-rwxr-xr-xdeps/v8/tools/bigint-tester.py347
1 files changed, 347 insertions, 0 deletions
diff --git a/deps/v8/tools/bigint-tester.py b/deps/v8/tools/bigint-tester.py
new file mode 100755
index 0000000000..0452a0d1db
--- /dev/null
+++ b/deps/v8/tools/bigint-tester.py
@@ -0,0 +1,347 @@
+#!/usr/bin/python
+# Copyright 2017 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.
+
+import argparse
+import math
+import multiprocessing
+import os
+import random
+import subprocess
+import sys
+import tempfile
+
+# Configuration.
+kChars = "0123456789abcdefghijklmnopqrstuvwxyz"
+kBase = 16
+kLineLength = 71 # A bit less than 80.
+kNumInputsGenerate = 20
+kNumInputsStress = 1000
+
+# Internally used sentinels.
+kNo = 0
+kYes = 1
+kRandom = 2
+
+TEST_HEADER = """\
+// Copyright 2017 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.
+
+// Generated by %s.
+
+// Flags: --harmony-bigint
+""" % sys.argv[0]
+
+TEST_BODY = """
+var error_count = 0;
+for (var i = 0; i < data.length; i++) {
+ var d = data[i];
+%s
+}
+if (error_count !== 0) {
+ print("Finished with " + error_count + " errors.")
+ quit(1);
+}"""
+
+def GenRandom(length, negative=kRandom):
+ if length == 0: return "0"
+ s = []
+ if negative == kYes or (negative == kRandom and (random.randint(0, 1) == 0)):
+ s.append("-") # 50% chance of negative.
+ s.append(kChars[random.randint(1, kBase - 1)]) # No leading zero.
+ for i in range(1, length):
+ s.append(kChars[random.randint(0, kBase - 1)])
+ return "".join(s)
+
+def Format(x, base):
+ original = x
+ negative = False
+ if x == 0: return "0"
+ if x < 0:
+ negative = True
+ x = -x
+ s = ""
+ while x > 0:
+ s = kChars[x % base] + s
+ x = x / base
+ if negative:
+ s = "-" + s
+ assert int(s, base) == original
+ return s
+
+class TestGenerator(object):
+ # Subclasses must implement these.
+ # Returns a JSON snippet defining inputs and expected output for one test.
+ def EmitOne(self): raise NotImplementedError
+ # Returns a snippet of JavaScript that will operate on a variable "d"
+ # whose content is defined by the result of a call to "EmitOne".
+ def EmitTestCore(self): raise NotImplementedError
+
+ def EmitHeader(self):
+ return TEST_HEADER
+
+ def EmitData(self, count):
+ s = []
+ for i in range(count):
+ s.append(self.EmitOne())
+ return "var data = [" + ", ".join(s) + "];"
+
+ def EmitTestBody(self):
+ return TEST_BODY % self.EmitTestCore()
+
+ def PrintTest(self, count):
+ print(self.EmitHeader())
+ print(self.EmitData(count))
+ print(self.EmitTestBody())
+
+ def RunTest(self, count, binary):
+ try:
+ fd, path = tempfile.mkstemp(suffix=".js", prefix="bigint-test-")
+ with open(path, "w") as f:
+ f.write(self.EmitData(count))
+ f.write(self.EmitTestBody())
+ return subprocess.call("%s --harmony-bigint %s" % (binary, path),
+ shell=True)
+ finally:
+ os.close(fd)
+ os.remove(path)
+
+class UnaryOp(TestGenerator):
+ # Subclasses must implement these two.
+ def GetOpString(self): raise NotImplementedError
+ def GenerateResult(self, x): raise NotImplementedError
+
+ # Subclasses may override this:
+ def GenerateInput(self):
+ return GenRandom(random.randint(0, kLineLength))
+
+ # Subclasses should not override anything below.
+ def EmitOne(self):
+ x_str = self.GenerateInput()
+ x_num = int(x_str, kBase)
+ result_num = self.GenerateResult(x_num)
+ result_str = Format(result_num, kBase)
+ return "{\n a: \"%s\",\n r: \"%s\"\n}" % (x_str, result_str)
+
+ def EmitTestCore(self):
+ return """\
+ var a = BigInt.parseInt(d.a, %(base)d);
+ var r = %(op)sa;
+ if (d.r !== r.toString(%(base)d)) {
+ print("Input: " + a.toString(%(base)d));
+ print("Result: " + r.toString(%(base)d));
+ print("Expected: " + d.r);
+ error_count++;
+ }""" % {"op": self.GetOpString(), "base": kBase}
+
+class BinaryOp(TestGenerator):
+ # Subclasses must implement these two.
+ def GetOpString(self): raise NotImplementedError
+ def GenerateResult(self, left, right): raise NotImplementedError
+
+ # Subclasses may override these:
+ def GenerateInputLengths(self):
+ return random.randint(0, kLineLength), random.randint(0, kLineLength)
+
+ def GenerateInputs(self):
+ left_length, right_length = self.GenerateInputLengths()
+ return GenRandom(left_length), GenRandom(right_length)
+
+ # Subclasses should not override anything below.
+ def EmitOne(self):
+ left_str, right_str = self.GenerateInputs()
+ left_num = int(left_str, kBase)
+ right_num = int(right_str, kBase)
+ result_num = self.GenerateResult(left_num, right_num)
+ result_str = Format(result_num, kBase)
+ return ("{\n a: \"%s\",\n b: \"%s\",\n r: \"%s\"\n}" %
+ (left_str, right_str, result_str))
+
+ def EmitTestCore(self):
+ return """\
+ var a = BigInt.parseInt(d.a, %(base)d);
+ var b = BigInt.parseInt(d.b, %(base)d);
+ var r = a %(op)s b;
+ if (d.r !== r.toString(%(base)d)) {
+ print("Input A: " + a.toString(%(base)d));
+ print("Input B: " + b.toString(%(base)d));
+ print("Result: " + r.toString(%(base)d));
+ print("Expected: " + d.r);
+ print("Op: %(op)s");
+ error_count++;
+ }""" % {"op": self.GetOpString(), "base": kBase}
+
+class Neg(UnaryOp):
+ def GetOpString(self): return "-"
+ def GenerateResult(self, x): return -x
+
+class BitNot(UnaryOp):
+ def GetOpString(self): return "~"
+ def GenerateResult(self, x): return ~x
+
+class Inc(UnaryOp):
+ def GetOpString(self): return "++"
+ def GenerateResult(self, x): return x + 1
+
+class Dec(UnaryOp):
+ def GetOpString(self): return "--"
+ def GenerateResult(self, x): return x - 1
+
+class Add(BinaryOp):
+ def GetOpString(self): return "+"
+ def GenerateResult(self, a, b): return a + b
+
+class Sub(BinaryOp):
+ def GetOpString(self): return "-"
+ def GenerateResult(self, a, b): return a - b
+
+class Mul(BinaryOp):
+ def GetOpString(self): return "*"
+ def GenerateResult(self, a, b): return a * b
+ def GenerateInputLengths(self):
+ left_length = random.randint(1, kLineLength)
+ return left_length, kLineLength - left_length
+
+class Div(BinaryOp):
+ def GetOpString(self): return "/"
+ def GenerateResult(self, a, b):
+ result = abs(a) / abs(b)
+ if (a < 0) != (b < 0): result = -result
+ return result
+ def GenerateInputLengths(self):
+ # Let the left side be longer than the right side with high probability,
+ # because that case is more interesting.
+ min_left = kLineLength * 6 / 10
+ max_right = kLineLength * 7 / 10
+ return random.randint(min_left, kLineLength), random.randint(1, max_right)
+
+class Mod(Div): # Sharing GenerateInputLengths.
+ def GetOpString(self): return "%"
+ def GenerateResult(self, a, b):
+ result = a % b
+ if a < 0 and result > 0:
+ result -= abs(b)
+ if a > 0 and result < 0:
+ result += abs(b)
+ return result
+
+class Shl(BinaryOp):
+ def GetOpString(self): return "<<"
+ def GenerateInputsInternal(self, small_shift_positive):
+ left_length = random.randint(0, kLineLength - 1)
+ left = GenRandom(left_length)
+ small_shift = random.randint(0, 1) == 0
+ if small_shift:
+ right_length = 1 + int(math.log((kLineLength - left_length), kBase))
+ neg = kNo if small_shift_positive else kYes
+ else:
+ right_length = random.randint(0, 3)
+ neg = kYes if small_shift_positive else kNo
+ right = GenRandom(right_length, negative=neg)
+ return left, right
+
+ def GenerateInputs(self): return self.GenerateInputsInternal(True)
+ def GenerateResult(self, a, b):
+ if b < 0: return a >> -b
+ return a << b
+
+class Sar(Shl): # Sharing GenerateInputsInternal.
+ def GetOpString(self): return ">>"
+ def GenerateInputs(self):
+ return self.GenerateInputsInternal(False)
+ def GenerateResult(self, a, b):
+ if b < 0: return a << -b
+ return a >> b
+
+class BitAnd(BinaryOp):
+ def GetOpString(self): return "&"
+ def GenerateResult(self, a, b): return a & b
+
+class BitOr(BinaryOp):
+ def GetOpString(self): return "|"
+ def GenerateResult(self, a, b): return a | b
+
+class BitXor(BinaryOp):
+ def GetOpString(self): return "^"
+ def GenerateResult(self, a, b): return a ^ b
+
+OPS = {
+ "add": Add,
+ "sub": Sub,
+ "mul": Mul,
+ "div": Div,
+ "mod": Mod,
+ "inc": Inc,
+ "dec": Dec,
+ "neg": Neg,
+ "not": BitNot,
+ "shl": Shl,
+ "sar": Sar,
+ "and": BitAnd,
+ "or": BitOr,
+ "xor": BitXor
+}
+
+OPS_NAMES = ", ".join(sorted(OPS.keys()))
+
+def RunOne(op, num_inputs, binary):
+ return OPS[op]().RunTest(num_inputs, binary)
+def WrapRunOne(args):
+ return RunOne(*args)
+def RunAll(args):
+ for op in args.op:
+ for r in xrange(args.runs):
+ yield (op, args.num_inputs, args.binary)
+
+def Main():
+ parser = argparse.ArgumentParser(
+ description="Helper for generating or running BigInt tests.")
+ parser.add_argument(
+ "action", help="Action to perform: 'generate' or 'stress'")
+ parser.add_argument(
+ "op", nargs="+",
+ help="Operation(s) to test, one or more of: %s. In 'stress' mode, "
+ "special op 'all' tests all ops." % OPS_NAMES)
+ parser.add_argument(
+ "-n", "--num-inputs", type=int, default=-1,
+ help="Number of input/output sets in each generated test. Defaults to "
+ "%d for 'generate' and '%d' for 'stress' mode." %
+ (kNumInputsGenerate, kNumInputsStress))
+
+ stressopts = parser.add_argument_group("'stress' mode arguments")
+ stressopts.add_argument(
+ "-r", "--runs", type=int, default=1000,
+ help="Number of tests (with NUM_INPUTS each) to generate and run. "
+ "Default: %(default)s.")
+ stressopts.add_argument(
+ "-b", "--binary", default="out/x64.debug/d8",
+ help="The 'd8' binary to use. Default: %(default)s.")
+ args = parser.parse_args()
+
+ for op in args.op:
+ if op not in OPS.keys() and op != "all":
+ print("Invalid op '%s'. See --help." % op)
+ return 1
+
+ if len(args.op) == 1 and args.op[0] == "all":
+ args.op = OPS.keys()
+
+ if args.action == "generate":
+ if args.num_inputs < 0: args.num_inputs = kNumInputsGenerate
+ for op in args.op:
+ OPS[op]().PrintTest(args.num_inputs)
+ elif args.action == "stress":
+ if args.num_inputs < 0: args.num_inputs = kNumInputsStress
+ result = 0
+ pool = multiprocessing.Pool(multiprocessing.cpu_count())
+ for r in pool.imap_unordered(WrapRunOne, RunAll(args)):
+ result = result or r
+ return result
+ else:
+ print("Invalid action '%s'. See --help." % args.action)
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(Main())