init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
0
.venv/lib/python3.8/site-packages/mypyc/test/__init__.py
Normal file
0
.venv/lib/python3.8/site-packages/mypyc/test/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
11
.venv/lib/python3.8/site-packages/mypyc/test/config.py
Normal file
11
.venv/lib/python3.8/site-packages/mypyc/test/config.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import os
|
||||
|
||||
provided_prefix = os.getenv('MYPY_TEST_PREFIX', None)
|
||||
if provided_prefix:
|
||||
PREFIX = provided_prefix
|
||||
else:
|
||||
this_file_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
PREFIX = os.path.dirname(os.path.dirname(this_file_dir))
|
||||
|
||||
# Location of test data files such as test case descriptions.
|
||||
test_data_prefix = os.path.join(PREFIX, 'mypyc', 'test-data')
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
"""Test runner for data-flow analysis test cases."""
|
||||
|
||||
import os.path
|
||||
from typing import Set
|
||||
|
||||
from mypy.test.data import DataDrivenTestCase
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.errors import CompileError
|
||||
|
||||
from mypyc.common import TOP_LEVEL_NAME
|
||||
from mypyc.analysis import dataflow
|
||||
from mypyc.transform import exceptions
|
||||
from mypyc.ir.pprint import format_func, generate_names_for_ir
|
||||
from mypyc.ir.ops import Value
|
||||
from mypyc.ir.func_ir import all_values
|
||||
from mypyc.test.testutil import (
|
||||
ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file,
|
||||
assert_test_output
|
||||
)
|
||||
|
||||
files = [
|
||||
'analysis.test'
|
||||
]
|
||||
|
||||
|
||||
class TestAnalysis(MypycDataSuite):
|
||||
files = files
|
||||
base_path = test_temp_dir
|
||||
optional_out = True
|
||||
|
||||
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||||
"""Perform a data-flow analysis test case."""
|
||||
|
||||
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
|
||||
try:
|
||||
ir = build_ir_for_single_file(testcase.input)
|
||||
except CompileError as e:
|
||||
actual = e.messages
|
||||
else:
|
||||
actual = []
|
||||
for fn in ir:
|
||||
if (fn.name == TOP_LEVEL_NAME
|
||||
and not testcase.name.endswith('_toplevel')):
|
||||
continue
|
||||
exceptions.insert_exception_handling(fn)
|
||||
actual.extend(format_func(fn))
|
||||
cfg = dataflow.get_cfg(fn.blocks)
|
||||
args: Set[Value] = set(fn.arg_regs)
|
||||
name = testcase.name
|
||||
if name.endswith('_MaybeDefined'):
|
||||
# Forward, maybe
|
||||
analysis_result = dataflow.analyze_maybe_defined_regs(fn.blocks, cfg, args)
|
||||
elif name.endswith('_Liveness'):
|
||||
# Backward, maybe
|
||||
analysis_result = dataflow.analyze_live_regs(fn.blocks, cfg)
|
||||
elif name.endswith('_MustDefined'):
|
||||
# Forward, must
|
||||
analysis_result = dataflow.analyze_must_defined_regs(
|
||||
fn.blocks, cfg, args,
|
||||
regs=all_values(fn.arg_regs, fn.blocks))
|
||||
elif name.endswith('_BorrowedArgument'):
|
||||
# Forward, must
|
||||
analysis_result = dataflow.analyze_borrowed_arguments(fn.blocks, cfg, args)
|
||||
else:
|
||||
assert False, 'No recognized _AnalysisName suffix in test case'
|
||||
|
||||
names = generate_names_for_ir(fn.arg_regs, fn.blocks)
|
||||
|
||||
for key in sorted(analysis_result.before.keys(),
|
||||
key=lambda x: (x[0].label, x[1])):
|
||||
pre = ', '.join(sorted(names[reg]
|
||||
for reg in analysis_result.before[key]))
|
||||
post = ', '.join(sorted(names[reg]
|
||||
for reg in analysis_result.after[key]))
|
||||
actual.append('%-8s %-23s %s' % ((key[0].label, key[1]),
|
||||
'{%s}' % pre, '{%s}' % post))
|
||||
assert_test_output(testcase, actual, 'Invalid source code output')
|
||||
40
.venv/lib/python3.8/site-packages/mypyc/test/test_cheader.py
Normal file
40
.venv/lib/python3.8/site-packages/mypyc/test/test_cheader.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
"""Test that C functions used in primitives are declared in a header such as CPy.h."""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from mypyc.primitives import registry
|
||||
from mypyc.primitives.registry import CFunctionDescription
|
||||
|
||||
|
||||
class TestHeaderInclusion(unittest.TestCase):
|
||||
def test_primitives_included_in_header(self) -> None:
|
||||
base_dir = os.path.join(os.path.dirname(__file__), '..', 'lib-rt')
|
||||
with open(os.path.join(base_dir, 'CPy.h')) as f:
|
||||
header = f.read()
|
||||
with open(os.path.join(base_dir, 'pythonsupport.h')) as f:
|
||||
header += f.read()
|
||||
|
||||
def check_name(name: str) -> None:
|
||||
if name.startswith('CPy'):
|
||||
assert re.search(r'\b{}\b'.format(name), header), (
|
||||
'"{}" is used in mypyc.primitives but not declared in CPy.h'.format(name))
|
||||
|
||||
for values in [registry.method_call_ops.values(),
|
||||
registry.function_ops.values(),
|
||||
registry.binary_ops.values(),
|
||||
registry.unary_ops.values()]:
|
||||
for ops in values:
|
||||
if isinstance(ops, CFunctionDescription):
|
||||
ops = [ops]
|
||||
for op in ops:
|
||||
check_name(op.c_function_name)
|
||||
|
||||
primitives_path = os.path.join(os.path.dirname(__file__), '..', 'primitives')
|
||||
for fnam in glob.glob('{}/*.py'.format(primitives_path)):
|
||||
with open(fnam) as f:
|
||||
content = f.read()
|
||||
for name in re.findall(r'c_function_name=["\'](CPy[A-Z_a-z0-9]+)', content):
|
||||
check_name(name)
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
"""Test cases for invoking mypyc on the command line.
|
||||
|
||||
These are slow -- do not add test cases unless you have a very good reason to do so.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from mypy.test.data import DataDrivenTestCase
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.test.helpers import normalize_error_messages
|
||||
|
||||
from mypyc.test.testutil import MypycDataSuite, assert_test_output
|
||||
|
||||
files = [
|
||||
'commandline.test',
|
||||
]
|
||||
|
||||
|
||||
base_path = os.path.join(os.path.dirname(__file__), '..', '..')
|
||||
|
||||
python3_path = sys.executable
|
||||
|
||||
|
||||
class TestCommandLine(MypycDataSuite):
|
||||
files = files
|
||||
base_path = test_temp_dir
|
||||
optional_out = True
|
||||
|
||||
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||||
# Parse options from test case description (arguments must not have spaces)
|
||||
text = '\n'.join(testcase.input)
|
||||
m = re.search(r'# *cmd: *(.*)', text)
|
||||
assert m is not None, 'Test case missing "# cmd: <files>" section'
|
||||
args = m.group(1).split()
|
||||
|
||||
# Write main program to run (not compiled)
|
||||
program = '_%s.py' % testcase.name
|
||||
program_path = os.path.join(test_temp_dir, program)
|
||||
with open(program_path, 'w') as f:
|
||||
f.write(text)
|
||||
|
||||
out = b''
|
||||
try:
|
||||
# Compile program
|
||||
cmd = subprocess.run([sys.executable, '-m', 'mypyc', *args],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd='tmp')
|
||||
if 'ErrorOutput' in testcase.name or cmd.returncode != 0:
|
||||
out += cmd.stdout
|
||||
|
||||
if cmd.returncode == 0:
|
||||
# Run main program
|
||||
out += subprocess.check_output(
|
||||
[python3_path, program],
|
||||
cwd='tmp')
|
||||
finally:
|
||||
suffix = 'pyd' if sys.platform == 'win32' else 'so'
|
||||
so_paths = glob.glob('tmp/**/*.{}'.format(suffix), recursive=True)
|
||||
for path in so_paths:
|
||||
os.remove(path)
|
||||
|
||||
# Strip out 'tmp/' from error message paths in the testcase output,
|
||||
# due to a mismatch between this test and mypy's test suite.
|
||||
expected = [x.replace('tmp/', '') for x in testcase.output]
|
||||
|
||||
# Verify output
|
||||
actual = normalize_error_messages(out.decode().splitlines())
|
||||
assert_test_output(testcase, actual, 'Invalid output', expected=expected)
|
||||
33
.venv/lib/python3.8/site-packages/mypyc/test/test_emit.py
Normal file
33
.venv/lib/python3.8/site-packages/mypyc/test/test_emit.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import unittest
|
||||
from typing import Dict
|
||||
|
||||
from mypyc.codegen.emit import Emitter, EmitterContext
|
||||
from mypyc.ir.ops import BasicBlock, Value, Register
|
||||
from mypyc.ir.rtypes import int_rprimitive
|
||||
from mypyc.namegen import NameGenerator
|
||||
|
||||
|
||||
class TestEmitter(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.n = Register(int_rprimitive, 'n')
|
||||
self.context = EmitterContext(NameGenerator([['mod']]))
|
||||
|
||||
def test_label(self) -> None:
|
||||
emitter = Emitter(self.context, {})
|
||||
assert emitter.label(BasicBlock(4)) == 'CPyL4'
|
||||
|
||||
def test_reg(self) -> None:
|
||||
names: Dict[Value, str] = {self.n: "n"}
|
||||
emitter = Emitter(self.context, names)
|
||||
assert emitter.reg(self.n) == 'cpy_r_n'
|
||||
|
||||
def test_emit_line(self) -> None:
|
||||
emitter = Emitter(self.context, {})
|
||||
emitter.emit_line('line;')
|
||||
emitter.emit_line('a {')
|
||||
emitter.emit_line('f();')
|
||||
emitter.emit_line('}')
|
||||
assert emitter.fragments == ['line;\n',
|
||||
'a {\n',
|
||||
' f();\n',
|
||||
'}\n']
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import unittest
|
||||
|
||||
from mypyc.codegen.emitclass import slot_key
|
||||
|
||||
|
||||
class TestEmitClass(unittest.TestCase):
|
||||
def test_slot_key(self) -> None:
|
||||
attrs = ['__add__', '__radd__', '__rshift__', '__rrshift__', '__setitem__', '__delitem__']
|
||||
s = sorted(attrs, key=lambda x: slot_key(x))
|
||||
# __delitem__ and reverse methods should come last.
|
||||
assert s == [
|
||||
'__add__', '__rshift__', '__setitem__', '__delitem__', '__radd__', '__rrshift__']
|
||||
524
.venv/lib/python3.8/site-packages/mypyc/test/test_emitfunc.py
Normal file
524
.venv/lib/python3.8/site-packages/mypyc/test/test_emitfunc.py
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
import unittest
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from mypy.backports import OrderedDict
|
||||
|
||||
from mypy.test.helpers import assert_string_arrays_equal
|
||||
|
||||
from mypyc.ir.ops import (
|
||||
BasicBlock, Goto, Return, Integer, Assign, AssignMulti, IncRef, DecRef, Branch,
|
||||
Call, Unbox, Box, TupleGet, GetAttr, SetAttr, Op, Value, CallC, IntOp, LoadMem,
|
||||
GetElementPtr, LoadAddress, ComparisonOp, SetMem, Register, Unreachable
|
||||
)
|
||||
from mypyc.ir.rtypes import (
|
||||
RTuple, RInstance, RType, RArray, int_rprimitive, bool_rprimitive, list_rprimitive,
|
||||
dict_rprimitive, object_rprimitive, c_int_rprimitive, short_int_rprimitive, int32_rprimitive,
|
||||
int64_rprimitive, RStruct, pointer_rprimitive
|
||||
)
|
||||
from mypyc.ir.func_ir import FuncIR, FuncDecl, RuntimeArg, FuncSignature
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.pprint import generate_names_for_ir
|
||||
from mypyc.irbuild.vtable import compute_vtable
|
||||
from mypyc.codegen.emit import Emitter, EmitterContext
|
||||
from mypyc.codegen.emitfunc import generate_native_function, FunctionEmitterVisitor
|
||||
from mypyc.primitives.registry import binary_ops
|
||||
from mypyc.primitives.misc_ops import none_object_op
|
||||
from mypyc.primitives.list_ops import list_get_item_op, list_set_item_op, list_append_op
|
||||
from mypyc.primitives.dict_ops import (
|
||||
dict_new_op, dict_update_op, dict_get_item_op, dict_set_item_op
|
||||
)
|
||||
from mypyc.primitives.int_ops import int_neg_op
|
||||
from mypyc.subtype import is_subtype
|
||||
from mypyc.namegen import NameGenerator
|
||||
|
||||
|
||||
class TestFunctionEmitterVisitor(unittest.TestCase):
|
||||
"""Test generation of fragments of C from individual IR ops."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.registers: List[Register] = []
|
||||
|
||||
def add_local(name: str, rtype: RType) -> Register:
|
||||
reg = Register(rtype, name)
|
||||
self.registers.append(reg)
|
||||
return reg
|
||||
|
||||
self.n = add_local('n', int_rprimitive)
|
||||
self.m = add_local('m', int_rprimitive)
|
||||
self.k = add_local('k', int_rprimitive)
|
||||
self.l = add_local('l', list_rprimitive) # noqa
|
||||
self.ll = add_local('ll', list_rprimitive)
|
||||
self.o = add_local('o', object_rprimitive)
|
||||
self.o2 = add_local('o2', object_rprimitive)
|
||||
self.d = add_local('d', dict_rprimitive)
|
||||
self.b = add_local('b', bool_rprimitive)
|
||||
self.s1 = add_local('s1', short_int_rprimitive)
|
||||
self.s2 = add_local('s2', short_int_rprimitive)
|
||||
self.i32 = add_local('i32', int32_rprimitive)
|
||||
self.i32_1 = add_local('i32_1', int32_rprimitive)
|
||||
self.i64 = add_local('i64', int64_rprimitive)
|
||||
self.i64_1 = add_local('i64_1', int64_rprimitive)
|
||||
self.ptr = add_local('ptr', pointer_rprimitive)
|
||||
self.t = add_local('t', RTuple([int_rprimitive, bool_rprimitive]))
|
||||
self.tt = add_local(
|
||||
'tt', RTuple([RTuple([int_rprimitive, bool_rprimitive]), bool_rprimitive]))
|
||||
ir = ClassIR('A', 'mod')
|
||||
ir.attributes = OrderedDict([('x', bool_rprimitive), ('y', int_rprimitive)])
|
||||
compute_vtable(ir)
|
||||
ir.mro = [ir]
|
||||
self.r = add_local('r', RInstance(ir))
|
||||
|
||||
self.context = EmitterContext(NameGenerator([['mod']]))
|
||||
|
||||
def test_goto(self) -> None:
|
||||
self.assert_emit(Goto(BasicBlock(2)),
|
||||
"goto CPyL2;")
|
||||
|
||||
def test_goto_next_block(self) -> None:
|
||||
next_block = BasicBlock(2)
|
||||
self.assert_emit(Goto(next_block), "", next_block=next_block)
|
||||
|
||||
def test_return(self) -> None:
|
||||
self.assert_emit(Return(self.m),
|
||||
"return cpy_r_m;")
|
||||
|
||||
def test_integer(self) -> None:
|
||||
self.assert_emit(Assign(self.n, Integer(5)),
|
||||
"cpy_r_n = 10;")
|
||||
self.assert_emit(Assign(self.i32, Integer(5, c_int_rprimitive)),
|
||||
"cpy_r_i32 = 5;")
|
||||
|
||||
def test_tuple_get(self) -> None:
|
||||
self.assert_emit(TupleGet(self.t, 1, 0), 'cpy_r_r0 = cpy_r_t.f1;')
|
||||
|
||||
def test_load_None(self) -> None:
|
||||
self.assert_emit(LoadAddress(none_object_op.type, none_object_op.src, 0),
|
||||
"cpy_r_r0 = (PyObject *)&_Py_NoneStruct;")
|
||||
|
||||
def test_assign_int(self) -> None:
|
||||
self.assert_emit(Assign(self.m, self.n),
|
||||
"cpy_r_m = cpy_r_n;")
|
||||
|
||||
def test_int_add(self) -> None:
|
||||
self.assert_emit_binary_op(
|
||||
'+', self.n, self.m, self.k,
|
||||
"cpy_r_r0 = CPyTagged_Add(cpy_r_m, cpy_r_k);")
|
||||
|
||||
def test_int_sub(self) -> None:
|
||||
self.assert_emit_binary_op(
|
||||
'-', self.n, self.m, self.k,
|
||||
"cpy_r_r0 = CPyTagged_Subtract(cpy_r_m, cpy_r_k);")
|
||||
|
||||
def test_int_neg(self) -> None:
|
||||
self.assert_emit(CallC(int_neg_op.c_function_name, [self.m], int_neg_op.return_type,
|
||||
int_neg_op.steals, int_neg_op.is_borrowed, int_neg_op.is_borrowed,
|
||||
int_neg_op.error_kind, 55),
|
||||
"cpy_r_r0 = CPyTagged_Negate(cpy_r_m);")
|
||||
|
||||
def test_branch(self) -> None:
|
||||
self.assert_emit(Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL),
|
||||
"""if (cpy_r_b) {
|
||||
goto CPyL8;
|
||||
} else
|
||||
goto CPyL9;
|
||||
""")
|
||||
b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL)
|
||||
b.negated = True
|
||||
self.assert_emit(b,
|
||||
"""if (!cpy_r_b) {
|
||||
goto CPyL8;
|
||||
} else
|
||||
goto CPyL9;
|
||||
""")
|
||||
|
||||
def test_branch_no_else(self) -> None:
|
||||
next_block = BasicBlock(9)
|
||||
b = Branch(self.b, BasicBlock(8), next_block, Branch.BOOL)
|
||||
self.assert_emit(b,
|
||||
"""if (cpy_r_b) goto CPyL8;""",
|
||||
next_block=next_block)
|
||||
next_block = BasicBlock(9)
|
||||
b = Branch(self.b, BasicBlock(8), next_block, Branch.BOOL)
|
||||
b.negated = True
|
||||
self.assert_emit(b,
|
||||
"""if (!cpy_r_b) goto CPyL8;""",
|
||||
next_block=next_block)
|
||||
|
||||
def test_branch_no_else_negated(self) -> None:
|
||||
next_block = BasicBlock(1)
|
||||
b = Branch(self.b, next_block, BasicBlock(2), Branch.BOOL)
|
||||
self.assert_emit(b,
|
||||
"""if (!cpy_r_b) goto CPyL2;""",
|
||||
next_block=next_block)
|
||||
next_block = BasicBlock(1)
|
||||
b = Branch(self.b, next_block, BasicBlock(2), Branch.BOOL)
|
||||
b.negated = True
|
||||
self.assert_emit(b,
|
||||
"""if (cpy_r_b) goto CPyL2;""",
|
||||
next_block=next_block)
|
||||
|
||||
def test_branch_is_error(self) -> None:
|
||||
b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
|
||||
self.assert_emit(b,
|
||||
"""if (cpy_r_b == 2) {
|
||||
goto CPyL8;
|
||||
} else
|
||||
goto CPyL9;
|
||||
""")
|
||||
b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
|
||||
b.negated = True
|
||||
self.assert_emit(b,
|
||||
"""if (cpy_r_b != 2) {
|
||||
goto CPyL8;
|
||||
} else
|
||||
goto CPyL9;
|
||||
""")
|
||||
|
||||
def test_branch_is_error_next_block(self) -> None:
|
||||
next_block = BasicBlock(8)
|
||||
b = Branch(self.b, next_block, BasicBlock(9), Branch.IS_ERROR)
|
||||
self.assert_emit(b,
|
||||
"""if (cpy_r_b != 2) goto CPyL9;""",
|
||||
next_block=next_block)
|
||||
b = Branch(self.b, next_block, BasicBlock(9), Branch.IS_ERROR)
|
||||
b.negated = True
|
||||
self.assert_emit(b,
|
||||
"""if (cpy_r_b == 2) goto CPyL9;""",
|
||||
next_block=next_block)
|
||||
|
||||
def test_branch_rare(self) -> None:
|
||||
self.assert_emit(Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL, rare=True),
|
||||
"""if (unlikely(cpy_r_b)) {
|
||||
goto CPyL8;
|
||||
} else
|
||||
goto CPyL9;
|
||||
""")
|
||||
next_block = BasicBlock(9)
|
||||
self.assert_emit(Branch(self.b, BasicBlock(8), next_block, Branch.BOOL, rare=True),
|
||||
"""if (unlikely(cpy_r_b)) goto CPyL8;""",
|
||||
next_block=next_block)
|
||||
next_block = BasicBlock(8)
|
||||
b = Branch(self.b, next_block, BasicBlock(9), Branch.BOOL, rare=True)
|
||||
self.assert_emit(b,
|
||||
"""if (likely(!cpy_r_b)) goto CPyL9;""",
|
||||
next_block=next_block)
|
||||
next_block = BasicBlock(8)
|
||||
b = Branch(self.b, next_block, BasicBlock(9), Branch.BOOL, rare=True)
|
||||
b.negated = True
|
||||
self.assert_emit(b,
|
||||
"""if (likely(cpy_r_b)) goto CPyL9;""",
|
||||
next_block=next_block)
|
||||
|
||||
def test_call(self) -> None:
|
||||
decl = FuncDecl('myfn', None, 'mod',
|
||||
FuncSignature([RuntimeArg('m', int_rprimitive)], int_rprimitive))
|
||||
self.assert_emit(Call(decl, [self.m], 55),
|
||||
"cpy_r_r0 = CPyDef_myfn(cpy_r_m);")
|
||||
|
||||
def test_call_two_args(self) -> None:
|
||||
decl = FuncDecl('myfn', None, 'mod',
|
||||
FuncSignature([RuntimeArg('m', int_rprimitive),
|
||||
RuntimeArg('n', int_rprimitive)],
|
||||
int_rprimitive))
|
||||
self.assert_emit(Call(decl, [self.m, self.k], 55),
|
||||
"cpy_r_r0 = CPyDef_myfn(cpy_r_m, cpy_r_k);")
|
||||
|
||||
def test_inc_ref(self) -> None:
|
||||
self.assert_emit(IncRef(self.o), "CPy_INCREF(cpy_r_o);")
|
||||
self.assert_emit(IncRef(self.o), "CPy_INCREF(cpy_r_o);", rare=True)
|
||||
|
||||
def test_dec_ref(self) -> None:
|
||||
self.assert_emit(DecRef(self.o), "CPy_DECREF(cpy_r_o);")
|
||||
self.assert_emit(DecRef(self.o), "CPy_DecRef(cpy_r_o);", rare=True)
|
||||
|
||||
def test_inc_ref_int(self) -> None:
|
||||
self.assert_emit(IncRef(self.m), "CPyTagged_INCREF(cpy_r_m);")
|
||||
self.assert_emit(IncRef(self.m), "CPyTagged_IncRef(cpy_r_m);", rare=True)
|
||||
|
||||
def test_dec_ref_int(self) -> None:
|
||||
self.assert_emit(DecRef(self.m), "CPyTagged_DECREF(cpy_r_m);")
|
||||
self.assert_emit(DecRef(self.m), "CPyTagged_DecRef(cpy_r_m);", rare=True)
|
||||
|
||||
def test_dec_ref_tuple(self) -> None:
|
||||
self.assert_emit(DecRef(self.t), 'CPyTagged_DECREF(cpy_r_t.f0);')
|
||||
|
||||
def test_dec_ref_tuple_nested(self) -> None:
|
||||
self.assert_emit(DecRef(self.tt), 'CPyTagged_DECREF(cpy_r_tt.f0.f0);')
|
||||
|
||||
def test_list_get_item(self) -> None:
|
||||
self.assert_emit(CallC(list_get_item_op.c_function_name, [self.m, self.k],
|
||||
list_get_item_op.return_type, list_get_item_op.steals,
|
||||
list_get_item_op.is_borrowed, list_get_item_op.error_kind, 55),
|
||||
"""cpy_r_r0 = CPyList_GetItem(cpy_r_m, cpy_r_k);""")
|
||||
|
||||
def test_list_set_item(self) -> None:
|
||||
self.assert_emit(CallC(list_set_item_op.c_function_name, [self.l, self.n, self.o],
|
||||
list_set_item_op.return_type, list_set_item_op.steals,
|
||||
list_set_item_op.is_borrowed, list_set_item_op.error_kind, 55),
|
||||
"""cpy_r_r0 = CPyList_SetItem(cpy_r_l, cpy_r_n, cpy_r_o);""")
|
||||
|
||||
def test_box(self) -> None:
|
||||
self.assert_emit(Box(self.n),
|
||||
"""cpy_r_r0 = CPyTagged_StealAsObject(cpy_r_n);""")
|
||||
|
||||
def test_unbox(self) -> None:
|
||||
self.assert_emit(Unbox(self.m, int_rprimitive, 55),
|
||||
"""if (likely(PyLong_Check(cpy_r_m)))
|
||||
cpy_r_r0 = CPyTagged_FromObject(cpy_r_m);
|
||||
else {
|
||||
CPy_TypeError("int", cpy_r_m); cpy_r_r0 = CPY_INT_TAG;
|
||||
}
|
||||
""")
|
||||
|
||||
def test_list_append(self) -> None:
|
||||
self.assert_emit(CallC(list_append_op.c_function_name, [self.l, self.o],
|
||||
list_append_op.return_type, list_append_op.steals,
|
||||
list_append_op.is_borrowed, list_append_op.error_kind, 1),
|
||||
"""cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o);""")
|
||||
|
||||
def test_get_attr(self) -> None:
|
||||
self.assert_emit(
|
||||
GetAttr(self.r, 'y', 1),
|
||||
"""cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_y;
|
||||
if (unlikely(((mod___AObject *)cpy_r_r)->_y == CPY_INT_TAG)) {
|
||||
PyErr_SetString(PyExc_AttributeError, "attribute 'y' of 'A' undefined");
|
||||
} else {
|
||||
CPyTagged_INCREF(((mod___AObject *)cpy_r_r)->_y);
|
||||
}
|
||||
""")
|
||||
|
||||
def test_get_attr_non_refcounted(self) -> None:
|
||||
self.assert_emit(
|
||||
GetAttr(self.r, 'x', 1),
|
||||
"""cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_x;
|
||||
if (unlikely(((mod___AObject *)cpy_r_r)->_x == 2)) {
|
||||
PyErr_SetString(PyExc_AttributeError, "attribute 'x' of 'A' undefined");
|
||||
}
|
||||
""")
|
||||
|
||||
def test_set_attr(self) -> None:
|
||||
self.assert_emit(
|
||||
SetAttr(self.r, 'y', self.m, 1),
|
||||
"""if (((mod___AObject *)cpy_r_r)->_y != CPY_INT_TAG) {
|
||||
CPyTagged_DECREF(((mod___AObject *)cpy_r_r)->_y);
|
||||
}
|
||||
((mod___AObject *)cpy_r_r)->_y = cpy_r_m;
|
||||
cpy_r_r0 = 1;
|
||||
""")
|
||||
|
||||
def test_dict_get_item(self) -> None:
|
||||
self.assert_emit(CallC(dict_get_item_op.c_function_name, [self.d, self.o2],
|
||||
dict_get_item_op.return_type, dict_get_item_op.steals,
|
||||
dict_get_item_op.is_borrowed, dict_get_item_op.error_kind, 1),
|
||||
"""cpy_r_r0 = CPyDict_GetItem(cpy_r_d, cpy_r_o2);""")
|
||||
|
||||
def test_dict_set_item(self) -> None:
|
||||
self.assert_emit(CallC(dict_set_item_op.c_function_name, [self.d, self.o, self.o2],
|
||||
dict_set_item_op.return_type, dict_set_item_op.steals,
|
||||
dict_set_item_op.is_borrowed, dict_set_item_op.error_kind, 1),
|
||||
"""cpy_r_r0 = CPyDict_SetItem(cpy_r_d, cpy_r_o, cpy_r_o2);""")
|
||||
|
||||
def test_dict_update(self) -> None:
|
||||
self.assert_emit(CallC(dict_update_op.c_function_name, [self.d, self.o],
|
||||
dict_update_op.return_type, dict_update_op.steals,
|
||||
dict_update_op.is_borrowed, dict_update_op.error_kind, 1),
|
||||
"""cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o);""")
|
||||
|
||||
def test_new_dict(self) -> None:
|
||||
self.assert_emit(CallC(dict_new_op.c_function_name, [], dict_new_op.return_type,
|
||||
dict_new_op.steals, dict_new_op.is_borrowed,
|
||||
dict_new_op.error_kind, 1),
|
||||
"""cpy_r_r0 = PyDict_New();""")
|
||||
|
||||
def test_dict_contains(self) -> None:
|
||||
self.assert_emit_binary_op(
|
||||
'in', self.b, self.o, self.d,
|
||||
"""cpy_r_r0 = PyDict_Contains(cpy_r_d, cpy_r_o);""")
|
||||
|
||||
def test_int_op(self) -> None:
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.ADD, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 + cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.SUB, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 - cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.MUL, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 * cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.DIV, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 / cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.MOD, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 % cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.AND, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 & cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.OR, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 | cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.XOR, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 ^ cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.LEFT_SHIFT, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 << cpy_r_s2;""")
|
||||
self.assert_emit(IntOp(short_int_rprimitive, self.s1, self.s2, IntOp.RIGHT_SHIFT, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 >> cpy_r_s2;""")
|
||||
|
||||
def test_comparison_op(self) -> None:
|
||||
# signed
|
||||
self.assert_emit(ComparisonOp(self.s1, self.s2, ComparisonOp.SLT, 1),
|
||||
"""cpy_r_r0 = (Py_ssize_t)cpy_r_s1 < (Py_ssize_t)cpy_r_s2;""")
|
||||
self.assert_emit(ComparisonOp(self.i32, self.i32_1, ComparisonOp.SLT, 1),
|
||||
"""cpy_r_r0 = cpy_r_i32 < cpy_r_i32_1;""")
|
||||
self.assert_emit(ComparisonOp(self.i64, self.i64_1, ComparisonOp.SLT, 1),
|
||||
"""cpy_r_r0 = cpy_r_i64 < cpy_r_i64_1;""")
|
||||
# unsigned
|
||||
self.assert_emit(ComparisonOp(self.s1, self.s2, ComparisonOp.ULT, 1),
|
||||
"""cpy_r_r0 = cpy_r_s1 < cpy_r_s2;""")
|
||||
self.assert_emit(ComparisonOp(self.i32, self.i32_1, ComparisonOp.ULT, 1),
|
||||
"""cpy_r_r0 = (uint32_t)cpy_r_i32 < (uint32_t)cpy_r_i32_1;""")
|
||||
self.assert_emit(ComparisonOp(self.i64, self.i64_1, ComparisonOp.ULT, 1),
|
||||
"""cpy_r_r0 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""")
|
||||
|
||||
# object type
|
||||
self.assert_emit(ComparisonOp(self.o, self.o2, ComparisonOp.EQ, 1),
|
||||
"""cpy_r_r0 = cpy_r_o == cpy_r_o2;""")
|
||||
self.assert_emit(ComparisonOp(self.o, self.o2, ComparisonOp.NEQ, 1),
|
||||
"""cpy_r_r0 = cpy_r_o != cpy_r_o2;""")
|
||||
|
||||
def test_load_mem(self) -> None:
|
||||
self.assert_emit(LoadMem(bool_rprimitive, self.ptr),
|
||||
"""cpy_r_r0 = *(char *)cpy_r_ptr;""")
|
||||
|
||||
def test_set_mem(self) -> None:
|
||||
self.assert_emit(SetMem(bool_rprimitive, self.ptr, self.b),
|
||||
"""*(char *)cpy_r_ptr = cpy_r_b;""")
|
||||
|
||||
def test_get_element_ptr(self) -> None:
|
||||
r = RStruct("Foo", ["b", "i32", "i64"], [bool_rprimitive,
|
||||
int32_rprimitive, int64_rprimitive])
|
||||
self.assert_emit(GetElementPtr(self.o, r, "b"),
|
||||
"""cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->b;""")
|
||||
self.assert_emit(GetElementPtr(self.o, r, "i32"),
|
||||
"""cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->i32;""")
|
||||
self.assert_emit(GetElementPtr(self.o, r, "i64"),
|
||||
"""cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->i64;""")
|
||||
|
||||
def test_load_address(self) -> None:
|
||||
self.assert_emit(LoadAddress(object_rprimitive, "PyDict_Type"),
|
||||
"""cpy_r_r0 = (PyObject *)&PyDict_Type;""")
|
||||
|
||||
def test_assign_multi(self) -> None:
|
||||
t = RArray(object_rprimitive, 2)
|
||||
a = Register(t, 'a')
|
||||
self.registers.append(a)
|
||||
self.assert_emit(AssignMulti(a, [self.o, self.o2]),
|
||||
"""PyObject *cpy_r_a[2] = {cpy_r_o, cpy_r_o2};""")
|
||||
|
||||
def test_long_unsigned(self) -> None:
|
||||
a = Register(int64_rprimitive, 'a')
|
||||
self.assert_emit(Assign(a, Integer(1 << 31, int64_rprimitive)),
|
||||
"""cpy_r_a = 2147483648ULL;""")
|
||||
self.assert_emit(Assign(a, Integer((1 << 31) - 1, int64_rprimitive)),
|
||||
"""cpy_r_a = 2147483647;""")
|
||||
|
||||
def test_long_signed(self) -> None:
|
||||
a = Register(int64_rprimitive, 'a')
|
||||
self.assert_emit(Assign(a, Integer(-(1 << 31) + 1, int64_rprimitive)),
|
||||
"""cpy_r_a = -2147483647;""")
|
||||
self.assert_emit(Assign(a, Integer(-(1 << 31), int64_rprimitive)),
|
||||
"""cpy_r_a = -2147483648LL;""")
|
||||
|
||||
def assert_emit(self,
|
||||
op: Op,
|
||||
expected: str,
|
||||
next_block: Optional[BasicBlock] = None,
|
||||
*,
|
||||
rare: bool = False) -> None:
|
||||
block = BasicBlock(0)
|
||||
block.ops.append(op)
|
||||
value_names = generate_names_for_ir(self.registers, [block])
|
||||
emitter = Emitter(self.context, value_names)
|
||||
declarations = Emitter(self.context, value_names)
|
||||
emitter.fragments = []
|
||||
declarations.fragments = []
|
||||
|
||||
visitor = FunctionEmitterVisitor(emitter, declarations, 'prog.py', 'prog')
|
||||
visitor.next_block = next_block
|
||||
visitor.rare = rare
|
||||
|
||||
op.accept(visitor)
|
||||
frags = declarations.fragments + emitter.fragments
|
||||
actual_lines = [line.strip(' ') for line in frags]
|
||||
assert all(line.endswith('\n') for line in actual_lines)
|
||||
actual_lines = [line.rstrip('\n') for line in actual_lines]
|
||||
if not expected.strip():
|
||||
expected_lines = []
|
||||
else:
|
||||
expected_lines = expected.rstrip().split('\n')
|
||||
expected_lines = [line.strip(' ') for line in expected_lines]
|
||||
assert_string_arrays_equal(expected_lines, actual_lines,
|
||||
msg='Generated code unexpected')
|
||||
|
||||
def assert_emit_binary_op(self,
|
||||
op: str,
|
||||
dest: Value,
|
||||
left: Value,
|
||||
right: Value,
|
||||
expected: str) -> None:
|
||||
if op in binary_ops:
|
||||
ops = binary_ops[op]
|
||||
for desc in ops:
|
||||
if (is_subtype(left.type, desc.arg_types[0])
|
||||
and is_subtype(right.type, desc.arg_types[1])):
|
||||
args = [left, right]
|
||||
if desc.ordering is not None:
|
||||
args = [args[i] for i in desc.ordering]
|
||||
self.assert_emit(CallC(desc.c_function_name, args, desc.return_type,
|
||||
desc.steals, desc.is_borrowed,
|
||||
desc.error_kind, 55), expected)
|
||||
return
|
||||
else:
|
||||
assert False, 'Could not find matching op'
|
||||
|
||||
|
||||
class TestGenerateFunction(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.arg = RuntimeArg('arg', int_rprimitive)
|
||||
self.reg = Register(int_rprimitive, 'arg')
|
||||
self.block = BasicBlock(0)
|
||||
|
||||
def test_simple(self) -> None:
|
||||
self.block.ops.append(Return(self.reg))
|
||||
fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)),
|
||||
[self.reg],
|
||||
[self.block])
|
||||
value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
|
||||
emitter = Emitter(EmitterContext(NameGenerator([['mod']])), value_names)
|
||||
generate_native_function(fn, emitter, 'prog.py', 'prog')
|
||||
result = emitter.fragments
|
||||
assert_string_arrays_equal(
|
||||
[
|
||||
'CPyTagged CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
|
||||
'CPyL0: ;\n',
|
||||
' return cpy_r_arg;\n',
|
||||
'}\n',
|
||||
],
|
||||
result, msg='Generated code invalid')
|
||||
|
||||
def test_register(self) -> None:
|
||||
reg = Register(int_rprimitive)
|
||||
op = Assign(reg, Integer(5))
|
||||
self.block.ops.append(op)
|
||||
self.block.ops.append(Unreachable())
|
||||
fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)),
|
||||
[self.reg],
|
||||
[self.block])
|
||||
value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
|
||||
emitter = Emitter(EmitterContext(NameGenerator([['mod']])), value_names)
|
||||
generate_native_function(fn, emitter, 'prog.py', 'prog')
|
||||
result = emitter.fragments
|
||||
assert_string_arrays_equal(
|
||||
[
|
||||
'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
|
||||
' CPyTagged cpy_r_r0;\n',
|
||||
'CPyL0: ;\n',
|
||||
' cpy_r_r0 = 10;\n',
|
||||
' CPy_Unreachable();\n',
|
||||
'}\n',
|
||||
],
|
||||
result, msg='Generated code invalid')
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import unittest
|
||||
from typing import List
|
||||
|
||||
from mypy.test.helpers import assert_string_arrays_equal
|
||||
|
||||
from mypyc.codegen.emit import Emitter, EmitterContext, ReturnHandler
|
||||
from mypyc.codegen.emitwrapper import generate_arg_check
|
||||
from mypyc.ir.rtypes import list_rprimitive, int_rprimitive
|
||||
from mypyc.namegen import NameGenerator
|
||||
|
||||
|
||||
class TestArgCheck(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.context = EmitterContext(NameGenerator([['mod']]))
|
||||
|
||||
def test_check_list(self) -> None:
|
||||
emitter = Emitter(self.context)
|
||||
generate_arg_check('x', list_rprimitive, emitter, ReturnHandler('NULL'))
|
||||
lines = emitter.fragments
|
||||
self.assert_lines([
|
||||
'PyObject *arg_x;',
|
||||
'if (likely(PyList_Check(obj_x)))',
|
||||
' arg_x = obj_x;',
|
||||
'else {',
|
||||
' CPy_TypeError("list", obj_x); return NULL;',
|
||||
'}',
|
||||
], lines)
|
||||
|
||||
def test_check_int(self) -> None:
|
||||
emitter = Emitter(self.context)
|
||||
generate_arg_check('x', int_rprimitive, emitter, ReturnHandler('NULL'))
|
||||
generate_arg_check('y', int_rprimitive, emitter, ReturnHandler('NULL'), optional=True)
|
||||
lines = emitter.fragments
|
||||
self.assert_lines([
|
||||
'CPyTagged arg_x;',
|
||||
'if (likely(PyLong_Check(obj_x)))',
|
||||
' arg_x = CPyTagged_BorrowFromObject(obj_x);',
|
||||
'else {',
|
||||
' CPy_TypeError("int", obj_x); return NULL;',
|
||||
'}',
|
||||
'CPyTagged arg_y;',
|
||||
'if (obj_y == NULL) {',
|
||||
' arg_y = CPY_INT_TAG;',
|
||||
'} else if (likely(PyLong_Check(obj_y)))',
|
||||
' arg_y = CPyTagged_BorrowFromObject(obj_y);',
|
||||
'else {',
|
||||
' CPy_TypeError("int", obj_y); return NULL;',
|
||||
'}',
|
||||
], lines)
|
||||
|
||||
def assert_lines(self, expected: List[str], actual: List[str]) -> None:
|
||||
actual = [line.rstrip('\n') for line in actual]
|
||||
assert_string_arrays_equal(expected, actual, 'Invalid output')
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Test runner for exception handling transform test cases.
|
||||
|
||||
The transform inserts exception handling branch operations to IR.
|
||||
"""
|
||||
|
||||
import os.path
|
||||
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.test.data import DataDrivenTestCase
|
||||
from mypy.errors import CompileError
|
||||
|
||||
from mypyc.common import TOP_LEVEL_NAME
|
||||
from mypyc.ir.pprint import format_func
|
||||
from mypyc.transform.uninit import insert_uninit_checks
|
||||
from mypyc.transform.exceptions import insert_exception_handling
|
||||
from mypyc.transform.refcount import insert_ref_count_opcodes
|
||||
from mypyc.test.testutil import (
|
||||
ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file,
|
||||
assert_test_output, remove_comment_lines
|
||||
)
|
||||
from mypyc.analysis.blockfreq import frequently_executed_blocks
|
||||
|
||||
files = [
|
||||
'exceptions.test',
|
||||
'exceptions-freq.test',
|
||||
]
|
||||
|
||||
|
||||
class TestExceptionTransform(MypycDataSuite):
|
||||
files = files
|
||||
base_path = test_temp_dir
|
||||
|
||||
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||||
"""Perform a runtime checking transformation test case."""
|
||||
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
|
||||
expected_output = remove_comment_lines(testcase.output)
|
||||
try:
|
||||
ir = build_ir_for_single_file(testcase.input)
|
||||
except CompileError as e:
|
||||
actual = e.messages
|
||||
else:
|
||||
actual = []
|
||||
for fn in ir:
|
||||
if (fn.name == TOP_LEVEL_NAME
|
||||
and not testcase.name.endswith('_toplevel')):
|
||||
continue
|
||||
insert_uninit_checks(fn)
|
||||
insert_exception_handling(fn)
|
||||
insert_ref_count_opcodes(fn)
|
||||
actual.extend(format_func(fn))
|
||||
if testcase.name.endswith('_freq'):
|
||||
common = frequently_executed_blocks(fn.blocks[0])
|
||||
actual.append('hot blocks: %s' % sorted(b.label for b in common))
|
||||
|
||||
assert_test_output(testcase, actual, 'Invalid source code output',
|
||||
expected_output)
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
"""Test cases that run tests as subprocesses."""
|
||||
|
||||
from typing import List
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
|
||||
base_dir = os.path.join(os.path.dirname(__file__), '..', '..')
|
||||
|
||||
|
||||
class TestExternal(unittest.TestCase):
|
||||
# TODO: Get this to work on Windows.
|
||||
# (Or don't. It is probably not a good use of time.)
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "rt tests don't work on windows")
|
||||
def test_c_unit_test(self) -> None:
|
||||
"""Run C unit tests in a subprocess."""
|
||||
# Build Google Test, the C++ framework we use for testing C code.
|
||||
# The source code for Google Test is copied to this repository.
|
||||
cppflags: List[str] = []
|
||||
env = os.environ.copy()
|
||||
if sys.platform == 'darwin':
|
||||
cppflags += ['-mmacosx-version-min=10.10', '-stdlib=libc++']
|
||||
env['CPPFLAGS'] = ' '.join(cppflags)
|
||||
subprocess.check_call(
|
||||
['make', 'libgtest.a'],
|
||||
env=env,
|
||||
cwd=os.path.join(base_dir, 'mypyc', 'external', 'googletest', 'make'))
|
||||
# Build Python wrapper for C unit tests.
|
||||
env = os.environ.copy()
|
||||
env['CPPFLAGS'] = ' '.join(cppflags)
|
||||
status = subprocess.check_call(
|
||||
[sys.executable, 'setup.py', 'build_ext', '--inplace'],
|
||||
env=env,
|
||||
cwd=os.path.join(base_dir, 'mypyc', 'lib-rt'))
|
||||
# Run C unit tests.
|
||||
env = os.environ.copy()
|
||||
if 'GTEST_COLOR' not in os.environ:
|
||||
env['GTEST_COLOR'] = 'yes' # Use fancy colors
|
||||
status = subprocess.call([sys.executable, '-c',
|
||||
'import sys, test_capi; sys.exit(test_capi.run_tests())'],
|
||||
env=env,
|
||||
cwd=os.path.join(base_dir, 'mypyc', 'lib-rt'))
|
||||
if status != 0:
|
||||
raise AssertionError("make test: C unit test failure")
|
||||
71
.venv/lib/python3.8/site-packages/mypyc/test/test_irbuild.py
Normal file
71
.venv/lib/python3.8/site-packages/mypyc/test/test_irbuild.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
"""Test cases for IR generation."""
|
||||
|
||||
import os.path
|
||||
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.test.data import DataDrivenTestCase
|
||||
from mypy.errors import CompileError
|
||||
|
||||
from mypyc.common import TOP_LEVEL_NAME
|
||||
from mypyc.ir.pprint import format_func
|
||||
from mypyc.test.testutil import (
|
||||
ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file,
|
||||
assert_test_output, remove_comment_lines, replace_word_size,
|
||||
infer_ir_build_options_from_test_name
|
||||
)
|
||||
|
||||
files = [
|
||||
'irbuild-basic.test',
|
||||
'irbuild-int.test',
|
||||
'irbuild-lists.test',
|
||||
'irbuild-tuple.test',
|
||||
'irbuild-dict.test',
|
||||
'irbuild-set.test',
|
||||
'irbuild-str.test',
|
||||
'irbuild-bytes.test',
|
||||
'irbuild-statements.test',
|
||||
'irbuild-nested.test',
|
||||
'irbuild-classes.test',
|
||||
'irbuild-optional.test',
|
||||
'irbuild-any.test',
|
||||
'irbuild-generics.test',
|
||||
'irbuild-try.test',
|
||||
'irbuild-strip-asserts.test',
|
||||
'irbuild-vectorcall.test',
|
||||
'irbuild-unreachable.test',
|
||||
'irbuild-isinstance.test',
|
||||
'irbuild-dunders.test',
|
||||
'irbuild-singledispatch.test',
|
||||
'irbuild-constant-fold.test',
|
||||
]
|
||||
|
||||
|
||||
class TestGenOps(MypycDataSuite):
|
||||
files = files
|
||||
base_path = test_temp_dir
|
||||
optional_out = True
|
||||
|
||||
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||||
"""Perform a runtime checking transformation test case."""
|
||||
options = infer_ir_build_options_from_test_name(testcase.name)
|
||||
if options is None:
|
||||
# Skipped test case
|
||||
return
|
||||
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
|
||||
expected_output = remove_comment_lines(testcase.output)
|
||||
expected_output = replace_word_size(expected_output)
|
||||
name = testcase.name
|
||||
try:
|
||||
ir = build_ir_for_single_file(testcase.input, options)
|
||||
except CompileError as e:
|
||||
actual = e.messages
|
||||
else:
|
||||
actual = []
|
||||
for fn in ir:
|
||||
if (fn.name == TOP_LEVEL_NAME
|
||||
and not name.endswith('_toplevel')):
|
||||
continue
|
||||
actual.extend(format_func(fn))
|
||||
|
||||
assert_test_output(testcase, actual, 'Invalid source code output',
|
||||
expected_output)
|
||||
216
.venv/lib/python3.8/site-packages/mypyc/test/test_ircheck.py
Normal file
216
.venv/lib/python3.8/site-packages/mypyc/test/test_ircheck.py
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import unittest
|
||||
from typing import List, Optional
|
||||
|
||||
from mypyc.analysis.ircheck import check_func_ir, FnError, can_coerce_to
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.rtypes import (
|
||||
none_rprimitive, str_rprimitive, int32_rprimitive, int64_rprimitive,
|
||||
RType, RUnion, bytes_rprimitive, RInstance, object_rprimitive
|
||||
)
|
||||
from mypyc.ir.ops import (
|
||||
BasicBlock, Op, Return, Integer, Goto, Register, LoadLiteral, Assign
|
||||
)
|
||||
from mypyc.ir.func_ir import FuncIR, FuncDecl, FuncSignature
|
||||
from mypyc.ir.pprint import format_func
|
||||
|
||||
|
||||
def assert_has_error(fn: FuncIR, error: FnError) -> None:
|
||||
errors = check_func_ir(fn)
|
||||
assert errors == [error]
|
||||
|
||||
|
||||
def assert_no_errors(fn: FuncIR) -> None:
|
||||
assert not check_func_ir(fn)
|
||||
|
||||
|
||||
NONE_VALUE = Integer(0, rtype=none_rprimitive)
|
||||
|
||||
|
||||
class TestIrcheck(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.label = 0
|
||||
|
||||
def basic_block(self, ops: List[Op]) -> BasicBlock:
|
||||
self.label += 1
|
||||
block = BasicBlock(self.label)
|
||||
block.ops = ops
|
||||
return block
|
||||
|
||||
def func_decl(self, name: str, ret_type: Optional[RType] = None) -> FuncDecl:
|
||||
if ret_type is None:
|
||||
ret_type = none_rprimitive
|
||||
return FuncDecl(
|
||||
name=name,
|
||||
class_name=None,
|
||||
module_name="module",
|
||||
sig=FuncSignature(
|
||||
args=[],
|
||||
ret_type=ret_type,
|
||||
),
|
||||
)
|
||||
|
||||
def test_valid_fn(self) -> None:
|
||||
assert_no_errors(
|
||||
FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[
|
||||
self.basic_block(
|
||||
ops=[
|
||||
Return(value=NONE_VALUE),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
def test_block_not_terminated_empty_block(self) -> None:
|
||||
block = self.basic_block([])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[block],
|
||||
)
|
||||
assert_has_error(fn, FnError(source=block, desc="Block not terminated"))
|
||||
|
||||
def test_valid_goto(self) -> None:
|
||||
block_1 = self.basic_block([Return(value=NONE_VALUE)])
|
||||
block_2 = self.basic_block([Goto(label=block_1)])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[block_1, block_2],
|
||||
)
|
||||
assert_no_errors(fn)
|
||||
|
||||
def test_invalid_goto(self) -> None:
|
||||
block_1 = self.basic_block([Return(value=NONE_VALUE)])
|
||||
goto = Goto(label=block_1)
|
||||
block_2 = self.basic_block([goto])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
# block_1 omitted
|
||||
blocks=[block_2],
|
||||
)
|
||||
assert_has_error(
|
||||
fn, FnError(source=goto, desc="Invalid control operation target: 1")
|
||||
)
|
||||
|
||||
def test_invalid_register_source(self) -> None:
|
||||
ret = Return(
|
||||
value=Register(
|
||||
type=none_rprimitive,
|
||||
name="r1",
|
||||
)
|
||||
)
|
||||
block = self.basic_block([ret])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[block],
|
||||
)
|
||||
assert_has_error(
|
||||
fn, FnError(source=ret, desc="Invalid op reference to register r1")
|
||||
)
|
||||
|
||||
def test_invalid_op_source(self) -> None:
|
||||
ret = Return(
|
||||
value=LoadLiteral(
|
||||
value="foo",
|
||||
rtype=str_rprimitive,
|
||||
)
|
||||
)
|
||||
block = self.basic_block([ret])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[block],
|
||||
)
|
||||
assert_has_error(
|
||||
fn,
|
||||
FnError(source=ret, desc="Invalid op reference to op of type LoadLiteral"),
|
||||
)
|
||||
|
||||
def test_invalid_return_type(self) -> None:
|
||||
ret = Return(value=Integer(value=5, rtype=int32_rprimitive))
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1", ret_type=int64_rprimitive),
|
||||
arg_regs=[],
|
||||
blocks=[self.basic_block([ret])],
|
||||
)
|
||||
assert_has_error(
|
||||
fn,
|
||||
FnError(
|
||||
source=ret, desc="Cannot coerce source type int32 to dest type int64"
|
||||
),
|
||||
)
|
||||
|
||||
def test_invalid_assign(self) -> None:
|
||||
arg_reg = Register(type=int64_rprimitive, name="r1")
|
||||
assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive))
|
||||
ret = Return(value=NONE_VALUE)
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[arg_reg],
|
||||
blocks=[self.basic_block([assign, ret])],
|
||||
)
|
||||
assert_has_error(
|
||||
fn,
|
||||
FnError(
|
||||
source=assign, desc="Cannot coerce source type int32 to dest type int64"
|
||||
),
|
||||
)
|
||||
|
||||
def test_can_coerce_to(self) -> None:
|
||||
cls = ClassIR(name="Cls", module_name="cls")
|
||||
valid_cases = [
|
||||
(int64_rprimitive, int64_rprimitive),
|
||||
(str_rprimitive, str_rprimitive),
|
||||
(str_rprimitive, object_rprimitive),
|
||||
(object_rprimitive, str_rprimitive),
|
||||
(RUnion([bytes_rprimitive, str_rprimitive]), str_rprimitive),
|
||||
(str_rprimitive, RUnion([bytes_rprimitive, str_rprimitive])),
|
||||
(RInstance(cls), object_rprimitive),
|
||||
]
|
||||
|
||||
invalid_cases = [
|
||||
(int64_rprimitive, int32_rprimitive),
|
||||
(RInstance(cls), str_rprimitive),
|
||||
(str_rprimitive, bytes_rprimitive),
|
||||
]
|
||||
|
||||
for src, dest in valid_cases:
|
||||
assert can_coerce_to(src, dest)
|
||||
for src, dest in invalid_cases:
|
||||
assert not can_coerce_to(src, dest)
|
||||
|
||||
def test_duplicate_op(self) -> None:
|
||||
arg_reg = Register(type=int32_rprimitive, name="r1")
|
||||
assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive))
|
||||
block = self.basic_block([assign, assign, Return(value=NONE_VALUE)])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[block],
|
||||
)
|
||||
assert_has_error(fn, FnError(source=assign, desc="Func has a duplicate op"))
|
||||
|
||||
def test_pprint(self) -> None:
|
||||
block_1 = self.basic_block([Return(value=NONE_VALUE)])
|
||||
goto = Goto(label=block_1)
|
||||
block_2 = self.basic_block([goto])
|
||||
fn = FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
# block_1 omitted
|
||||
blocks=[block_2],
|
||||
)
|
||||
errors = [(goto, "Invalid control operation target: 1")]
|
||||
formatted = format_func(fn, errors)
|
||||
assert formatted == [
|
||||
"def func_1():",
|
||||
"L0:",
|
||||
" goto L1",
|
||||
" ERR: Invalid control operation target: 1",
|
||||
]
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
"""Test code geneneration for literals."""
|
||||
|
||||
import unittest
|
||||
|
||||
from mypyc.codegen.literals import (
|
||||
Literals, format_str_literal, _encode_str_values, _encode_bytes_values, _encode_int_values
|
||||
)
|
||||
|
||||
|
||||
class TestLiterals(unittest.TestCase):
|
||||
def test_format_str_literal(self) -> None:
|
||||
assert format_str_literal('') == b'\x00'
|
||||
assert format_str_literal('xyz') == b'\x03xyz'
|
||||
assert format_str_literal('x' * 127) == b'\x7f' + b'x' * 127
|
||||
assert format_str_literal('x' * 128) == b'\x81\x00' + b'x' * 128
|
||||
assert format_str_literal('x' * 131) == b'\x81\x03' + b'x' * 131
|
||||
|
||||
def test_encode_str_values(self) -> None:
|
||||
assert _encode_str_values({}) == [b'']
|
||||
assert _encode_str_values({'foo': 0}) == [b'\x01\x03foo', b'']
|
||||
assert _encode_str_values({'foo': 0, 'b': 1}) == [b'\x02\x03foo\x01b', b'']
|
||||
assert _encode_str_values({'foo': 0, 'x' * 70: 1}) == [
|
||||
b'\x01\x03foo',
|
||||
bytes([1, 70]) + b'x' * 70,
|
||||
b''
|
||||
]
|
||||
assert _encode_str_values({'y' * 100: 0}) == [
|
||||
bytes([1, 100]) + b'y' * 100,
|
||||
b''
|
||||
]
|
||||
|
||||
def test_encode_bytes_values(self) -> None:
|
||||
assert _encode_bytes_values({}) == [b'']
|
||||
assert _encode_bytes_values({b'foo': 0}) == [b'\x01\x03foo', b'']
|
||||
assert _encode_bytes_values({b'foo': 0, b'b': 1}) == [b'\x02\x03foo\x01b', b'']
|
||||
assert _encode_bytes_values({b'foo': 0, b'x' * 70: 1}) == [
|
||||
b'\x01\x03foo',
|
||||
bytes([1, 70]) + b'x' * 70,
|
||||
b''
|
||||
]
|
||||
assert _encode_bytes_values({b'y' * 100: 0}) == [
|
||||
bytes([1, 100]) + b'y' * 100,
|
||||
b''
|
||||
]
|
||||
|
||||
def test_encode_int_values(self) -> None:
|
||||
assert _encode_int_values({}) == [b'']
|
||||
assert _encode_int_values({123: 0}) == [b'\x01123', b'']
|
||||
assert _encode_int_values({123: 0, 9: 1}) == [b'\x02123\x009', b'']
|
||||
assert _encode_int_values({123: 0, 45: 1, 5 * 10**70: 2}) == [
|
||||
b'\x02123\x0045',
|
||||
b'\x015' + b'0' * 70,
|
||||
b''
|
||||
]
|
||||
assert _encode_int_values({6 * 10**100: 0}) == [
|
||||
b'\x016' + b'0' * 100,
|
||||
b''
|
||||
]
|
||||
|
||||
def test_simple_literal_index(self) -> None:
|
||||
lit = Literals()
|
||||
lit.record_literal(1)
|
||||
lit.record_literal('y')
|
||||
lit.record_literal(True)
|
||||
lit.record_literal(None)
|
||||
lit.record_literal(False)
|
||||
assert lit.literal_index(None) == 0
|
||||
assert lit.literal_index(False) == 1
|
||||
assert lit.literal_index(True) == 2
|
||||
assert lit.literal_index('y') == 3
|
||||
assert lit.literal_index(1) == 4
|
||||
|
||||
def test_tuple_literal(self) -> None:
|
||||
lit = Literals()
|
||||
lit.record_literal((1, 'y', None, (b'a', 'b')))
|
||||
lit.record_literal((b'a', 'b'))
|
||||
lit.record_literal(())
|
||||
assert lit.literal_index((b'a', 'b')) == 7
|
||||
assert lit.literal_index((1, 'y', None, (b'a', 'b'))) == 8
|
||||
assert lit.literal_index(()) == 9
|
||||
print(lit.encoded_tuple_values())
|
||||
assert lit.encoded_tuple_values() == [
|
||||
'3', # Number of tuples
|
||||
'2', '5', '4', # First tuple (length=2)
|
||||
'4', '6', '3', '0', '7', # Second tuple (length=4)
|
||||
'0', # Third tuple (length=0)
|
||||
]
|
||||
40
.venv/lib/python3.8/site-packages/mypyc/test/test_namegen.py
Normal file
40
.venv/lib/python3.8/site-packages/mypyc/test/test_namegen.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import unittest
|
||||
|
||||
from mypyc.namegen import (
|
||||
NameGenerator, exported_name, candidate_suffixes, make_module_translation_map
|
||||
)
|
||||
|
||||
|
||||
class TestNameGen(unittest.TestCase):
|
||||
def test_candidate_suffixes(self) -> None:
|
||||
assert candidate_suffixes('foo') == ['', 'foo.']
|
||||
assert candidate_suffixes('foo.bar') == ['', 'bar.', 'foo.bar.']
|
||||
|
||||
def test_exported_name(self) -> None:
|
||||
assert exported_name('foo') == 'foo'
|
||||
assert exported_name('foo.bar') == 'foo___bar'
|
||||
|
||||
def test_make_module_translation_map(self) -> None:
|
||||
assert make_module_translation_map(
|
||||
['foo', 'bar']) == {'foo': 'foo.', 'bar': 'bar.'}
|
||||
assert make_module_translation_map(
|
||||
['foo.bar', 'foo.baz']) == {'foo.bar': 'bar.', 'foo.baz': 'baz.'}
|
||||
assert make_module_translation_map(
|
||||
['zar', 'foo.bar', 'foo.baz']) == {'foo.bar': 'bar.',
|
||||
'foo.baz': 'baz.',
|
||||
'zar': 'zar.'}
|
||||
assert make_module_translation_map(
|
||||
['foo.bar', 'fu.bar', 'foo.baz']) == {'foo.bar': 'foo.bar.',
|
||||
'fu.bar': 'fu.bar.',
|
||||
'foo.baz': 'baz.'}
|
||||
|
||||
def test_name_generator(self) -> None:
|
||||
g = NameGenerator([['foo', 'foo.zar']])
|
||||
assert g.private_name('foo', 'f') == 'foo___f'
|
||||
assert g.private_name('foo', 'C.x.y') == 'foo___C___x___y'
|
||||
assert g.private_name('foo', 'C.x.y') == 'foo___C___x___y'
|
||||
assert g.private_name('foo.zar', 'C.x.y') == 'zar___C___x___y'
|
||||
assert g.private_name('foo', 'C.x_y') == 'foo___C___x_y'
|
||||
assert g.private_name('foo', 'C_x_y') == 'foo___C_x_y'
|
||||
assert g.private_name('foo', 'C_x_y') == 'foo___C_x_y'
|
||||
assert g.private_name('foo', '___') == 'foo______3_'
|
||||
41
.venv/lib/python3.8/site-packages/mypyc/test/test_pprint.py
Normal file
41
.venv/lib/python3.8/site-packages/mypyc/test/test_pprint.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import unittest
|
||||
from typing import List
|
||||
|
||||
from mypyc.ir.ops import BasicBlock, Register, Op, Integer, IntOp, Unreachable, Assign
|
||||
from mypyc.ir.rtypes import int_rprimitive
|
||||
from mypyc.ir.pprint import generate_names_for_ir
|
||||
|
||||
|
||||
def register(name: str) -> Register:
|
||||
return Register(int_rprimitive, 'foo', is_arg=True)
|
||||
|
||||
|
||||
def make_block(ops: List[Op]) -> BasicBlock:
|
||||
block = BasicBlock()
|
||||
block.ops.extend(ops)
|
||||
return block
|
||||
|
||||
|
||||
class TestGenerateNames(unittest.TestCase):
|
||||
def test_empty(self) -> None:
|
||||
assert generate_names_for_ir([], []) == {}
|
||||
|
||||
def test_arg(self) -> None:
|
||||
reg = register('foo')
|
||||
assert generate_names_for_ir([reg], []) == {reg: 'foo'}
|
||||
|
||||
def test_int_op(self) -> None:
|
||||
n1 = Integer(2)
|
||||
n2 = Integer(4)
|
||||
op1 = IntOp(int_rprimitive, n1, n2, IntOp.ADD)
|
||||
op2 = IntOp(int_rprimitive, op1, n2, IntOp.ADD)
|
||||
block = make_block([op1, op2, Unreachable()])
|
||||
assert generate_names_for_ir([], [block]) == {op1: 'r0', op2: 'r1'}
|
||||
|
||||
def test_assign(self) -> None:
|
||||
reg = register('foo')
|
||||
n = Integer(2)
|
||||
op1 = Assign(reg, n)
|
||||
op2 = Assign(reg, n)
|
||||
block = make_block([op1, op2])
|
||||
assert generate_names_for_ir([reg], [block]) == {reg: 'foo'}
|
||||
42
.venv/lib/python3.8/site-packages/mypyc/test/test_rarray.py
Normal file
42
.venv/lib/python3.8/site-packages/mypyc/test/test_rarray.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
"""Unit tests for RArray types."""
|
||||
|
||||
import unittest
|
||||
|
||||
from mypyc.common import PLATFORM_SIZE
|
||||
from mypyc.ir.rtypes import (
|
||||
RArray, int_rprimitive, bool_rprimitive, compute_rtype_alignment, compute_rtype_size
|
||||
)
|
||||
|
||||
|
||||
class TestRArray(unittest.TestCase):
|
||||
def test_basics(self) -> None:
|
||||
a = RArray(int_rprimitive, 10)
|
||||
assert a.item_type == int_rprimitive
|
||||
assert a.length == 10
|
||||
|
||||
def test_str_conversion(self) -> None:
|
||||
a = RArray(int_rprimitive, 10)
|
||||
assert str(a) == 'int[10]'
|
||||
assert repr(a) == '<RArray <RPrimitive builtins.int>[10]>'
|
||||
|
||||
def test_eq(self) -> None:
|
||||
a = RArray(int_rprimitive, 10)
|
||||
assert a == RArray(int_rprimitive, 10)
|
||||
assert a != RArray(bool_rprimitive, 10)
|
||||
assert a != RArray(int_rprimitive, 9)
|
||||
|
||||
def test_hash(self) -> None:
|
||||
assert hash(RArray(int_rprimitive, 10)) == hash(RArray(int_rprimitive, 10))
|
||||
assert hash(RArray(bool_rprimitive, 5)) == hash(RArray(bool_rprimitive, 5))
|
||||
|
||||
def test_alignment(self) -> None:
|
||||
a = RArray(int_rprimitive, 10)
|
||||
assert compute_rtype_alignment(a) == PLATFORM_SIZE
|
||||
b = RArray(bool_rprimitive, 55)
|
||||
assert compute_rtype_alignment(b) == 1
|
||||
|
||||
def test_size(self) -> None:
|
||||
a = RArray(int_rprimitive, 9)
|
||||
assert compute_rtype_size(a) == 9 * PLATFORM_SIZE
|
||||
b = RArray(bool_rprimitive, 3)
|
||||
assert compute_rtype_size(b) == 3
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
"""Test runner for reference count opcode insertion transform test cases.
|
||||
|
||||
The transform inserts needed reference count increment/decrement
|
||||
operations to IR.
|
||||
"""
|
||||
|
||||
import os.path
|
||||
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.test.data import DataDrivenTestCase
|
||||
from mypy.errors import CompileError
|
||||
|
||||
from mypyc.common import TOP_LEVEL_NAME
|
||||
from mypyc.ir.pprint import format_func
|
||||
from mypyc.transform.refcount import insert_ref_count_opcodes
|
||||
from mypyc.transform.uninit import insert_uninit_checks
|
||||
from mypyc.test.testutil import (
|
||||
ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file,
|
||||
assert_test_output, remove_comment_lines, replace_word_size,
|
||||
infer_ir_build_options_from_test_name
|
||||
)
|
||||
|
||||
files = [
|
||||
'refcount.test'
|
||||
]
|
||||
|
||||
|
||||
class TestRefCountTransform(MypycDataSuite):
|
||||
files = files
|
||||
base_path = test_temp_dir
|
||||
optional_out = True
|
||||
|
||||
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||||
"""Perform a runtime checking transformation test case."""
|
||||
options = infer_ir_build_options_from_test_name(testcase.name)
|
||||
if options is None:
|
||||
# Skipped test case
|
||||
return
|
||||
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
|
||||
expected_output = remove_comment_lines(testcase.output)
|
||||
expected_output = replace_word_size(expected_output)
|
||||
try:
|
||||
ir = build_ir_for_single_file(testcase.input, options)
|
||||
except CompileError as e:
|
||||
actual = e.messages
|
||||
else:
|
||||
actual = []
|
||||
for fn in ir:
|
||||
if (fn.name == TOP_LEVEL_NAME
|
||||
and not testcase.name.endswith('_toplevel')):
|
||||
continue
|
||||
insert_uninit_checks(fn)
|
||||
insert_ref_count_opcodes(fn)
|
||||
actual.extend(format_func(fn))
|
||||
|
||||
assert_test_output(testcase, actual, 'Invalid source code output',
|
||||
expected_output)
|
||||
410
.venv/lib/python3.8/site-packages/mypyc/test/test_run.py
Normal file
410
.venv/lib/python3.8/site-packages/mypyc/test/test_run.py
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
"""Test cases for building an C extension and running it."""
|
||||
|
||||
import ast
|
||||
import glob
|
||||
import os.path
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import contextlib
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Any, Iterator, List, cast
|
||||
|
||||
from mypy import build
|
||||
from mypy.test.data import DataDrivenTestCase
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.errors import CompileError
|
||||
from mypy.options import Options
|
||||
from mypy.test.helpers import assert_module_equivalence, perform_file_operations
|
||||
|
||||
from mypyc.codegen import emitmodule
|
||||
from mypyc.options import CompilerOptions
|
||||
from mypyc.errors import Errors
|
||||
from mypyc.build import construct_groups
|
||||
from mypyc.test.testutil import (
|
||||
ICODE_GEN_BUILTINS, TESTUTIL_PATH,
|
||||
use_custom_builtins, MypycDataSuite, assert_test_output,
|
||||
show_c, fudge_dir_mtimes,
|
||||
)
|
||||
from mypyc.test.test_serialization import check_serialization_roundtrip
|
||||
|
||||
files = [
|
||||
'run-misc.test',
|
||||
'run-functions.test',
|
||||
'run-integers.test',
|
||||
'run-floats.test',
|
||||
'run-bools.test',
|
||||
'run-strings.test',
|
||||
'run-bytes.test',
|
||||
'run-tuples.test',
|
||||
'run-lists.test',
|
||||
'run-dicts.test',
|
||||
'run-sets.test',
|
||||
'run-primitives.test',
|
||||
'run-loops.test',
|
||||
'run-exceptions.test',
|
||||
'run-imports.test',
|
||||
'run-classes.test',
|
||||
'run-traits.test',
|
||||
'run-generators.test',
|
||||
'run-multimodule.test',
|
||||
'run-bench.test',
|
||||
'run-mypy-sim.test',
|
||||
'run-dunders.test',
|
||||
'run-singledispatch.test',
|
||||
'run-attrs.test',
|
||||
]
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
files.append('run-python37.test')
|
||||
if sys.version_info >= (3, 8):
|
||||
files.append('run-python38.test')
|
||||
|
||||
setup_format = """\
|
||||
from setuptools import setup
|
||||
from mypyc.build import mypycify
|
||||
|
||||
setup(name='test_run_output',
|
||||
ext_modules=mypycify({}, separate={}, skip_cgen_input={!r}, strip_asserts=False,
|
||||
multi_file={}, opt_level='{}'),
|
||||
)
|
||||
"""
|
||||
|
||||
WORKDIR = 'build'
|
||||
|
||||
|
||||
def run_setup(script_name: str, script_args: List[str]) -> bool:
|
||||
"""Run a setup script in a somewhat controlled environment.
|
||||
|
||||
This is adapted from code in distutils and our goal here is that is
|
||||
faster to not need to spin up a python interpreter to run it.
|
||||
|
||||
We had to fork it because the real run_setup swallows errors
|
||||
and KeyboardInterrupt with no way to recover them (!).
|
||||
The real version has some extra features that we removed since
|
||||
we weren't using them.
|
||||
|
||||
Returns whether the setup succeeded.
|
||||
"""
|
||||
save_argv = sys.argv.copy()
|
||||
g = {'__file__': script_name}
|
||||
try:
|
||||
try:
|
||||
sys.argv[0] = script_name
|
||||
sys.argv[1:] = script_args
|
||||
with open(script_name, 'rb') as f:
|
||||
exec(f.read(), g)
|
||||
finally:
|
||||
sys.argv = save_argv
|
||||
except SystemExit as e:
|
||||
# typeshed reports code as being an int but that is wrong
|
||||
code = cast(Any, e).code
|
||||
# distutils converts KeyboardInterrupt into a SystemExit with
|
||||
# "interrupted" as the argument. Convert it back so that
|
||||
# pytest will exit instead of just failing the test.
|
||||
if code == "interrupted":
|
||||
raise KeyboardInterrupt from e
|
||||
|
||||
return code == 0 or code is None
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def chdir_manager(target: str) -> Iterator[None]:
|
||||
dir = os.getcwd()
|
||||
os.chdir(target)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(dir)
|
||||
|
||||
|
||||
class TestRun(MypycDataSuite):
|
||||
"""Test cases that build a C extension and run code."""
|
||||
files = files
|
||||
base_path = test_temp_dir
|
||||
optional_out = True
|
||||
multi_file = False
|
||||
separate = False # If True, using separate (incremental) compilation
|
||||
|
||||
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||||
# setup.py wants to be run from the root directory of the package, which we accommodate
|
||||
# by chdiring into tmp/
|
||||
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase), (
|
||||
chdir_manager('tmp')):
|
||||
self.run_case_inner(testcase)
|
||||
|
||||
def run_case_inner(self, testcase: DataDrivenTestCase) -> None:
|
||||
if not os.path.isdir(WORKDIR): # (one test puts something in build...)
|
||||
os.mkdir(WORKDIR)
|
||||
|
||||
text = '\n'.join(testcase.input)
|
||||
|
||||
with open('native.py', 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
with open('interpreted.py', 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
|
||||
shutil.copyfile(TESTUTIL_PATH, 'testutil.py')
|
||||
|
||||
step = 1
|
||||
self.run_case_step(testcase, step)
|
||||
|
||||
steps = testcase.find_steps()
|
||||
if steps == [[]]:
|
||||
steps = []
|
||||
|
||||
for operations in steps:
|
||||
# To make sure that any new changes get picked up as being
|
||||
# new by distutils, shift the mtime of all of the
|
||||
# generated artifacts back by a second.
|
||||
fudge_dir_mtimes(WORKDIR, -1)
|
||||
|
||||
step += 1
|
||||
with chdir_manager('..'):
|
||||
perform_file_operations(operations)
|
||||
self.run_case_step(testcase, step)
|
||||
|
||||
def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> None:
|
||||
bench = testcase.config.getoption('--bench', False) and 'Benchmark' in testcase.name
|
||||
|
||||
options = Options()
|
||||
options.use_builtins_fixtures = True
|
||||
options.show_traceback = True
|
||||
options.strict_optional = True
|
||||
# N.B: We try to (and ought to!) run with the current
|
||||
# version of python, since we are going to link and run
|
||||
# against the current version of python.
|
||||
# But a lot of the tests use type annotations so we can't say it is 3.5.
|
||||
options.python_version = max(sys.version_info[:2], (3, 6))
|
||||
options.export_types = True
|
||||
options.preserve_asts = True
|
||||
options.incremental = self.separate
|
||||
|
||||
# Avoid checking modules/packages named 'unchecked', to provide a way
|
||||
# to test interacting with code we don't have types for.
|
||||
options.per_module_options['unchecked.*'] = {'follow_imports': 'error'}
|
||||
|
||||
source = build.BuildSource('native.py', 'native', None)
|
||||
sources = [source]
|
||||
module_names = ['native']
|
||||
module_paths = ['native.py']
|
||||
|
||||
# Hard code another module name to compile in the same compilation unit.
|
||||
to_delete = []
|
||||
for fn, text in testcase.files:
|
||||
fn = os.path.relpath(fn, test_temp_dir)
|
||||
|
||||
if os.path.basename(fn).startswith('other') and fn.endswith('.py'):
|
||||
name = fn.split('.')[0].replace(os.sep, '.')
|
||||
module_names.append(name)
|
||||
sources.append(build.BuildSource(fn, name, None))
|
||||
to_delete.append(fn)
|
||||
module_paths.append(fn)
|
||||
|
||||
shutil.copyfile(fn,
|
||||
os.path.join(os.path.dirname(fn), name + '_interpreted.py'))
|
||||
|
||||
for source in sources:
|
||||
options.per_module_options.setdefault(source.module, {})['mypyc'] = True
|
||||
|
||||
separate = (self.get_separate('\n'.join(testcase.input), incremental_step) if self.separate
|
||||
else False)
|
||||
|
||||
groups = construct_groups(sources, separate, len(module_names) > 1)
|
||||
|
||||
try:
|
||||
compiler_options = CompilerOptions(multi_file=self.multi_file, separate=self.separate)
|
||||
result = emitmodule.parse_and_typecheck(
|
||||
sources=sources,
|
||||
options=options,
|
||||
compiler_options=compiler_options,
|
||||
groups=groups,
|
||||
alt_lib_path='.')
|
||||
errors = Errors()
|
||||
ir, cfiles = emitmodule.compile_modules_to_c(
|
||||
result,
|
||||
compiler_options=compiler_options,
|
||||
errors=errors,
|
||||
groups=groups,
|
||||
)
|
||||
if errors.num_errors:
|
||||
errors.flush_errors()
|
||||
assert False, "Compile error"
|
||||
except CompileError as e:
|
||||
for line in e.messages:
|
||||
print(fix_native_line_number(line, testcase.file, testcase.line))
|
||||
assert False, 'Compile error'
|
||||
|
||||
# Check that serialization works on this IR. (Only on the first
|
||||
# step because the the returned ir only includes updated code.)
|
||||
if incremental_step == 1:
|
||||
check_serialization_roundtrip(ir)
|
||||
|
||||
opt_level = int(os.environ.get('MYPYC_OPT_LEVEL', 0))
|
||||
debug_level = int(os.environ.get('MYPYC_DEBUG_LEVEL', 0))
|
||||
|
||||
setup_file = os.path.abspath(os.path.join(WORKDIR, 'setup.py'))
|
||||
# We pass the C file information to the build script via setup.py unfortunately
|
||||
with open(setup_file, 'w', encoding='utf-8') as f:
|
||||
f.write(setup_format.format(module_paths,
|
||||
separate,
|
||||
cfiles,
|
||||
self.multi_file,
|
||||
opt_level,
|
||||
debug_level))
|
||||
|
||||
if not run_setup(setup_file, ['build_ext', '--inplace']):
|
||||
if testcase.config.getoption('--mypyc-showc'):
|
||||
show_c(cfiles)
|
||||
assert False, "Compilation failed"
|
||||
|
||||
# Assert that an output file got created
|
||||
suffix = 'pyd' if sys.platform == 'win32' else 'so'
|
||||
assert glob.glob('native.*.{}'.format(suffix))
|
||||
|
||||
driver_path = 'driver.py'
|
||||
if not os.path.isfile(driver_path):
|
||||
# No driver.py provided by test case. Use the default one
|
||||
# (mypyc/test-data/driver/driver.py) that calls each
|
||||
# function named test_*.
|
||||
default_driver = os.path.join(
|
||||
os.path.dirname(__file__), '..', 'test-data', 'driver', 'driver.py')
|
||||
shutil.copy(default_driver, driver_path)
|
||||
env = os.environ.copy()
|
||||
env['MYPYC_RUN_BENCH'] = '1' if bench else '0'
|
||||
|
||||
# XXX: This is an ugly hack.
|
||||
if 'MYPYC_RUN_GDB' in os.environ:
|
||||
if platform.system() == 'Darwin':
|
||||
subprocess.check_call(['lldb', '--', sys.executable, driver_path], env=env)
|
||||
assert False, ("Test can't pass in lldb mode. (And remember to pass -s to "
|
||||
"pytest)")
|
||||
elif platform.system() == 'Linux':
|
||||
subprocess.check_call(['gdb', '--args', sys.executable, driver_path], env=env)
|
||||
assert False, ("Test can't pass in gdb mode. (And remember to pass -s to "
|
||||
"pytest)")
|
||||
else:
|
||||
assert False, 'Unsupported OS'
|
||||
|
||||
proc = subprocess.Popen([sys.executable, driver_path], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, env=env)
|
||||
output = proc.communicate()[0].decode('utf8')
|
||||
outlines = output.splitlines()
|
||||
|
||||
if testcase.config.getoption('--mypyc-showc'):
|
||||
show_c(cfiles)
|
||||
if proc.returncode != 0:
|
||||
print()
|
||||
print('*** Exit status: %d' % proc.returncode)
|
||||
|
||||
# Verify output.
|
||||
if bench:
|
||||
print('Test output:')
|
||||
print(output)
|
||||
else:
|
||||
if incremental_step == 1:
|
||||
msg = 'Invalid output'
|
||||
expected = testcase.output
|
||||
else:
|
||||
msg = 'Invalid output (step {})'.format(incremental_step)
|
||||
expected = testcase.output2.get(incremental_step, [])
|
||||
|
||||
if not expected:
|
||||
# Tweak some line numbers, but only if the expected output is empty,
|
||||
# as tweaked output might not match expected output.
|
||||
outlines = [fix_native_line_number(line, testcase.file, testcase.line)
|
||||
for line in outlines]
|
||||
assert_test_output(testcase, outlines, msg, expected)
|
||||
|
||||
if incremental_step > 1 and options.incremental:
|
||||
suffix = '' if incremental_step == 2 else str(incremental_step - 1)
|
||||
expected_rechecked = testcase.expected_rechecked_modules.get(incremental_step - 1)
|
||||
if expected_rechecked is not None:
|
||||
assert_module_equivalence(
|
||||
'rechecked' + suffix,
|
||||
expected_rechecked, result.manager.rechecked_modules)
|
||||
expected_stale = testcase.expected_stale_modules.get(incremental_step - 1)
|
||||
if expected_stale is not None:
|
||||
assert_module_equivalence(
|
||||
'stale' + suffix,
|
||||
expected_stale, result.manager.stale_modules)
|
||||
|
||||
assert proc.returncode == 0
|
||||
|
||||
def get_separate(self, program_text: str,
|
||||
incremental_step: int) -> Any:
|
||||
template = r'# separate{}: (\[.*\])$'
|
||||
m = re.search(template.format(incremental_step), program_text, flags=re.MULTILINE)
|
||||
if not m:
|
||||
m = re.search(template.format(''), program_text, flags=re.MULTILINE)
|
||||
if m:
|
||||
return ast.literal_eval(m.group(1))
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class TestRunMultiFile(TestRun):
|
||||
"""Run the main multi-module tests in multi-file compilation mode.
|
||||
|
||||
In multi-file mode each module gets compiled into a separate C file,
|
||||
but all modules (C files) are compiled together.
|
||||
"""
|
||||
|
||||
multi_file = True
|
||||
test_name_suffix = '_multi'
|
||||
files = [
|
||||
'run-multimodule.test',
|
||||
'run-mypy-sim.test',
|
||||
]
|
||||
|
||||
|
||||
class TestRunSeparate(TestRun):
|
||||
"""Run the main multi-module tests in separate compilation mode.
|
||||
|
||||
In this mode there are multiple compilation groups, which are compiled
|
||||
incrementally. Each group is compiled to a separate C file, and these C
|
||||
files are compiled separately.
|
||||
|
||||
Each compiled module is placed into a separate compilation group, unless
|
||||
overridden by a special comment. Consider this example:
|
||||
|
||||
# separate: [(["other.py", "other_b.py"], "stuff")]
|
||||
|
||||
This puts other.py and other_b.py into a compilation group named "stuff".
|
||||
Any files not mentioned in the comment will get single-file groups.
|
||||
"""
|
||||
|
||||
separate = True
|
||||
test_name_suffix = '_separate'
|
||||
files = [
|
||||
'run-multimodule.test',
|
||||
'run-mypy-sim.test',
|
||||
]
|
||||
|
||||
|
||||
def fix_native_line_number(message: str, fnam: str, delta: int) -> str:
|
||||
"""Update code locations in test case output to point to the .test file.
|
||||
|
||||
The description of the test case is written to native.py, and line numbers
|
||||
in test case output often are relative to native.py. This translates the
|
||||
line numbers to be relative to the .test file that contains the test case
|
||||
description, and also updates the file name to the .test file name.
|
||||
|
||||
Args:
|
||||
message: message to update
|
||||
fnam: path of the .test file
|
||||
delta: line number of the beginning of the test case in the .test file
|
||||
|
||||
Returns updated message (or original message if we couldn't find anything).
|
||||
"""
|
||||
fnam = os.path.basename(fnam)
|
||||
message = re.sub(r'native\.py:([0-9]+):',
|
||||
lambda m: '%s:%d:' % (fnam, int(m.group(1)) + delta),
|
||||
message)
|
||||
message = re.sub(r'"native.py", line ([0-9]+),',
|
||||
lambda m: '"%s", line %d,' % (fnam, int(m.group(1)) + delta),
|
||||
message)
|
||||
return message
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
"""Functions to check that serialization round-tripped properly."""
|
||||
|
||||
# This file is named test_serialization.py even though it doesn't
|
||||
# contain its own tests so that pytest will rewrite the asserts...
|
||||
|
||||
from typing import Any, Dict, Tuple
|
||||
from mypy.backports import OrderedDict
|
||||
from collections.abc import Iterable
|
||||
|
||||
from mypyc.ir.ops import DeserMaps
|
||||
from mypyc.ir.rtypes import RType
|
||||
from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.module_ir import ModuleIR, deserialize_modules
|
||||
from mypyc.sametype import is_same_type, is_same_signature
|
||||
|
||||
|
||||
def get_dict(x: Any) -> Dict[str, Any]:
|
||||
if hasattr(x, '__mypyc_attrs__'):
|
||||
return {k: getattr(x, k) for k in x.__mypyc_attrs__ if hasattr(x, k)}
|
||||
else:
|
||||
return dict(x.__dict__)
|
||||
|
||||
|
||||
def get_function_dict(x: FuncIR) -> Dict[str, Any]:
|
||||
"""Get a dict of function attributes safe to compare across serialization"""
|
||||
d = get_dict(x)
|
||||
d.pop('blocks', None)
|
||||
d.pop('env', None)
|
||||
return d
|
||||
|
||||
|
||||
def assert_blobs_same(x: Any, y: Any, trail: Tuple[Any, ...]) -> None:
|
||||
"""Compare two blobs of IR as best we can.
|
||||
|
||||
FuncDecls, FuncIRs, and ClassIRs are compared by fullname to avoid
|
||||
infinite recursion.
|
||||
(More detailed comparisons should be done manually.)
|
||||
|
||||
Types and signatures are compared using mypyc.sametype.
|
||||
|
||||
Containers are compared recursively.
|
||||
|
||||
Anything else is compared with ==.
|
||||
|
||||
The `trail` argument is used in error messages.
|
||||
"""
|
||||
|
||||
assert type(x) is type(y), ("Type mismatch at {}".format(trail), type(x), type(y))
|
||||
if isinstance(x, (FuncDecl, FuncIR, ClassIR)):
|
||||
assert x.fullname == y.fullname, "Name mismatch at {}".format(trail)
|
||||
elif isinstance(x, OrderedDict):
|
||||
assert len(x.keys()) == len(y.keys()), "Keys mismatch at {}".format(trail)
|
||||
for (xk, xv), (yk, yv) in zip(x.items(), y.items()):
|
||||
assert_blobs_same(xk, yk, trail + ("keys",))
|
||||
assert_blobs_same(xv, yv, trail + (xk,))
|
||||
elif isinstance(x, dict):
|
||||
assert x.keys() == y.keys(), "Keys mismatch at {}".format(trail)
|
||||
for k in x.keys():
|
||||
assert_blobs_same(x[k], y[k], trail + (k,))
|
||||
elif isinstance(x, Iterable) and not isinstance(x, str):
|
||||
for i, (xv, yv) in enumerate(zip(x, y)):
|
||||
assert_blobs_same(xv, yv, trail + (i,))
|
||||
elif isinstance(x, RType):
|
||||
assert is_same_type(x, y), "RType mismatch at {}".format(trail)
|
||||
elif isinstance(x, FuncSignature):
|
||||
assert is_same_signature(x, y), "Signature mismatch at {}".format(trail)
|
||||
else:
|
||||
assert x == y, "Value mismatch at {}".format(trail)
|
||||
|
||||
|
||||
def assert_modules_same(ir1: ModuleIR, ir2: ModuleIR) -> None:
|
||||
"""Assert that two module IRs are the same (*).
|
||||
|
||||
* Or rather, as much as we care about preserving across
|
||||
serialization. We drop the actual IR bodies of functions but try
|
||||
to preserve everything else.
|
||||
"""
|
||||
assert ir1.fullname == ir2.fullname
|
||||
|
||||
assert ir1.imports == ir2.imports
|
||||
|
||||
for cls1, cls2 in zip(ir1.classes, ir2.classes):
|
||||
assert_blobs_same(get_dict(cls1), get_dict(cls2), (ir1.fullname, cls1.fullname))
|
||||
|
||||
for fn1, fn2 in zip(ir1.functions, ir2.functions):
|
||||
assert_blobs_same(get_function_dict(fn1), get_function_dict(fn2),
|
||||
(ir1.fullname, fn1.fullname))
|
||||
assert_blobs_same(get_dict(fn1.decl), get_dict(fn2.decl),
|
||||
(ir1.fullname, fn1.fullname))
|
||||
|
||||
assert_blobs_same(ir1.final_names, ir2.final_names, (ir1.fullname, 'final_names'))
|
||||
|
||||
|
||||
def check_serialization_roundtrip(irs: Dict[str, ModuleIR]) -> None:
|
||||
"""Check that we can serialize modules out and deserialize them to the same thing."""
|
||||
serialized = {k: ir.serialize() for k, ir in irs.items()}
|
||||
|
||||
ctx = DeserMaps({}, {})
|
||||
irs2 = deserialize_modules(serialized, ctx)
|
||||
assert irs.keys() == irs2.keys()
|
||||
|
||||
for k in irs:
|
||||
assert_modules_same(irs[k], irs2[k])
|
||||
115
.venv/lib/python3.8/site-packages/mypyc/test/test_struct.py
Normal file
115
.venv/lib/python3.8/site-packages/mypyc/test/test_struct.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import unittest
|
||||
|
||||
from mypyc.ir.rtypes import (
|
||||
RStruct, bool_rprimitive, int64_rprimitive, int32_rprimitive, object_rprimitive,
|
||||
int_rprimitive
|
||||
)
|
||||
from mypyc.rt_subtype import is_runtime_subtype
|
||||
|
||||
|
||||
class TestStruct(unittest.TestCase):
|
||||
def test_struct_offsets(self) -> None:
|
||||
# test per-member alignment
|
||||
r = RStruct("", [], [bool_rprimitive, int32_rprimitive, int64_rprimitive])
|
||||
assert r.size == 16
|
||||
assert r.offsets == [0, 4, 8]
|
||||
|
||||
# test final alignment
|
||||
r1 = RStruct("", [], [bool_rprimitive, bool_rprimitive])
|
||||
assert r1.size == 2
|
||||
assert r1.offsets == [0, 1]
|
||||
r2 = RStruct("", [], [int32_rprimitive, bool_rprimitive])
|
||||
r3 = RStruct("", [], [int64_rprimitive, bool_rprimitive])
|
||||
assert r2.offsets == [0, 4]
|
||||
assert r3.offsets == [0, 8]
|
||||
assert r2.size == 8
|
||||
assert r3.size == 16
|
||||
|
||||
r4 = RStruct("", [], [bool_rprimitive, bool_rprimitive,
|
||||
bool_rprimitive, int32_rprimitive])
|
||||
assert r4.size == 8
|
||||
assert r4.offsets == [0, 1, 2, 4]
|
||||
|
||||
# test nested struct
|
||||
r5 = RStruct("", [], [bool_rprimitive, r])
|
||||
assert r5.offsets == [0, 8]
|
||||
assert r5.size == 24
|
||||
r6 = RStruct("", [], [int32_rprimitive, r5])
|
||||
assert r6.offsets == [0, 8]
|
||||
assert r6.size == 32
|
||||
# test nested struct with alignment less than 8
|
||||
r7 = RStruct("", [], [bool_rprimitive, r4])
|
||||
assert r7.offsets == [0, 4]
|
||||
assert r7.size == 12
|
||||
|
||||
def test_struct_str(self) -> None:
|
||||
r = RStruct("Foo", ["a", "b"],
|
||||
[bool_rprimitive, object_rprimitive])
|
||||
assert str(r) == "Foo{a:bool, b:object}"
|
||||
assert repr(r) == "<RStruct Foo{a:<RPrimitive builtins.bool>, " \
|
||||
"b:<RPrimitive builtins.object>}>"
|
||||
r1 = RStruct("Bar", ["c"], [int32_rprimitive])
|
||||
assert str(r1) == "Bar{c:int32}"
|
||||
assert repr(r1) == "<RStruct Bar{c:<RPrimitive int32>}>"
|
||||
r2 = RStruct("Baz", [], [])
|
||||
assert str(r2) == "Baz{}"
|
||||
assert repr(r2) == "<RStruct Baz{}>"
|
||||
|
||||
def test_runtime_subtype(self) -> None:
|
||||
# right type to check with
|
||||
r = RStruct("Foo", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
|
||||
# using the exact same fields
|
||||
r1 = RStruct("Foo", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
|
||||
# names different
|
||||
r2 = RStruct("Bar", ["c", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
|
||||
# name different
|
||||
r3 = RStruct("Baz", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
|
||||
# type different
|
||||
r4 = RStruct("FooBar", ["a", "b"],
|
||||
[bool_rprimitive, int32_rprimitive])
|
||||
|
||||
# number of types different
|
||||
r5 = RStruct("FooBarBaz", ["a", "b", "c"],
|
||||
[bool_rprimitive, int_rprimitive, bool_rprimitive])
|
||||
|
||||
assert is_runtime_subtype(r1, r) is True
|
||||
assert is_runtime_subtype(r2, r) is False
|
||||
assert is_runtime_subtype(r3, r) is False
|
||||
assert is_runtime_subtype(r4, r) is False
|
||||
assert is_runtime_subtype(r5, r) is False
|
||||
|
||||
def test_eq_and_hash(self) -> None:
|
||||
r = RStruct("Foo", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
|
||||
# using the exact same fields
|
||||
r1 = RStruct("Foo", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
assert hash(r) == hash(r1)
|
||||
assert r == r1
|
||||
|
||||
# different name
|
||||
r2 = RStruct("Foq", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
assert hash(r) != hash(r2)
|
||||
assert r != r2
|
||||
|
||||
# different names
|
||||
r3 = RStruct("Foo", ["a", "c"],
|
||||
[bool_rprimitive, int_rprimitive])
|
||||
assert hash(r) != hash(r3)
|
||||
assert r != r3
|
||||
|
||||
# different type
|
||||
r4 = RStruct("Foo", ["a", "b"],
|
||||
[bool_rprimitive, int_rprimitive, bool_rprimitive])
|
||||
assert hash(r) != hash(r4)
|
||||
assert r != r4
|
||||
27
.venv/lib/python3.8/site-packages/mypyc/test/test_subtype.py
Normal file
27
.venv/lib/python3.8/site-packages/mypyc/test/test_subtype.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
"""Test cases for is_subtype and is_runtime_subtype."""
|
||||
|
||||
import unittest
|
||||
|
||||
from mypyc.ir.rtypes import bit_rprimitive, bool_rprimitive, int_rprimitive
|
||||
from mypyc.subtype import is_subtype
|
||||
from mypyc.rt_subtype import is_runtime_subtype
|
||||
|
||||
|
||||
class TestSubtype(unittest.TestCase):
|
||||
def test_bit(self) -> None:
|
||||
assert is_subtype(bit_rprimitive, bool_rprimitive)
|
||||
assert is_subtype(bit_rprimitive, int_rprimitive)
|
||||
|
||||
def test_bool(self) -> None:
|
||||
assert not is_subtype(bool_rprimitive, bit_rprimitive)
|
||||
assert is_subtype(bool_rprimitive, int_rprimitive)
|
||||
|
||||
|
||||
class TestRuntimeSubtype(unittest.TestCase):
|
||||
def test_bit(self) -> None:
|
||||
assert is_runtime_subtype(bit_rprimitive, bool_rprimitive)
|
||||
assert not is_runtime_subtype(bit_rprimitive, int_rprimitive)
|
||||
|
||||
def test_bool(self) -> None:
|
||||
assert not is_runtime_subtype(bool_rprimitive, bit_rprimitive)
|
||||
assert not is_runtime_subtype(bool_rprimitive, int_rprimitive)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import unittest
|
||||
|
||||
from mypyc.ir.rtypes import (
|
||||
RTuple, object_rprimitive, int_rprimitive, bool_rprimitive, list_rprimitive,
|
||||
RInstance, RUnion,
|
||||
)
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
|
||||
|
||||
class TestTupleNames(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.inst_a = RInstance(ClassIR('A', '__main__'))
|
||||
self.inst_b = RInstance(ClassIR('B', '__main__'))
|
||||
|
||||
def test_names(self) -> None:
|
||||
assert RTuple([int_rprimitive, int_rprimitive]).unique_id == "T2II"
|
||||
assert RTuple([list_rprimitive, object_rprimitive, self.inst_a]).unique_id == "T3OOO"
|
||||
assert RTuple([list_rprimitive, object_rprimitive, self.inst_b]).unique_id == "T3OOO"
|
||||
assert RTuple([]).unique_id == "T0"
|
||||
assert RTuple([RTuple([]),
|
||||
RTuple([int_rprimitive, int_rprimitive])]).unique_id == "T2T0T2II"
|
||||
assert RTuple([bool_rprimitive,
|
||||
RUnion([bool_rprimitive, int_rprimitive])]).unique_id == "T2CO"
|
||||
266
.venv/lib/python3.8/site-packages/mypyc/test/testutil.py
Normal file
266
.venv/lib/python3.8/site-packages/mypyc/test/testutil.py
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
"""Helpers for writing tests"""
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
from typing import List, Callable, Iterator, Optional, Tuple
|
||||
|
||||
from mypy import build
|
||||
from mypy.errors import CompileError
|
||||
from mypy.options import Options
|
||||
from mypy.test.data import DataSuite, DataDrivenTestCase
|
||||
from mypy.test.config import test_temp_dir
|
||||
from mypy.test.helpers import assert_string_arrays_equal
|
||||
|
||||
from mypyc.options import CompilerOptions
|
||||
from mypyc.analysis.ircheck import assert_func_ir_valid
|
||||
from mypyc.ir.func_ir import FuncIR
|
||||
from mypyc.errors import Errors
|
||||
from mypyc.irbuild.main import build_ir
|
||||
from mypyc.irbuild.mapper import Mapper
|
||||
from mypyc.test.config import test_data_prefix
|
||||
from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE
|
||||
|
||||
# The builtins stub used during icode generation test cases.
|
||||
ICODE_GEN_BUILTINS = os.path.join(test_data_prefix, 'fixtures/ir.py')
|
||||
# The testutil support library
|
||||
TESTUTIL_PATH = os.path.join(test_data_prefix, 'fixtures/testutil.py')
|
||||
|
||||
|
||||
class MypycDataSuite(DataSuite):
|
||||
# Need to list no files, since this will be picked up as a suite of tests
|
||||
files: List[str] = []
|
||||
data_prefix = test_data_prefix
|
||||
|
||||
|
||||
def builtins_wrapper(func: Callable[[DataDrivenTestCase], None],
|
||||
path: str) -> Callable[[DataDrivenTestCase], None]:
|
||||
"""Decorate a function that implements a data-driven test case to copy an
|
||||
alternative builtins module implementation in place before performing the
|
||||
test case. Clean up after executing the test case.
|
||||
"""
|
||||
return lambda testcase: perform_test(func, path, testcase)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def use_custom_builtins(builtins_path: str, testcase: DataDrivenTestCase) -> Iterator[None]:
|
||||
for path, _ in testcase.files:
|
||||
if os.path.basename(path) == 'builtins.pyi':
|
||||
default_builtins = False
|
||||
break
|
||||
else:
|
||||
# Use default builtins.
|
||||
builtins = os.path.abspath(os.path.join(test_temp_dir, 'builtins.pyi'))
|
||||
shutil.copyfile(builtins_path, builtins)
|
||||
default_builtins = True
|
||||
|
||||
# Actually perform the test case.
|
||||
try:
|
||||
yield None
|
||||
finally:
|
||||
if default_builtins:
|
||||
# Clean up.
|
||||
os.remove(builtins)
|
||||
|
||||
|
||||
def perform_test(func: Callable[[DataDrivenTestCase], None],
|
||||
builtins_path: str, testcase: DataDrivenTestCase) -> None:
|
||||
for path, _ in testcase.files:
|
||||
if os.path.basename(path) == 'builtins.py':
|
||||
default_builtins = False
|
||||
break
|
||||
else:
|
||||
# Use default builtins.
|
||||
builtins = os.path.join(test_temp_dir, 'builtins.py')
|
||||
shutil.copyfile(builtins_path, builtins)
|
||||
default_builtins = True
|
||||
|
||||
# Actually perform the test case.
|
||||
func(testcase)
|
||||
|
||||
if default_builtins:
|
||||
# Clean up.
|
||||
os.remove(builtins)
|
||||
|
||||
|
||||
def build_ir_for_single_file(input_lines: List[str],
|
||||
compiler_options: Optional[CompilerOptions] = None) -> List[FuncIR]:
|
||||
program_text = '\n'.join(input_lines)
|
||||
|
||||
# By default generate IR compatible with the earliest supported Python C API.
|
||||
# If a test needs more recent API features, this should be overridden.
|
||||
compiler_options = compiler_options or CompilerOptions(capi_version=(3, 5))
|
||||
options = Options()
|
||||
options.show_traceback = True
|
||||
options.use_builtins_fixtures = True
|
||||
options.strict_optional = True
|
||||
options.python_version = (3, 6)
|
||||
options.export_types = True
|
||||
options.preserve_asts = True
|
||||
options.per_module_options['__main__'] = {'mypyc': True}
|
||||
|
||||
source = build.BuildSource('main', '__main__', program_text)
|
||||
# Construct input as a single single.
|
||||
# Parse and type check the input program.
|
||||
result = build.build(sources=[source],
|
||||
options=options,
|
||||
alt_lib_path=test_temp_dir)
|
||||
if result.errors:
|
||||
raise CompileError(result.errors)
|
||||
|
||||
errors = Errors()
|
||||
modules = build_ir(
|
||||
[result.files['__main__']], result.graph, result.types,
|
||||
Mapper({'__main__': None}),
|
||||
compiler_options, errors)
|
||||
if errors.num_errors:
|
||||
raise CompileError(errors.new_messages())
|
||||
|
||||
module = list(modules.values())[0]
|
||||
for fn in module.functions:
|
||||
assert_func_ir_valid(fn)
|
||||
return module.functions
|
||||
|
||||
|
||||
def update_testcase_output(testcase: DataDrivenTestCase, output: List[str]) -> None:
|
||||
# TODO: backport this to mypy
|
||||
assert testcase.old_cwd is not None, "test was not properly set up"
|
||||
testcase_path = os.path.join(testcase.old_cwd, testcase.file)
|
||||
with open(testcase_path) as f:
|
||||
data_lines = f.read().splitlines()
|
||||
|
||||
# We can't rely on the test line numbers to *find* the test, since
|
||||
# we might fix multiple tests in a run. So find it by the case
|
||||
# header. Give up if there are multiple tests with the same name.
|
||||
test_slug = '[case {}]'.format(testcase.name)
|
||||
if data_lines.count(test_slug) != 1:
|
||||
return
|
||||
start_idx = data_lines.index(test_slug)
|
||||
stop_idx = start_idx + 11
|
||||
while stop_idx < len(data_lines) and not data_lines[stop_idx].startswith('[case '):
|
||||
stop_idx += 1
|
||||
|
||||
test = data_lines[start_idx:stop_idx]
|
||||
out_start = test.index('[out]')
|
||||
test[out_start + 1:] = output
|
||||
data_lines[start_idx:stop_idx] = test + ['']
|
||||
data = '\n'.join(data_lines)
|
||||
|
||||
with open(testcase_path, 'w') as f:
|
||||
print(data, file=f)
|
||||
|
||||
|
||||
def assert_test_output(testcase: DataDrivenTestCase,
|
||||
actual: List[str],
|
||||
message: str,
|
||||
expected: Optional[List[str]] = None,
|
||||
formatted: Optional[List[str]] = None) -> None:
|
||||
__tracebackhide__ = True
|
||||
|
||||
expected_output = expected if expected is not None else testcase.output
|
||||
if expected_output != actual and testcase.config.getoption('--update-data', False):
|
||||
update_testcase_output(testcase, actual)
|
||||
|
||||
assert_string_arrays_equal(
|
||||
expected_output, actual,
|
||||
'{} ({}, line {})'.format(message, testcase.file, testcase.line))
|
||||
|
||||
|
||||
def get_func_names(expected: List[str]) -> List[str]:
|
||||
res = []
|
||||
for s in expected:
|
||||
m = re.match(r'def ([_a-zA-Z0-9.*$]+)\(', s)
|
||||
if m:
|
||||
res.append(m.group(1))
|
||||
return res
|
||||
|
||||
|
||||
def remove_comment_lines(a: List[str]) -> List[str]:
|
||||
"""Return a copy of array with comments removed.
|
||||
|
||||
Lines starting with '--' (but not with '---') are removed.
|
||||
"""
|
||||
r = []
|
||||
for s in a:
|
||||
if s.strip().startswith('--') and not s.strip().startswith('---'):
|
||||
pass
|
||||
else:
|
||||
r.append(s)
|
||||
return r
|
||||
|
||||
|
||||
def print_with_line_numbers(s: str) -> None:
|
||||
lines = s.splitlines()
|
||||
for i, line in enumerate(lines):
|
||||
print('%-4d %s' % (i + 1, line))
|
||||
|
||||
|
||||
def heading(text: str) -> None:
|
||||
print('=' * 20 + ' ' + text + ' ' + '=' * 20)
|
||||
|
||||
|
||||
def show_c(cfiles: List[List[Tuple[str, str]]]) -> None:
|
||||
heading('Generated C')
|
||||
for group in cfiles:
|
||||
for cfile, ctext in group:
|
||||
print('== {} =='.format(cfile))
|
||||
print_with_line_numbers(ctext)
|
||||
heading('End C')
|
||||
|
||||
|
||||
def fudge_dir_mtimes(dir: str, delta: int) -> None:
|
||||
for dirpath, _, filenames in os.walk(dir):
|
||||
for name in filenames:
|
||||
path = os.path.join(dirpath, name)
|
||||
new_mtime = os.stat(path).st_mtime + delta
|
||||
os.utime(path, times=(new_mtime, new_mtime))
|
||||
|
||||
|
||||
def replace_word_size(text: List[str]) -> List[str]:
|
||||
"""Replace WORDSIZE with platform specific word sizes"""
|
||||
result = []
|
||||
for line in text:
|
||||
index = line.find('WORD_SIZE')
|
||||
if index != -1:
|
||||
# get 'WORDSIZE*n' token
|
||||
word_size_token = line[index:].split()[0]
|
||||
n = int(word_size_token[10:])
|
||||
replace_str = str(PLATFORM_SIZE * n)
|
||||
result.append(line.replace(word_size_token, replace_str))
|
||||
else:
|
||||
result.append(line)
|
||||
return result
|
||||
|
||||
|
||||
def infer_ir_build_options_from_test_name(name: str) -> Optional[CompilerOptions]:
|
||||
"""Look for magic substrings in test case name to set compiler options.
|
||||
|
||||
Return None if the test case should be skipped (always pass).
|
||||
|
||||
Supported naming conventions:
|
||||
|
||||
*_64bit*:
|
||||
Run test case only on 64-bit platforms
|
||||
*_32bit*:
|
||||
Run test caseonly on 32-bit platforms
|
||||
*_python3_8* (or for any Python version):
|
||||
Use Python 3.8+ C API features (default: lowest supported version)
|
||||
*StripAssert*:
|
||||
Don't generate code for assert statements
|
||||
"""
|
||||
# If this is specific to some bit width, always pass if platform doesn't match.
|
||||
if '_64bit' in name and IS_32_BIT_PLATFORM:
|
||||
return None
|
||||
if '_32bit' in name and not IS_32_BIT_PLATFORM:
|
||||
return None
|
||||
options = CompilerOptions(strip_asserts='StripAssert' in name,
|
||||
capi_version=(3, 5))
|
||||
# A suffix like _python3.8 is used to set the target C API version.
|
||||
m = re.search(r'_python([3-9]+)_([0-9]+)(_|\b)', name)
|
||||
if m:
|
||||
options.capi_version = (int(m.group(1)), int(m.group(2)))
|
||||
elif '_py' in name or '_Python' in name:
|
||||
assert False, 'Invalid _py* suffix (should be _pythonX_Y): {}'.format(name)
|
||||
return options
|
||||
Loading…
Add table
Add a link
Reference in a new issue