Major fixes and new features
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
133
venv/lib/python3.12/site-packages/mypyc/README.md
Normal file
133
venv/lib/python3.12/site-packages/mypyc/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
mypyc: Mypy to Python C Extension Compiler
|
||||
==========================================
|
||||
|
||||
**NOTE: We are in the process of moving the mypyc README to the**
|
||||
**[mypyc repository](https://github.com/mypyc/mypyc)**
|
||||
|
||||
**This may be out of date!**
|
||||
|
||||
Mypyc is a compiler that compiles mypy-annotated, statically typed
|
||||
Python modules into CPython C extensions. Currently our primary focus
|
||||
is on making mypy faster through compilation -- the default mypy wheels
|
||||
are compiled with mypyc. Compiled mypy is about 4x faster than
|
||||
without compilation.
|
||||
|
||||
Mypyc compiles what is essentially a Python language variant using "strict"
|
||||
semantics. This means (among some other things):
|
||||
|
||||
* Most type annotations are enforced at runtime (raising ``TypeError`` on mismatch)
|
||||
|
||||
* Classes are compiled into extension classes without ``__dict__``
|
||||
(much, but not quite, like if they used ``__slots__``)
|
||||
|
||||
* Monkey patching doesn't work
|
||||
|
||||
* Instance attributes won't fall back to class attributes if undefined
|
||||
|
||||
* Also there are still a bunch of bad bugs and unsupported features :)
|
||||
|
||||
Compiled modules can import arbitrary Python modules, and compiled modules
|
||||
can be used from other Python modules. Typically mypyc is used to only
|
||||
compile modules that contain performance bottlenecks.
|
||||
|
||||
You can run compiled modules also as normal, interpreted Python
|
||||
modules, since mypyc targets valid Python code. This means that
|
||||
all Python developer tools and debuggers can be used.
|
||||
|
||||
macOS Requirements
|
||||
------------------
|
||||
|
||||
* macOS Sierra or later
|
||||
|
||||
* Xcode command line tools
|
||||
|
||||
* Python 3.5+ from python.org (other versions are untested)
|
||||
|
||||
Linux Requirements
|
||||
------------------
|
||||
|
||||
* A recent enough C/C++ build environment
|
||||
|
||||
* Python 3.5+
|
||||
|
||||
Windows Requirements
|
||||
--------------------
|
||||
|
||||
* Windows has been tested with Windows 10 and MSVC 2017.
|
||||
|
||||
* Python 3.5+
|
||||
|
||||
Quick Start for Contributors
|
||||
----------------------------
|
||||
|
||||
First clone the mypy git repository:
|
||||
|
||||
$ git clone https://github.com/python/mypy.git
|
||||
$ cd mypy
|
||||
|
||||
Optionally create a virtualenv (recommended):
|
||||
|
||||
$ python3 -m venv <directory>
|
||||
$ source <directory>/bin/activate
|
||||
|
||||
Then install the dependencies:
|
||||
|
||||
$ python3 -m pip install -r test-requirements.txt
|
||||
|
||||
Now you can run the tests:
|
||||
|
||||
$ pytest -q mypyc
|
||||
|
||||
Look at the [issue tracker](https://github.com/mypyc/mypyc/issues)
|
||||
for things to work on. Please express your interest in working on an
|
||||
issue by adding a comment before doing any significant work, since
|
||||
there is a risk of duplicate work.
|
||||
|
||||
Note that the issue tracker is hosted on the mypyc GitHub project, not
|
||||
with mypy itself.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
We have some [developer documentation](doc/dev-intro.md).
|
||||
|
||||
Development Status and Roadmap
|
||||
------------------------------
|
||||
|
||||
These are the current planned major milestones:
|
||||
|
||||
1. [DONE] Support a smallish but useful Python subset. Focus on compiling
|
||||
single modules, while the rest of the program is interpreted and does not
|
||||
need to be type checked.
|
||||
|
||||
2. [DONE] Support compiling multiple modules as a single compilation unit (or
|
||||
dynamic linking of compiled modules). Without this inter-module
|
||||
calls will use slower Python-level objects, wrapper functions and
|
||||
Python namespaces.
|
||||
|
||||
3. [DONE] Mypyc can compile mypy.
|
||||
|
||||
4. [DONE] Optimize some important performance bottlenecks.
|
||||
|
||||
5. [PARTIALLY DONE] Generate useful errors for code that uses unsupported Python
|
||||
features instead of crashing or generating bad code.
|
||||
|
||||
6. [DONE] Release a version of mypy that includes a compiled mypy.
|
||||
|
||||
7.
|
||||
1. More feature/compatibility work. (100% compatibility with Python is distinctly
|
||||
an anti-goal, but more than we have now is a good idea.)
|
||||
2. [DONE] Support compiling Black, which is a prominent tool that could benefit
|
||||
and has maintainer buy-in.
|
||||
(Let us know if you maintain another Python tool or library and are
|
||||
interested in working with us on this!)
|
||||
3. More optimization! Code size reductions in particular are likely to
|
||||
be valuable and will speed up mypyc compilation.
|
||||
|
||||
8. We'll see! Adventure is out there!
|
||||
|
||||
Future
|
||||
------
|
||||
|
||||
We have some ideas for
|
||||
[future improvements and optimizations](doc/future.md).
|
||||
BIN
venv/lib/python3.12/site-packages/mypyc/__init__.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/mypyc/__init__.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
0
venv/lib/python3.12/site-packages/mypyc/__init__.py
Normal file
0
venv/lib/python3.12/site-packages/mypyc/__init__.py
Normal file
57
venv/lib/python3.12/site-packages/mypyc/__main__.py
Normal file
57
venv/lib/python3.12/site-packages/mypyc/__main__.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""Mypyc command-line tool.
|
||||
|
||||
Usage:
|
||||
|
||||
$ mypyc foo.py [...]
|
||||
$ python3 -c 'import foo' # Uses compiled 'foo'
|
||||
|
||||
|
||||
This is just a thin wrapper that generates a setup.py file that uses
|
||||
mypycify, suitable for prototyping and testing.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
base_path = os.path.join(os.path.dirname(__file__), "..")
|
||||
|
||||
setup_format = """\
|
||||
from setuptools import setup
|
||||
from mypyc.build import mypycify
|
||||
|
||||
setup(name='mypyc_output',
|
||||
ext_modules=mypycify({}, opt_level="{}", debug_level="{}"),
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
def main() -> None:
|
||||
build_dir = "build" # can this be overridden??
|
||||
try:
|
||||
os.mkdir(build_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
opt_level = os.getenv("MYPYC_OPT_LEVEL", "3")
|
||||
debug_level = os.getenv("MYPYC_DEBUG_LEVEL", "1")
|
||||
|
||||
setup_file = os.path.join(build_dir, "setup.py")
|
||||
with open(setup_file, "w") as f:
|
||||
f.write(setup_format.format(sys.argv[1:], opt_level, debug_level))
|
||||
|
||||
# We don't use run_setup (like we do in the test suite) because it throws
|
||||
# away the error code from distutils, and we don't care about the slight
|
||||
# performance loss here.
|
||||
env = os.environ.copy()
|
||||
base_path = os.path.join(os.path.dirname(__file__), "..")
|
||||
env["PYTHONPATH"] = base_path + os.pathsep + env.get("PYTHONPATH", "")
|
||||
cmd = subprocess.run([sys.executable, setup_file, "build_ext", "--inplace"], env=env)
|
||||
sys.exit(cmd.returncode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
Binary file not shown.
436
venv/lib/python3.12/site-packages/mypyc/analysis/attrdefined.py
Normal file
436
venv/lib/python3.12/site-packages/mypyc/analysis/attrdefined.py
Normal file
@@ -0,0 +1,436 @@
|
||||
"""Always defined attribute analysis.
|
||||
|
||||
An always defined attribute has some statements in __init__ or the
|
||||
class body that cause the attribute to be always initialized when an
|
||||
instance is constructed. It must also not be possible to read the
|
||||
attribute before initialization, and it can't be deletable.
|
||||
|
||||
We can assume that the value is always defined when reading an always
|
||||
defined attribute. Otherwise we'll need to raise AttributeError if the
|
||||
value is undefined (i.e. has the error value).
|
||||
|
||||
We use data flow analysis to figure out attributes that are always
|
||||
defined. Example:
|
||||
|
||||
class C:
|
||||
def __init__(self) -> None:
|
||||
self.x = 0
|
||||
if func():
|
||||
self.y = 1
|
||||
else:
|
||||
self.y = 2
|
||||
self.z = 3
|
||||
|
||||
In this example, the attributes 'x' and 'y' are always defined, but 'z'
|
||||
is not. The analysis assumes that we know that there won't be any subclasses.
|
||||
|
||||
The analysis also works if there is a known, closed set of subclasses.
|
||||
An attribute defined in a base class can only be always defined if it's
|
||||
also always defined in all subclasses.
|
||||
|
||||
As soon as __init__ contains an op that can 'leak' self to another
|
||||
function, we will stop inferring always defined attributes, since the
|
||||
analysis is mostly intra-procedural and only looks at __init__ methods.
|
||||
The called code could read an uninitialized attribute. Example:
|
||||
|
||||
class C:
|
||||
def __init__(self) -> None:
|
||||
self.x = self.foo()
|
||||
|
||||
def foo(self) -> int:
|
||||
...
|
||||
|
||||
Now we won't infer 'x' as always defined, since 'foo' might read 'x'
|
||||
before initialization.
|
||||
|
||||
As an exception to the above limitation, we perform inter-procedural
|
||||
analysis of super().__init__ calls, since these are very common.
|
||||
|
||||
Our analysis is somewhat optimistic. We assume that nobody calls a
|
||||
method of a partially uninitialized object through gc.get_objects(), in
|
||||
particular. Code like this could potentially cause a segfault with a null
|
||||
pointer dereference. This seems very unlikely to be an issue in practice,
|
||||
however.
|
||||
|
||||
Accessing an attribute via getattr always checks for undefined attributes
|
||||
and thus works if the object is partially uninitialized. This can be used
|
||||
as a workaround if somebody ever needs to inspect partially uninitialized
|
||||
objects via gc.get_objects().
|
||||
|
||||
The analysis runs after IR building as a separate pass. Since we only
|
||||
run this on __init__ methods, this analysis pass will be fairly quick.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final, Set, Tuple
|
||||
|
||||
from mypyc.analysis.dataflow import (
|
||||
CFG,
|
||||
MAYBE_ANALYSIS,
|
||||
AnalysisResult,
|
||||
BaseAnalysisVisitor,
|
||||
get_cfg,
|
||||
run_analysis,
|
||||
)
|
||||
from mypyc.analysis.selfleaks import analyze_self_leaks
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.ops import (
|
||||
Assign,
|
||||
AssignMulti,
|
||||
BasicBlock,
|
||||
Branch,
|
||||
Call,
|
||||
ControlOp,
|
||||
GetAttr,
|
||||
Register,
|
||||
RegisterOp,
|
||||
Return,
|
||||
SetAttr,
|
||||
SetMem,
|
||||
Unreachable,
|
||||
)
|
||||
from mypyc.ir.rtypes import RInstance
|
||||
|
||||
# If True, print out all always-defined attributes of native classes (to aid
|
||||
# debugging and testing)
|
||||
dump_always_defined: Final = False
|
||||
|
||||
|
||||
def analyze_always_defined_attrs(class_irs: list[ClassIR]) -> None:
|
||||
"""Find always defined attributes all classes of a compilation unit.
|
||||
|
||||
Also tag attribute initialization ops to not decref the previous
|
||||
value (as this would read a NULL pointer and segfault).
|
||||
|
||||
Update the _always_initialized_attrs, _sometimes_initialized_attrs
|
||||
and init_self_leak attributes in ClassIR instances.
|
||||
|
||||
This is the main entry point.
|
||||
"""
|
||||
seen: set[ClassIR] = set()
|
||||
|
||||
# First pass: only look at target class and classes in MRO
|
||||
for cl in class_irs:
|
||||
analyze_always_defined_attrs_in_class(cl, seen)
|
||||
|
||||
# Second pass: look at all derived class
|
||||
seen = set()
|
||||
for cl in class_irs:
|
||||
update_always_defined_attrs_using_subclasses(cl, seen)
|
||||
|
||||
# Final pass: detect attributes that need to use a bitmap to track definedness
|
||||
seen = set()
|
||||
for cl in class_irs:
|
||||
detect_undefined_bitmap(cl, seen)
|
||||
|
||||
|
||||
def analyze_always_defined_attrs_in_class(cl: ClassIR, seen: set[ClassIR]) -> None:
|
||||
if cl in seen:
|
||||
return
|
||||
|
||||
seen.add(cl)
|
||||
|
||||
if (
|
||||
cl.is_trait
|
||||
or cl.inherits_python
|
||||
or cl.allow_interpreted_subclasses
|
||||
or cl.builtin_base is not None
|
||||
or cl.children is None
|
||||
or cl.is_serializable()
|
||||
):
|
||||
# Give up -- we can't enforce that attributes are always defined.
|
||||
return
|
||||
|
||||
# First analyze all base classes. Track seen classes to avoid duplicate work.
|
||||
for base in cl.mro[1:]:
|
||||
analyze_always_defined_attrs_in_class(base, seen)
|
||||
|
||||
m = cl.get_method("__init__")
|
||||
if m is None:
|
||||
cl._always_initialized_attrs = cl.attrs_with_defaults.copy()
|
||||
cl._sometimes_initialized_attrs = cl.attrs_with_defaults.copy()
|
||||
return
|
||||
self_reg = m.arg_regs[0]
|
||||
cfg = get_cfg(m.blocks)
|
||||
dirty = analyze_self_leaks(m.blocks, self_reg, cfg)
|
||||
maybe_defined = analyze_maybe_defined_attrs_in_init(
|
||||
m.blocks, self_reg, cl.attrs_with_defaults, cfg
|
||||
)
|
||||
all_attrs: set[str] = set()
|
||||
for base in cl.mro:
|
||||
all_attrs.update(base.attributes)
|
||||
maybe_undefined = analyze_maybe_undefined_attrs_in_init(
|
||||
m.blocks, self_reg, initial_undefined=all_attrs - cl.attrs_with_defaults, cfg=cfg
|
||||
)
|
||||
|
||||
always_defined = find_always_defined_attributes(
|
||||
m.blocks, self_reg, all_attrs, maybe_defined, maybe_undefined, dirty
|
||||
)
|
||||
always_defined = {a for a in always_defined if not cl.is_deletable(a)}
|
||||
|
||||
cl._always_initialized_attrs = always_defined
|
||||
if dump_always_defined:
|
||||
print(cl.name, sorted(always_defined))
|
||||
cl._sometimes_initialized_attrs = find_sometimes_defined_attributes(
|
||||
m.blocks, self_reg, maybe_defined, dirty
|
||||
)
|
||||
|
||||
mark_attr_initialiation_ops(m.blocks, self_reg, maybe_defined, dirty)
|
||||
|
||||
# Check if __init__ can run unpredictable code (leak 'self').
|
||||
any_dirty = False
|
||||
for b in m.blocks:
|
||||
for i, op in enumerate(b.ops):
|
||||
if dirty.after[b, i] and not isinstance(op, Return):
|
||||
any_dirty = True
|
||||
break
|
||||
cl.init_self_leak = any_dirty
|
||||
|
||||
|
||||
def find_always_defined_attributes(
|
||||
blocks: list[BasicBlock],
|
||||
self_reg: Register,
|
||||
all_attrs: set[str],
|
||||
maybe_defined: AnalysisResult[str],
|
||||
maybe_undefined: AnalysisResult[str],
|
||||
dirty: AnalysisResult[None],
|
||||
) -> set[str]:
|
||||
"""Find attributes that are always initialized in some basic blocks.
|
||||
|
||||
The analysis results are expected to be up-to-date for the blocks.
|
||||
|
||||
Return a set of always defined attributes.
|
||||
"""
|
||||
attrs = all_attrs.copy()
|
||||
for block in blocks:
|
||||
for i, op in enumerate(block.ops):
|
||||
# If an attribute we *read* may be undefined, it isn't always defined.
|
||||
if isinstance(op, GetAttr) and op.obj is self_reg:
|
||||
if op.attr in maybe_undefined.before[block, i]:
|
||||
attrs.discard(op.attr)
|
||||
# If an attribute we *set* may be sometimes undefined and
|
||||
# sometimes defined, don't consider it always defined. Unlike
|
||||
# the get case, it's fine for the attribute to be undefined.
|
||||
# The set operation will then be treated as initialization.
|
||||
if isinstance(op, SetAttr) and op.obj is self_reg:
|
||||
if (
|
||||
op.attr in maybe_undefined.before[block, i]
|
||||
and op.attr in maybe_defined.before[block, i]
|
||||
):
|
||||
attrs.discard(op.attr)
|
||||
# Treat an op that might run arbitrary code as an "exit"
|
||||
# in terms of the analysis -- we can't do any inference
|
||||
# afterwards reliably.
|
||||
if dirty.after[block, i]:
|
||||
if not dirty.before[block, i]:
|
||||
attrs = attrs & (
|
||||
maybe_defined.after[block, i] - maybe_undefined.after[block, i]
|
||||
)
|
||||
break
|
||||
if isinstance(op, ControlOp):
|
||||
for target in op.targets():
|
||||
# Gotos/branches can also be "exits".
|
||||
if not dirty.after[block, i] and dirty.before[target, 0]:
|
||||
attrs = attrs & (
|
||||
maybe_defined.after[target, 0] - maybe_undefined.after[target, 0]
|
||||
)
|
||||
return attrs
|
||||
|
||||
|
||||
def find_sometimes_defined_attributes(
|
||||
blocks: list[BasicBlock],
|
||||
self_reg: Register,
|
||||
maybe_defined: AnalysisResult[str],
|
||||
dirty: AnalysisResult[None],
|
||||
) -> set[str]:
|
||||
"""Find attributes that are sometimes initialized in some basic blocks."""
|
||||
attrs: set[str] = set()
|
||||
for block in blocks:
|
||||
for i, op in enumerate(block.ops):
|
||||
# Only look at possibly defined attributes at exits.
|
||||
if dirty.after[block, i]:
|
||||
if not dirty.before[block, i]:
|
||||
attrs = attrs | maybe_defined.after[block, i]
|
||||
break
|
||||
if isinstance(op, ControlOp):
|
||||
for target in op.targets():
|
||||
if not dirty.after[block, i] and dirty.before[target, 0]:
|
||||
attrs = attrs | maybe_defined.after[target, 0]
|
||||
return attrs
|
||||
|
||||
|
||||
def mark_attr_initialiation_ops(
|
||||
blocks: list[BasicBlock],
|
||||
self_reg: Register,
|
||||
maybe_defined: AnalysisResult[str],
|
||||
dirty: AnalysisResult[None],
|
||||
) -> None:
|
||||
"""Tag all SetAttr ops in the basic blocks that initialize attributes.
|
||||
|
||||
Initialization ops assume that the previous attribute value is the error value,
|
||||
so there's no need to decref or check for definedness.
|
||||
"""
|
||||
for block in blocks:
|
||||
for i, op in enumerate(block.ops):
|
||||
if isinstance(op, SetAttr) and op.obj is self_reg:
|
||||
attr = op.attr
|
||||
if attr not in maybe_defined.before[block, i] and not dirty.after[block, i]:
|
||||
op.mark_as_initializer()
|
||||
|
||||
|
||||
GenAndKill = Tuple[Set[str], Set[str]]
|
||||
|
||||
|
||||
def attributes_initialized_by_init_call(op: Call) -> set[str]:
|
||||
"""Calculate attributes that are always initialized by a super().__init__ call."""
|
||||
self_type = op.fn.sig.args[0].type
|
||||
assert isinstance(self_type, RInstance)
|
||||
cl = self_type.class_ir
|
||||
return {a for base in cl.mro for a in base.attributes if base.is_always_defined(a)}
|
||||
|
||||
|
||||
def attributes_maybe_initialized_by_init_call(op: Call) -> set[str]:
|
||||
"""Calculate attributes that may be initialized by a super().__init__ call."""
|
||||
self_type = op.fn.sig.args[0].type
|
||||
assert isinstance(self_type, RInstance)
|
||||
cl = self_type.class_ir
|
||||
return attributes_initialized_by_init_call(op) | cl._sometimes_initialized_attrs
|
||||
|
||||
|
||||
class AttributeMaybeDefinedVisitor(BaseAnalysisVisitor[str]):
|
||||
"""Find attributes that may have been defined via some code path.
|
||||
|
||||
Consider initializations in class body and assignments to 'self.x'
|
||||
and calls to base class '__init__'.
|
||||
"""
|
||||
|
||||
def __init__(self, self_reg: Register) -> None:
|
||||
self.self_reg = self_reg
|
||||
|
||||
def visit_branch(self, op: Branch) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_return(self, op: Return) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_register_op(self, op: RegisterOp) -> tuple[set[str], set[str]]:
|
||||
if isinstance(op, SetAttr) and op.obj is self.self_reg:
|
||||
return {op.attr}, set()
|
||||
if isinstance(op, Call) and op.fn.class_name and op.fn.name == "__init__":
|
||||
return attributes_maybe_initialized_by_init_call(op), set()
|
||||
return set(), set()
|
||||
|
||||
def visit_assign(self, op: Assign) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
|
||||
def analyze_maybe_defined_attrs_in_init(
|
||||
blocks: list[BasicBlock], self_reg: Register, attrs_with_defaults: set[str], cfg: CFG
|
||||
) -> AnalysisResult[str]:
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=AttributeMaybeDefinedVisitor(self_reg),
|
||||
initial=attrs_with_defaults,
|
||||
backward=False,
|
||||
kind=MAYBE_ANALYSIS,
|
||||
)
|
||||
|
||||
|
||||
class AttributeMaybeUndefinedVisitor(BaseAnalysisVisitor[str]):
|
||||
"""Find attributes that may be undefined via some code path.
|
||||
|
||||
Consider initializations in class body, assignments to 'self.x'
|
||||
and calls to base class '__init__'.
|
||||
"""
|
||||
|
||||
def __init__(self, self_reg: Register) -> None:
|
||||
self.self_reg = self_reg
|
||||
|
||||
def visit_branch(self, op: Branch) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_return(self, op: Return) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_register_op(self, op: RegisterOp) -> tuple[set[str], set[str]]:
|
||||
if isinstance(op, SetAttr) and op.obj is self.self_reg:
|
||||
return set(), {op.attr}
|
||||
if isinstance(op, Call) and op.fn.class_name and op.fn.name == "__init__":
|
||||
return set(), attributes_initialized_by_init_call(op)
|
||||
return set(), set()
|
||||
|
||||
def visit_assign(self, op: Assign) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> tuple[set[str], set[str]]:
|
||||
return set(), set()
|
||||
|
||||
|
||||
def analyze_maybe_undefined_attrs_in_init(
|
||||
blocks: list[BasicBlock], self_reg: Register, initial_undefined: set[str], cfg: CFG
|
||||
) -> AnalysisResult[str]:
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=AttributeMaybeUndefinedVisitor(self_reg),
|
||||
initial=initial_undefined,
|
||||
backward=False,
|
||||
kind=MAYBE_ANALYSIS,
|
||||
)
|
||||
|
||||
|
||||
def update_always_defined_attrs_using_subclasses(cl: ClassIR, seen: set[ClassIR]) -> None:
|
||||
"""Remove attributes not defined in all subclasses from always defined attrs."""
|
||||
if cl in seen:
|
||||
return
|
||||
if cl.children is None:
|
||||
# Subclasses are unknown
|
||||
return
|
||||
removed = set()
|
||||
for attr in cl._always_initialized_attrs:
|
||||
for child in cl.children:
|
||||
update_always_defined_attrs_using_subclasses(child, seen)
|
||||
if attr not in child._always_initialized_attrs:
|
||||
removed.add(attr)
|
||||
cl._always_initialized_attrs -= removed
|
||||
seen.add(cl)
|
||||
|
||||
|
||||
def detect_undefined_bitmap(cl: ClassIR, seen: set[ClassIR]) -> None:
|
||||
if cl.is_trait:
|
||||
return
|
||||
|
||||
if cl in seen:
|
||||
return
|
||||
seen.add(cl)
|
||||
for base in cl.base_mro[1:]:
|
||||
detect_undefined_bitmap(cl, seen)
|
||||
|
||||
if len(cl.base_mro) > 1:
|
||||
cl.bitmap_attrs.extend(cl.base_mro[1].bitmap_attrs)
|
||||
for n, t in cl.attributes.items():
|
||||
if t.error_overlap and not cl.is_always_defined(n):
|
||||
cl.bitmap_attrs.append(n)
|
||||
|
||||
for base in cl.mro[1:]:
|
||||
if base.is_trait:
|
||||
for n, t in base.attributes.items():
|
||||
if t.error_overlap and not cl.is_always_defined(n) and n not in cl.bitmap_attrs:
|
||||
cl.bitmap_attrs.append(n)
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
"""Find basic blocks that are likely to be executed frequently.
|
||||
|
||||
For example, this would not include blocks that have exception handlers.
|
||||
|
||||
We can use different optimization heuristics for common and rare code. For
|
||||
example, we can make IR fast to compile instead of fast to execute for rare
|
||||
code.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from mypyc.ir.ops import BasicBlock, Branch, Goto
|
||||
|
||||
|
||||
def frequently_executed_blocks(entry_point: BasicBlock) -> set[BasicBlock]:
|
||||
result: set[BasicBlock] = set()
|
||||
worklist = [entry_point]
|
||||
while worklist:
|
||||
block = worklist.pop()
|
||||
if block in result:
|
||||
continue
|
||||
result.add(block)
|
||||
t = block.terminator
|
||||
if isinstance(t, Goto):
|
||||
worklist.append(t.label)
|
||||
elif isinstance(t, Branch):
|
||||
if t.rare or t.traceback_entry is not None:
|
||||
worklist.append(t.false)
|
||||
else:
|
||||
worklist.append(t.true)
|
||||
worklist.append(t.false)
|
||||
return result
|
||||
Binary file not shown.
623
venv/lib/python3.12/site-packages/mypyc/analysis/dataflow.py
Normal file
623
venv/lib/python3.12/site-packages/mypyc/analysis/dataflow.py
Normal file
@@ -0,0 +1,623 @@
|
||||
"""Data-flow analyses."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import Dict, Generic, Iterable, Iterator, Set, Tuple, TypeVar
|
||||
|
||||
from mypyc.ir.func_ir import all_values
|
||||
from mypyc.ir.ops import (
|
||||
Assign,
|
||||
AssignMulti,
|
||||
BasicBlock,
|
||||
Box,
|
||||
Branch,
|
||||
Call,
|
||||
CallC,
|
||||
Cast,
|
||||
ComparisonOp,
|
||||
ControlOp,
|
||||
Extend,
|
||||
Float,
|
||||
FloatComparisonOp,
|
||||
FloatNeg,
|
||||
FloatOp,
|
||||
GetAttr,
|
||||
GetElementPtr,
|
||||
Goto,
|
||||
InitStatic,
|
||||
Integer,
|
||||
IntOp,
|
||||
KeepAlive,
|
||||
LoadAddress,
|
||||
LoadErrorValue,
|
||||
LoadGlobal,
|
||||
LoadLiteral,
|
||||
LoadMem,
|
||||
LoadStatic,
|
||||
MethodCall,
|
||||
Op,
|
||||
OpVisitor,
|
||||
RaiseStandardError,
|
||||
RegisterOp,
|
||||
Return,
|
||||
SetAttr,
|
||||
SetMem,
|
||||
Truncate,
|
||||
TupleGet,
|
||||
TupleSet,
|
||||
Unbox,
|
||||
Unreachable,
|
||||
Value,
|
||||
)
|
||||
|
||||
|
||||
class CFG:
|
||||
"""Control-flow graph.
|
||||
|
||||
Node 0 is always assumed to be the entry point. There must be a
|
||||
non-empty set of exits.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
succ: dict[BasicBlock, list[BasicBlock]],
|
||||
pred: dict[BasicBlock, list[BasicBlock]],
|
||||
exits: set[BasicBlock],
|
||||
) -> None:
|
||||
assert exits
|
||||
self.succ = succ
|
||||
self.pred = pred
|
||||
self.exits = exits
|
||||
|
||||
def __str__(self) -> str:
|
||||
lines = []
|
||||
lines.append("exits: %s" % sorted(self.exits, key=lambda e: int(e.label)))
|
||||
lines.append("succ: %s" % self.succ)
|
||||
lines.append("pred: %s" % self.pred)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def get_cfg(blocks: list[BasicBlock]) -> CFG:
|
||||
"""Calculate basic block control-flow graph.
|
||||
|
||||
The result is a dictionary like this:
|
||||
|
||||
basic block index -> (successors blocks, predecesssor blocks)
|
||||
"""
|
||||
succ_map = {}
|
||||
pred_map: dict[BasicBlock, list[BasicBlock]] = {}
|
||||
exits = set()
|
||||
for block in blocks:
|
||||
assert not any(
|
||||
isinstance(op, ControlOp) for op in block.ops[:-1]
|
||||
), "Control-flow ops must be at the end of blocks"
|
||||
|
||||
succ = list(block.terminator.targets())
|
||||
if not succ:
|
||||
exits.add(block)
|
||||
|
||||
# Errors can occur anywhere inside a block, which means that
|
||||
# we can't assume that the entire block has executed before
|
||||
# jumping to the error handler. In our CFG construction, we
|
||||
# model this as saying that a block can jump to its error
|
||||
# handler or the error handlers of any of its normal
|
||||
# successors (to represent an error before that next block
|
||||
# completes). This works well for analyses like "must
|
||||
# defined", where it implies that registers assigned in a
|
||||
# block may be undefined in its error handler, but is in
|
||||
# general not a precise representation of reality; any
|
||||
# analyses that require more fidelity must wait until after
|
||||
# exception insertion.
|
||||
for error_point in [block] + succ:
|
||||
if error_point.error_handler:
|
||||
succ.append(error_point.error_handler)
|
||||
|
||||
succ_map[block] = succ
|
||||
pred_map[block] = []
|
||||
for prev, nxt in succ_map.items():
|
||||
for label in nxt:
|
||||
pred_map[label].append(prev)
|
||||
return CFG(succ_map, pred_map, exits)
|
||||
|
||||
|
||||
def get_real_target(label: BasicBlock) -> BasicBlock:
|
||||
if len(label.ops) == 1 and isinstance(label.ops[-1], Goto):
|
||||
label = label.ops[-1].label
|
||||
return label
|
||||
|
||||
|
||||
def cleanup_cfg(blocks: list[BasicBlock]) -> None:
|
||||
"""Cleanup the control flow graph.
|
||||
|
||||
This eliminates obviously dead basic blocks and eliminates blocks that contain
|
||||
nothing but a single jump.
|
||||
|
||||
There is a lot more that could be done.
|
||||
"""
|
||||
changed = True
|
||||
while changed:
|
||||
# First collapse any jumps to basic block that only contain a goto
|
||||
for block in blocks:
|
||||
for i, tgt in enumerate(block.terminator.targets()):
|
||||
block.terminator.set_target(i, get_real_target(tgt))
|
||||
|
||||
# Then delete any blocks that have no predecessors
|
||||
changed = False
|
||||
cfg = get_cfg(blocks)
|
||||
orig_blocks = blocks.copy()
|
||||
blocks.clear()
|
||||
for i, block in enumerate(orig_blocks):
|
||||
if i == 0 or cfg.pred[block]:
|
||||
blocks.append(block)
|
||||
else:
|
||||
changed = True
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
AnalysisDict = Dict[Tuple[BasicBlock, int], Set[T]]
|
||||
|
||||
|
||||
class AnalysisResult(Generic[T]):
|
||||
def __init__(self, before: AnalysisDict[T], after: AnalysisDict[T]) -> None:
|
||||
self.before = before
|
||||
self.after = after
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"before: {self.before}\nafter: {self.after}\n"
|
||||
|
||||
|
||||
GenAndKill = Tuple[Set[T], Set[T]]
|
||||
|
||||
|
||||
class BaseAnalysisVisitor(OpVisitor[GenAndKill[T]]):
|
||||
def visit_goto(self, op: Goto) -> GenAndKill[T]:
|
||||
return set(), set()
|
||||
|
||||
@abstractmethod
|
||||
def visit_register_op(self, op: RegisterOp) -> GenAndKill[T]:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def visit_assign(self, op: Assign) -> GenAndKill[T]:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[T]:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def visit_set_mem(self, op: SetMem) -> GenAndKill[T]:
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_call(self, op: Call) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_method_call(self, op: MethodCall) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_load_error_value(self, op: LoadErrorValue) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_load_literal(self, op: LoadLiteral) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_get_attr(self, op: GetAttr) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_set_attr(self, op: SetAttr) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_load_static(self, op: LoadStatic) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_init_static(self, op: InitStatic) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_tuple_get(self, op: TupleGet) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_tuple_set(self, op: TupleSet) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_box(self, op: Box) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_unbox(self, op: Unbox) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_cast(self, op: Cast) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_call_c(self, op: CallC) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_truncate(self, op: Truncate) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_extend(self, op: Extend) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_load_global(self, op: LoadGlobal) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_int_op(self, op: IntOp) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_float_op(self, op: FloatOp) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_float_neg(self, op: FloatNeg) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_comparison_op(self, op: ComparisonOp) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_float_comparison_op(self, op: FloatComparisonOp) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_load_mem(self, op: LoadMem) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_load_address(self, op: LoadAddress) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
def visit_keep_alive(self, op: KeepAlive) -> GenAndKill[T]:
|
||||
return self.visit_register_op(op)
|
||||
|
||||
|
||||
class DefinedVisitor(BaseAnalysisVisitor[Value]):
|
||||
"""Visitor for finding defined registers.
|
||||
|
||||
Note that this only deals with registers and not temporaries, on
|
||||
the assumption that we never access temporaries when they might be
|
||||
undefined.
|
||||
|
||||
If strict_errors is True, then we regard any use of LoadErrorValue
|
||||
as making a register undefined. Otherwise we only do if
|
||||
`undefines` is set on the error value.
|
||||
|
||||
This lets us only consider the things we care about during
|
||||
uninitialized variable checking while capturing all possibly
|
||||
undefined things for refcounting.
|
||||
"""
|
||||
|
||||
def __init__(self, strict_errors: bool = False) -> None:
|
||||
self.strict_errors = strict_errors
|
||||
|
||||
def visit_branch(self, op: Branch) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_return(self, op: Return) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_register_op(self, op: RegisterOp) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_assign(self, op: Assign) -> GenAndKill[Value]:
|
||||
# Loading an error value may undefine the register.
|
||||
if isinstance(op.src, LoadErrorValue) and (op.src.undefines or self.strict_errors):
|
||||
return set(), {op.dest}
|
||||
else:
|
||||
return {op.dest}, set()
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[Value]:
|
||||
# Array registers are special and we don't track the definedness of them.
|
||||
return set(), set()
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
|
||||
def analyze_maybe_defined_regs(
|
||||
blocks: list[BasicBlock], cfg: CFG, initial_defined: set[Value]
|
||||
) -> AnalysisResult[Value]:
|
||||
"""Calculate potentially defined registers at each CFG location.
|
||||
|
||||
A register is defined if it has a value along some path from the initial location.
|
||||
"""
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=DefinedVisitor(),
|
||||
initial=initial_defined,
|
||||
backward=False,
|
||||
kind=MAYBE_ANALYSIS,
|
||||
)
|
||||
|
||||
|
||||
def analyze_must_defined_regs(
|
||||
blocks: list[BasicBlock],
|
||||
cfg: CFG,
|
||||
initial_defined: set[Value],
|
||||
regs: Iterable[Value],
|
||||
strict_errors: bool = False,
|
||||
) -> AnalysisResult[Value]:
|
||||
"""Calculate always defined registers at each CFG location.
|
||||
|
||||
This analysis can work before exception insertion, since it is a
|
||||
sound assumption that registers defined in a block might not be
|
||||
initialized in its error handler.
|
||||
|
||||
A register is defined if it has a value along all paths from the
|
||||
initial location.
|
||||
"""
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=DefinedVisitor(strict_errors=strict_errors),
|
||||
initial=initial_defined,
|
||||
backward=False,
|
||||
kind=MUST_ANALYSIS,
|
||||
universe=set(regs),
|
||||
)
|
||||
|
||||
|
||||
class BorrowedArgumentsVisitor(BaseAnalysisVisitor[Value]):
|
||||
def __init__(self, args: set[Value]) -> None:
|
||||
self.args = args
|
||||
|
||||
def visit_branch(self, op: Branch) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_return(self, op: Return) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_register_op(self, op: RegisterOp) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_assign(self, op: Assign) -> GenAndKill[Value]:
|
||||
if op.dest in self.args:
|
||||
return set(), {op.dest}
|
||||
return set(), set()
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
|
||||
def analyze_borrowed_arguments(
|
||||
blocks: list[BasicBlock], cfg: CFG, borrowed: set[Value]
|
||||
) -> AnalysisResult[Value]:
|
||||
"""Calculate arguments that can use references borrowed from the caller.
|
||||
|
||||
When assigning to an argument, it no longer is borrowed.
|
||||
"""
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=BorrowedArgumentsVisitor(borrowed),
|
||||
initial=borrowed,
|
||||
backward=False,
|
||||
kind=MUST_ANALYSIS,
|
||||
universe=borrowed,
|
||||
)
|
||||
|
||||
|
||||
class UndefinedVisitor(BaseAnalysisVisitor[Value]):
|
||||
def visit_branch(self, op: Branch) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_return(self, op: Return) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_register_op(self, op: RegisterOp) -> GenAndKill[Value]:
|
||||
return set(), {op} if not op.is_void else set()
|
||||
|
||||
def visit_assign(self, op: Assign) -> GenAndKill[Value]:
|
||||
return set(), {op.dest}
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[Value]:
|
||||
return set(), {op.dest}
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
|
||||
def analyze_undefined_regs(
|
||||
blocks: list[BasicBlock], cfg: CFG, initial_defined: set[Value]
|
||||
) -> AnalysisResult[Value]:
|
||||
"""Calculate potentially undefined registers at each CFG location.
|
||||
|
||||
A register is undefined if there is some path from initial block
|
||||
where it has an undefined value.
|
||||
|
||||
Function arguments are assumed to be always defined.
|
||||
"""
|
||||
initial_undefined = set(all_values([], blocks)) - initial_defined
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=UndefinedVisitor(),
|
||||
initial=initial_undefined,
|
||||
backward=False,
|
||||
kind=MAYBE_ANALYSIS,
|
||||
)
|
||||
|
||||
|
||||
def non_trivial_sources(op: Op) -> set[Value]:
|
||||
result = set()
|
||||
for source in op.sources():
|
||||
if not isinstance(source, (Integer, Float)):
|
||||
result.add(source)
|
||||
return result
|
||||
|
||||
|
||||
class LivenessVisitor(BaseAnalysisVisitor[Value]):
|
||||
def visit_branch(self, op: Branch) -> GenAndKill[Value]:
|
||||
return non_trivial_sources(op), set()
|
||||
|
||||
def visit_return(self, op: Return) -> GenAndKill[Value]:
|
||||
if not isinstance(op.value, (Integer, Float)):
|
||||
return {op.value}, set()
|
||||
else:
|
||||
return set(), set()
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> GenAndKill[Value]:
|
||||
return set(), set()
|
||||
|
||||
def visit_register_op(self, op: RegisterOp) -> GenAndKill[Value]:
|
||||
gen = non_trivial_sources(op)
|
||||
if not op.is_void:
|
||||
return gen, {op}
|
||||
else:
|
||||
return gen, set()
|
||||
|
||||
def visit_assign(self, op: Assign) -> GenAndKill[Value]:
|
||||
return non_trivial_sources(op), {op.dest}
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[Value]:
|
||||
return non_trivial_sources(op), {op.dest}
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]:
|
||||
return non_trivial_sources(op), set()
|
||||
|
||||
|
||||
def analyze_live_regs(blocks: list[BasicBlock], cfg: CFG) -> AnalysisResult[Value]:
|
||||
"""Calculate live registers at each CFG location.
|
||||
|
||||
A register is live at a location if it can be read along some CFG path starting
|
||||
from the location.
|
||||
"""
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=LivenessVisitor(),
|
||||
initial=set(),
|
||||
backward=True,
|
||||
kind=MAYBE_ANALYSIS,
|
||||
)
|
||||
|
||||
|
||||
# Analysis kinds
|
||||
MUST_ANALYSIS = 0
|
||||
MAYBE_ANALYSIS = 1
|
||||
|
||||
|
||||
def run_analysis(
|
||||
blocks: list[BasicBlock],
|
||||
cfg: CFG,
|
||||
gen_and_kill: OpVisitor[GenAndKill[T]],
|
||||
initial: set[T],
|
||||
kind: int,
|
||||
backward: bool,
|
||||
universe: set[T] | None = None,
|
||||
) -> AnalysisResult[T]:
|
||||
"""Run a general set-based data flow analysis.
|
||||
|
||||
Args:
|
||||
blocks: All basic blocks
|
||||
cfg: Control-flow graph for the code
|
||||
gen_and_kill: Implementation of gen and kill functions for each op
|
||||
initial: Value of analysis for the entry points (for a forward analysis) or the
|
||||
exit points (for a backward analysis)
|
||||
kind: MUST_ANALYSIS or MAYBE_ANALYSIS
|
||||
backward: If False, the analysis is a forward analysis; it's backward otherwise
|
||||
universe: For a must analysis, the set of all possible values. This is the starting
|
||||
value for the work list algorithm, which will narrow this down until reaching a
|
||||
fixed point. For a maybe analysis the iteration always starts from an empty set
|
||||
and this argument is ignored.
|
||||
|
||||
Return analysis results: (before, after)
|
||||
"""
|
||||
block_gen = {}
|
||||
block_kill = {}
|
||||
|
||||
# Calculate kill and gen sets for entire basic blocks.
|
||||
for block in blocks:
|
||||
gen: set[T] = set()
|
||||
kill: set[T] = set()
|
||||
ops = block.ops
|
||||
if backward:
|
||||
ops = list(reversed(ops))
|
||||
for op in ops:
|
||||
opgen, opkill = op.accept(gen_and_kill)
|
||||
gen = (gen - opkill) | opgen
|
||||
kill = (kill - opgen) | opkill
|
||||
block_gen[block] = gen
|
||||
block_kill[block] = kill
|
||||
|
||||
# Set up initial state for worklist algorithm.
|
||||
worklist = list(blocks)
|
||||
if not backward:
|
||||
worklist = worklist[::-1] # Reverse for a small performance improvement
|
||||
workset = set(worklist)
|
||||
before: dict[BasicBlock, set[T]] = {}
|
||||
after: dict[BasicBlock, set[T]] = {}
|
||||
for block in blocks:
|
||||
if kind == MAYBE_ANALYSIS:
|
||||
before[block] = set()
|
||||
after[block] = set()
|
||||
else:
|
||||
assert universe is not None, "Universe must be defined for a must analysis"
|
||||
before[block] = set(universe)
|
||||
after[block] = set(universe)
|
||||
|
||||
if backward:
|
||||
pred_map = cfg.succ
|
||||
succ_map = cfg.pred
|
||||
else:
|
||||
pred_map = cfg.pred
|
||||
succ_map = cfg.succ
|
||||
|
||||
# Run work list algorithm to generate in and out sets for each basic block.
|
||||
while worklist:
|
||||
label = worklist.pop()
|
||||
workset.remove(label)
|
||||
if pred_map[label]:
|
||||
new_before: set[T] | None = None
|
||||
for pred in pred_map[label]:
|
||||
if new_before is None:
|
||||
new_before = set(after[pred])
|
||||
elif kind == MAYBE_ANALYSIS:
|
||||
new_before |= after[pred]
|
||||
else:
|
||||
new_before &= after[pred]
|
||||
assert new_before is not None
|
||||
else:
|
||||
new_before = set(initial)
|
||||
before[label] = new_before
|
||||
new_after = (new_before - block_kill[label]) | block_gen[label]
|
||||
if new_after != after[label]:
|
||||
for succ in succ_map[label]:
|
||||
if succ not in workset:
|
||||
worklist.append(succ)
|
||||
workset.add(succ)
|
||||
after[label] = new_after
|
||||
|
||||
# Run algorithm for each basic block to generate opcode-level sets.
|
||||
op_before: dict[tuple[BasicBlock, int], set[T]] = {}
|
||||
op_after: dict[tuple[BasicBlock, int], set[T]] = {}
|
||||
for block in blocks:
|
||||
label = block
|
||||
cur = before[label]
|
||||
ops_enum: Iterator[tuple[int, Op]] = enumerate(block.ops)
|
||||
if backward:
|
||||
ops_enum = reversed(list(ops_enum))
|
||||
for idx, op in ops_enum:
|
||||
op_before[label, idx] = cur
|
||||
opgen, opkill = op.accept(gen_and_kill)
|
||||
cur = (cur - opkill) | opgen
|
||||
op_after[label, idx] = cur
|
||||
if backward:
|
||||
op_after, op_before = op_before, op_after
|
||||
|
||||
return AnalysisResult(op_before, op_after)
|
||||
Binary file not shown.
424
venv/lib/python3.12/site-packages/mypyc/analysis/ircheck.py
Normal file
424
venv/lib/python3.12/site-packages/mypyc/analysis/ircheck.py
Normal file
@@ -0,0 +1,424 @@
|
||||
"""Utilities for checking that internal ir is valid and consistent."""
|
||||
from __future__ import annotations
|
||||
|
||||
from mypyc.ir.func_ir import FUNC_STATICMETHOD, FuncIR
|
||||
from mypyc.ir.ops import (
|
||||
Assign,
|
||||
AssignMulti,
|
||||
BaseAssign,
|
||||
BasicBlock,
|
||||
Box,
|
||||
Branch,
|
||||
Call,
|
||||
CallC,
|
||||
Cast,
|
||||
ComparisonOp,
|
||||
ControlOp,
|
||||
DecRef,
|
||||
Extend,
|
||||
FloatComparisonOp,
|
||||
FloatNeg,
|
||||
FloatOp,
|
||||
GetAttr,
|
||||
GetElementPtr,
|
||||
Goto,
|
||||
IncRef,
|
||||
InitStatic,
|
||||
Integer,
|
||||
IntOp,
|
||||
KeepAlive,
|
||||
LoadAddress,
|
||||
LoadErrorValue,
|
||||
LoadGlobal,
|
||||
LoadLiteral,
|
||||
LoadMem,
|
||||
LoadStatic,
|
||||
MethodCall,
|
||||
Op,
|
||||
OpVisitor,
|
||||
RaiseStandardError,
|
||||
Register,
|
||||
Return,
|
||||
SetAttr,
|
||||
SetMem,
|
||||
Truncate,
|
||||
TupleGet,
|
||||
TupleSet,
|
||||
Unbox,
|
||||
Unreachable,
|
||||
Value,
|
||||
)
|
||||
from mypyc.ir.pprint import format_func
|
||||
from mypyc.ir.rtypes import (
|
||||
RArray,
|
||||
RInstance,
|
||||
RPrimitive,
|
||||
RType,
|
||||
RUnion,
|
||||
bytes_rprimitive,
|
||||
dict_rprimitive,
|
||||
int_rprimitive,
|
||||
is_float_rprimitive,
|
||||
is_object_rprimitive,
|
||||
list_rprimitive,
|
||||
range_rprimitive,
|
||||
set_rprimitive,
|
||||
str_rprimitive,
|
||||
tuple_rprimitive,
|
||||
)
|
||||
|
||||
|
||||
class FnError:
|
||||
def __init__(self, source: Op | BasicBlock, desc: str) -> None:
|
||||
self.source = source
|
||||
self.desc = desc
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
return (
|
||||
isinstance(other, FnError) and self.source == other.source and self.desc == other.desc
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"FnError(source={self.source}, desc={self.desc})"
|
||||
|
||||
|
||||
def check_func_ir(fn: FuncIR) -> list[FnError]:
|
||||
"""Applies validations to a given function ir and returns a list of errors found."""
|
||||
errors = []
|
||||
|
||||
op_set = set()
|
||||
|
||||
for block in fn.blocks:
|
||||
if not block.terminated:
|
||||
errors.append(
|
||||
FnError(source=block.ops[-1] if block.ops else block, desc="Block not terminated")
|
||||
)
|
||||
for op in block.ops[:-1]:
|
||||
if isinstance(op, ControlOp):
|
||||
errors.append(FnError(source=op, desc="Block has operations after control op"))
|
||||
|
||||
if op in op_set:
|
||||
errors.append(FnError(source=op, desc="Func has a duplicate op"))
|
||||
op_set.add(op)
|
||||
|
||||
errors.extend(check_op_sources_valid(fn))
|
||||
if errors:
|
||||
return errors
|
||||
|
||||
op_checker = OpChecker(fn)
|
||||
for block in fn.blocks:
|
||||
for op in block.ops:
|
||||
op.accept(op_checker)
|
||||
|
||||
return op_checker.errors
|
||||
|
||||
|
||||
class IrCheckException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def assert_func_ir_valid(fn: FuncIR) -> None:
|
||||
errors = check_func_ir(fn)
|
||||
if errors:
|
||||
raise IrCheckException(
|
||||
"Internal error: Generated invalid IR: \n"
|
||||
+ "\n".join(format_func(fn, [(e.source, e.desc) for e in errors]))
|
||||
)
|
||||
|
||||
|
||||
def check_op_sources_valid(fn: FuncIR) -> list[FnError]:
|
||||
errors = []
|
||||
valid_ops: set[Op] = set()
|
||||
valid_registers: set[Register] = set()
|
||||
|
||||
for block in fn.blocks:
|
||||
valid_ops.update(block.ops)
|
||||
|
||||
for op in block.ops:
|
||||
if isinstance(op, BaseAssign):
|
||||
valid_registers.add(op.dest)
|
||||
elif isinstance(op, LoadAddress) and isinstance(op.src, Register):
|
||||
valid_registers.add(op.src)
|
||||
|
||||
valid_registers.update(fn.arg_regs)
|
||||
|
||||
for block in fn.blocks:
|
||||
for op in block.ops:
|
||||
for source in op.sources():
|
||||
if isinstance(source, Integer):
|
||||
pass
|
||||
elif isinstance(source, Op):
|
||||
if source not in valid_ops:
|
||||
errors.append(
|
||||
FnError(
|
||||
source=op,
|
||||
desc=f"Invalid op reference to op of type {type(source).__name__}",
|
||||
)
|
||||
)
|
||||
elif isinstance(source, Register):
|
||||
if source not in valid_registers:
|
||||
errors.append(
|
||||
FnError(
|
||||
source=op, desc=f"Invalid op reference to register {source.name!r}"
|
||||
)
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
disjoint_types = {
|
||||
int_rprimitive.name,
|
||||
bytes_rprimitive.name,
|
||||
str_rprimitive.name,
|
||||
dict_rprimitive.name,
|
||||
list_rprimitive.name,
|
||||
set_rprimitive.name,
|
||||
tuple_rprimitive.name,
|
||||
range_rprimitive.name,
|
||||
}
|
||||
|
||||
|
||||
def can_coerce_to(src: RType, dest: RType) -> bool:
|
||||
"""Check if src can be assigned to dest_rtype.
|
||||
|
||||
Currently okay to have false positives.
|
||||
"""
|
||||
if isinstance(dest, RUnion):
|
||||
return any(can_coerce_to(src, d) for d in dest.items)
|
||||
|
||||
if isinstance(dest, RPrimitive):
|
||||
if isinstance(src, RPrimitive):
|
||||
# If either src or dest is a disjoint type, then they must both be.
|
||||
if src.name in disjoint_types and dest.name in disjoint_types:
|
||||
return src.name == dest.name
|
||||
return src.size == dest.size
|
||||
if isinstance(src, RInstance):
|
||||
return is_object_rprimitive(dest)
|
||||
if isinstance(src, RUnion):
|
||||
# IR doesn't have the ability to narrow unions based on
|
||||
# control flow, so cannot be a strict all() here.
|
||||
return any(can_coerce_to(s, dest) for s in src.items)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class OpChecker(OpVisitor[None]):
|
||||
def __init__(self, parent_fn: FuncIR) -> None:
|
||||
self.parent_fn = parent_fn
|
||||
self.errors: list[FnError] = []
|
||||
|
||||
def fail(self, source: Op, desc: str) -> None:
|
||||
self.errors.append(FnError(source=source, desc=desc))
|
||||
|
||||
def check_control_op_targets(self, op: ControlOp) -> None:
|
||||
for target in op.targets():
|
||||
if target not in self.parent_fn.blocks:
|
||||
self.fail(source=op, desc=f"Invalid control operation target: {target.label}")
|
||||
|
||||
def check_type_coercion(self, op: Op, src: RType, dest: RType) -> None:
|
||||
if not can_coerce_to(src, dest):
|
||||
self.fail(
|
||||
source=op, desc=f"Cannot coerce source type {src.name} to dest type {dest.name}"
|
||||
)
|
||||
|
||||
def check_compatibility(self, op: Op, t: RType, s: RType) -> None:
|
||||
if not can_coerce_to(t, s) or not can_coerce_to(s, t):
|
||||
self.fail(source=op, desc=f"{t.name} and {s.name} are not compatible")
|
||||
|
||||
def expect_float(self, op: Op, v: Value) -> None:
|
||||
if not is_float_rprimitive(v.type):
|
||||
self.fail(op, f"Float expected (actual type is {v.type})")
|
||||
|
||||
def expect_non_float(self, op: Op, v: Value) -> None:
|
||||
if is_float_rprimitive(v.type):
|
||||
self.fail(op, "Float not expected")
|
||||
|
||||
def visit_goto(self, op: Goto) -> None:
|
||||
self.check_control_op_targets(op)
|
||||
|
||||
def visit_branch(self, op: Branch) -> None:
|
||||
self.check_control_op_targets(op)
|
||||
|
||||
def visit_return(self, op: Return) -> None:
|
||||
self.check_type_coercion(op, op.value.type, self.parent_fn.decl.sig.ret_type)
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> None:
|
||||
# Unreachables are checked at a higher level since validation
|
||||
# requires access to the entire basic block.
|
||||
pass
|
||||
|
||||
def visit_assign(self, op: Assign) -> None:
|
||||
self.check_type_coercion(op, op.src.type, op.dest.type)
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> None:
|
||||
for src in op.src:
|
||||
assert isinstance(op.dest.type, RArray)
|
||||
self.check_type_coercion(op, src.type, op.dest.type.item_type)
|
||||
|
||||
def visit_load_error_value(self, op: LoadErrorValue) -> None:
|
||||
# Currently it is assumed that all types have an error value.
|
||||
# Once this is fixed we can validate that the rtype here actually
|
||||
# has an error value.
|
||||
pass
|
||||
|
||||
def check_tuple_items_valid_literals(self, op: LoadLiteral, t: tuple[object, ...]) -> None:
|
||||
for x in t:
|
||||
if x is not None and not isinstance(x, (str, bytes, bool, int, float, complex, tuple)):
|
||||
self.fail(op, f"Invalid type for item of tuple literal: {type(x)})")
|
||||
if isinstance(x, tuple):
|
||||
self.check_tuple_items_valid_literals(op, x)
|
||||
|
||||
def check_frozenset_items_valid_literals(self, op: LoadLiteral, s: frozenset[object]) -> None:
|
||||
for x in s:
|
||||
if x is None or isinstance(x, (str, bytes, bool, int, float, complex)):
|
||||
pass
|
||||
elif isinstance(x, tuple):
|
||||
self.check_tuple_items_valid_literals(op, x)
|
||||
else:
|
||||
self.fail(op, f"Invalid type for item of frozenset literal: {type(x)})")
|
||||
|
||||
def visit_load_literal(self, op: LoadLiteral) -> None:
|
||||
expected_type = None
|
||||
if op.value is None:
|
||||
expected_type = "builtins.object"
|
||||
elif isinstance(op.value, int):
|
||||
expected_type = "builtins.int"
|
||||
elif isinstance(op.value, str):
|
||||
expected_type = "builtins.str"
|
||||
elif isinstance(op.value, bytes):
|
||||
expected_type = "builtins.bytes"
|
||||
elif isinstance(op.value, bool):
|
||||
expected_type = "builtins.object"
|
||||
elif isinstance(op.value, float):
|
||||
expected_type = "builtins.float"
|
||||
elif isinstance(op.value, complex):
|
||||
expected_type = "builtins.object"
|
||||
elif isinstance(op.value, tuple):
|
||||
expected_type = "builtins.tuple"
|
||||
self.check_tuple_items_valid_literals(op, op.value)
|
||||
elif isinstance(op.value, frozenset):
|
||||
# There's no frozenset_rprimitive type since it'd be pretty useless so we just pretend
|
||||
# it's a set (when it's really a frozenset).
|
||||
expected_type = "builtins.set"
|
||||
self.check_frozenset_items_valid_literals(op, op.value)
|
||||
|
||||
assert expected_type is not None, "Missed a case for LoadLiteral check"
|
||||
|
||||
if op.type.name not in [expected_type, "builtins.object"]:
|
||||
self.fail(
|
||||
op,
|
||||
f"Invalid literal value for type: value has "
|
||||
f"type {expected_type}, but op has type {op.type.name}",
|
||||
)
|
||||
|
||||
def visit_get_attr(self, op: GetAttr) -> None:
|
||||
# Nothing to do.
|
||||
pass
|
||||
|
||||
def visit_set_attr(self, op: SetAttr) -> None:
|
||||
# Nothing to do.
|
||||
pass
|
||||
|
||||
# Static operations cannot be checked at the function level.
|
||||
def visit_load_static(self, op: LoadStatic) -> None:
|
||||
pass
|
||||
|
||||
def visit_init_static(self, op: InitStatic) -> None:
|
||||
pass
|
||||
|
||||
def visit_tuple_get(self, op: TupleGet) -> None:
|
||||
# Nothing to do.
|
||||
pass
|
||||
|
||||
def visit_tuple_set(self, op: TupleSet) -> None:
|
||||
# Nothing to do.
|
||||
pass
|
||||
|
||||
def visit_inc_ref(self, op: IncRef) -> None:
|
||||
# Nothing to do.
|
||||
pass
|
||||
|
||||
def visit_dec_ref(self, op: DecRef) -> None:
|
||||
# Nothing to do.
|
||||
pass
|
||||
|
||||
def visit_call(self, op: Call) -> None:
|
||||
# Length is checked in constructor, and return type is set
|
||||
# in a way that can't be incorrect
|
||||
for arg_value, arg_runtime in zip(op.args, op.fn.sig.args):
|
||||
self.check_type_coercion(op, arg_value.type, arg_runtime.type)
|
||||
|
||||
def visit_method_call(self, op: MethodCall) -> None:
|
||||
# Similar to above, but we must look up method first.
|
||||
method_decl = op.receiver_type.class_ir.method_decl(op.method)
|
||||
if method_decl.kind == FUNC_STATICMETHOD:
|
||||
decl_index = 0
|
||||
else:
|
||||
decl_index = 1
|
||||
|
||||
if len(op.args) + decl_index != len(method_decl.sig.args):
|
||||
self.fail(op, "Incorrect number of args for method call.")
|
||||
|
||||
# Skip the receiver argument (self)
|
||||
for arg_value, arg_runtime in zip(op.args, method_decl.sig.args[decl_index:]):
|
||||
self.check_type_coercion(op, arg_value.type, arg_runtime.type)
|
||||
|
||||
def visit_cast(self, op: Cast) -> None:
|
||||
pass
|
||||
|
||||
def visit_box(self, op: Box) -> None:
|
||||
pass
|
||||
|
||||
def visit_unbox(self, op: Unbox) -> None:
|
||||
pass
|
||||
|
||||
def visit_raise_standard_error(self, op: RaiseStandardError) -> None:
|
||||
pass
|
||||
|
||||
def visit_call_c(self, op: CallC) -> None:
|
||||
pass
|
||||
|
||||
def visit_truncate(self, op: Truncate) -> None:
|
||||
pass
|
||||
|
||||
def visit_extend(self, op: Extend) -> None:
|
||||
pass
|
||||
|
||||
def visit_load_global(self, op: LoadGlobal) -> None:
|
||||
pass
|
||||
|
||||
def visit_int_op(self, op: IntOp) -> None:
|
||||
self.expect_non_float(op, op.lhs)
|
||||
self.expect_non_float(op, op.rhs)
|
||||
|
||||
def visit_comparison_op(self, op: ComparisonOp) -> None:
|
||||
self.check_compatibility(op, op.lhs.type, op.rhs.type)
|
||||
self.expect_non_float(op, op.lhs)
|
||||
self.expect_non_float(op, op.rhs)
|
||||
|
||||
def visit_float_op(self, op: FloatOp) -> None:
|
||||
self.expect_float(op, op.lhs)
|
||||
self.expect_float(op, op.rhs)
|
||||
|
||||
def visit_float_neg(self, op: FloatNeg) -> None:
|
||||
self.expect_float(op, op.src)
|
||||
|
||||
def visit_float_comparison_op(self, op: FloatComparisonOp) -> None:
|
||||
self.expect_float(op, op.lhs)
|
||||
self.expect_float(op, op.rhs)
|
||||
|
||||
def visit_load_mem(self, op: LoadMem) -> None:
|
||||
pass
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> None:
|
||||
pass
|
||||
|
||||
def visit_get_element_ptr(self, op: GetElementPtr) -> None:
|
||||
pass
|
||||
|
||||
def visit_load_address(self, op: LoadAddress) -> None:
|
||||
pass
|
||||
|
||||
def visit_keep_alive(self, op: KeepAlive) -> None:
|
||||
pass
|
||||
Binary file not shown.
203
venv/lib/python3.12/site-packages/mypyc/analysis/selfleaks.py
Normal file
203
venv/lib/python3.12/site-packages/mypyc/analysis/selfleaks.py
Normal file
@@ -0,0 +1,203 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Set, Tuple
|
||||
|
||||
from mypyc.analysis.dataflow import CFG, MAYBE_ANALYSIS, AnalysisResult, run_analysis
|
||||
from mypyc.ir.ops import (
|
||||
Assign,
|
||||
AssignMulti,
|
||||
BasicBlock,
|
||||
Box,
|
||||
Branch,
|
||||
Call,
|
||||
CallC,
|
||||
Cast,
|
||||
ComparisonOp,
|
||||
Extend,
|
||||
FloatComparisonOp,
|
||||
FloatNeg,
|
||||
FloatOp,
|
||||
GetAttr,
|
||||
GetElementPtr,
|
||||
Goto,
|
||||
InitStatic,
|
||||
IntOp,
|
||||
KeepAlive,
|
||||
LoadAddress,
|
||||
LoadErrorValue,
|
||||
LoadGlobal,
|
||||
LoadLiteral,
|
||||
LoadMem,
|
||||
LoadStatic,
|
||||
MethodCall,
|
||||
OpVisitor,
|
||||
RaiseStandardError,
|
||||
Register,
|
||||
RegisterOp,
|
||||
Return,
|
||||
SetAttr,
|
||||
SetMem,
|
||||
Truncate,
|
||||
TupleGet,
|
||||
TupleSet,
|
||||
Unbox,
|
||||
Unreachable,
|
||||
)
|
||||
from mypyc.ir.rtypes import RInstance
|
||||
|
||||
GenAndKill = Tuple[Set[None], Set[None]]
|
||||
|
||||
CLEAN: GenAndKill = (set(), set())
|
||||
DIRTY: GenAndKill = ({None}, {None})
|
||||
|
||||
|
||||
class SelfLeakedVisitor(OpVisitor[GenAndKill]):
|
||||
"""Analyze whether 'self' may be seen by arbitrary code in '__init__'.
|
||||
|
||||
More formally, the set is not empty if along some path from IR entry point
|
||||
arbitrary code could have been executed that has access to 'self'.
|
||||
|
||||
(We don't consider access via 'gc.get_objects()'.)
|
||||
"""
|
||||
|
||||
def __init__(self, self_reg: Register) -> None:
|
||||
self.self_reg = self_reg
|
||||
|
||||
def visit_goto(self, op: Goto) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_branch(self, op: Branch) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_return(self, op: Return) -> GenAndKill:
|
||||
# Consider all exits from the function 'dirty' since they implicitly
|
||||
# cause 'self' to be returned.
|
||||
return DIRTY
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_assign(self, op: Assign) -> GenAndKill:
|
||||
if op.src is self.self_reg or op.dest is self.self_reg:
|
||||
return DIRTY
|
||||
return CLEAN
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_call(self, op: Call) -> GenAndKill:
|
||||
fn = op.fn
|
||||
if fn.class_name and fn.name == "__init__":
|
||||
self_type = op.fn.sig.args[0].type
|
||||
assert isinstance(self_type, RInstance)
|
||||
cl = self_type.class_ir
|
||||
if not cl.init_self_leak:
|
||||
return CLEAN
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_method_call(self, op: MethodCall) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_load_error_value(self, op: LoadErrorValue) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_load_literal(self, op: LoadLiteral) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_get_attr(self, op: GetAttr) -> GenAndKill:
|
||||
cl = op.class_type.class_ir
|
||||
if cl.get_method(op.attr):
|
||||
# Property -- calls a function
|
||||
return self.check_register_op(op)
|
||||
return CLEAN
|
||||
|
||||
def visit_set_attr(self, op: SetAttr) -> GenAndKill:
|
||||
cl = op.class_type.class_ir
|
||||
if cl.get_method(op.attr):
|
||||
# Property - calls a function
|
||||
return self.check_register_op(op)
|
||||
return CLEAN
|
||||
|
||||
def visit_load_static(self, op: LoadStatic) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_init_static(self, op: InitStatic) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_tuple_get(self, op: TupleGet) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_tuple_set(self, op: TupleSet) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_box(self, op: Box) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_unbox(self, op: Unbox) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_cast(self, op: Cast) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_call_c(self, op: CallC) -> GenAndKill:
|
||||
return self.check_register_op(op)
|
||||
|
||||
def visit_truncate(self, op: Truncate) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_extend(self, op: Extend) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_load_global(self, op: LoadGlobal) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_int_op(self, op: IntOp) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_comparison_op(self, op: ComparisonOp) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_float_op(self, op: FloatOp) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_float_neg(self, op: FloatNeg) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_float_comparison_op(self, op: FloatComparisonOp) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_load_mem(self, op: LoadMem) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_load_address(self, op: LoadAddress) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def visit_keep_alive(self, op: KeepAlive) -> GenAndKill:
|
||||
return CLEAN
|
||||
|
||||
def check_register_op(self, op: RegisterOp) -> GenAndKill:
|
||||
if any(src is self.self_reg for src in op.sources()):
|
||||
return DIRTY
|
||||
return CLEAN
|
||||
|
||||
|
||||
def analyze_self_leaks(
|
||||
blocks: list[BasicBlock], self_reg: Register, cfg: CFG
|
||||
) -> AnalysisResult[None]:
|
||||
return run_analysis(
|
||||
blocks=blocks,
|
||||
cfg=cfg,
|
||||
gen_and_kill=SelfLeakedVisitor(self_reg),
|
||||
initial=set(),
|
||||
backward=False,
|
||||
kind=MAYBE_ANALYSIS,
|
||||
)
|
||||
BIN
venv/lib/python3.12/site-packages/mypyc/build.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/mypyc/build.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
612
venv/lib/python3.12/site-packages/mypyc/build.py
Normal file
612
venv/lib/python3.12/site-packages/mypyc/build.py
Normal file
@@ -0,0 +1,612 @@
|
||||
"""Support for building extensions using mypyc with distutils or setuptools
|
||||
|
||||
The main entry point is mypycify, which produces a list of extension
|
||||
modules to be passed to setup. A trivial setup.py for a mypyc built
|
||||
project, then, looks like:
|
||||
|
||||
from setuptools import setup
|
||||
from mypyc.build import mypycify
|
||||
|
||||
setup(name='test_module',
|
||||
ext_modules=mypycify(['foo.py']),
|
||||
)
|
||||
|
||||
See the mypycify docs for additional arguments.
|
||||
|
||||
mypycify can integrate with either distutils or setuptools, but needs
|
||||
to know at import-time whether it is using distutils or setuputils. We
|
||||
hackily decide based on whether setuptools has been imported already.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, NoReturn, Union, cast
|
||||
|
||||
from mypy.build import BuildSource
|
||||
from mypy.errors import CompileError
|
||||
from mypy.fscache import FileSystemCache
|
||||
from mypy.main import process_options
|
||||
from mypy.options import Options
|
||||
from mypy.util import write_junit_xml
|
||||
from mypyc.codegen import emitmodule
|
||||
from mypyc.common import RUNTIME_C_FILES, shared_lib_name
|
||||
from mypyc.errors import Errors
|
||||
from mypyc.ir.pprint import format_modules
|
||||
from mypyc.namegen import exported_name
|
||||
from mypyc.options import CompilerOptions
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
if TYPE_CHECKING:
|
||||
from distutils.core import Extension as _distutils_Extension
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from setuptools import Extension as _setuptools_Extension
|
||||
|
||||
Extension: TypeAlias = Union[_setuptools_Extension, _distutils_Extension]
|
||||
|
||||
try:
|
||||
# Import setuptools so that it monkey-patch overrides distutils
|
||||
import setuptools
|
||||
except ImportError:
|
||||
pass
|
||||
from distutils import ccompiler, sysconfig
|
||||
else:
|
||||
import setuptools
|
||||
from setuptools import Extension
|
||||
from setuptools._distutils import (
|
||||
ccompiler as _ccompiler, # type: ignore[attr-defined]
|
||||
sysconfig as _sysconfig, # type: ignore[attr-defined]
|
||||
)
|
||||
|
||||
ccompiler = _ccompiler
|
||||
sysconfig = _sysconfig
|
||||
|
||||
|
||||
def get_extension() -> type[Extension]:
|
||||
# We can work with either setuptools or distutils, and pick setuptools
|
||||
# if it has been imported.
|
||||
use_setuptools = "setuptools" in sys.modules
|
||||
extension_class: type[Extension]
|
||||
|
||||
if sys.version_info < (3, 12) and not use_setuptools:
|
||||
import distutils.core
|
||||
|
||||
extension_class = distutils.core.Extension
|
||||
else:
|
||||
if not use_setuptools:
|
||||
sys.exit("error: setuptools not installed")
|
||||
extension_class = setuptools.Extension
|
||||
|
||||
return extension_class
|
||||
|
||||
|
||||
def setup_mypycify_vars() -> None:
|
||||
"""Rewrite a bunch of config vars in pretty dubious ways."""
|
||||
# There has to be a better approach to this.
|
||||
|
||||
# The vars can contain ints but we only work with str ones
|
||||
vars = cast(Dict[str, str], sysconfig.get_config_vars())
|
||||
if sys.platform == "darwin":
|
||||
# Disable building 32-bit binaries, since we generate too much code
|
||||
# for a 32-bit Mach-O object. There has to be a better way to do this.
|
||||
vars["LDSHARED"] = vars["LDSHARED"].replace("-arch i386", "")
|
||||
vars["LDFLAGS"] = vars["LDFLAGS"].replace("-arch i386", "")
|
||||
vars["CFLAGS"] = vars["CFLAGS"].replace("-arch i386", "")
|
||||
|
||||
|
||||
def fail(message: str) -> NoReturn:
|
||||
# TODO: Is there something else we should do to fail?
|
||||
sys.exit(message)
|
||||
|
||||
|
||||
def emit_messages(options: Options, messages: list[str], dt: float, serious: bool = False) -> None:
|
||||
# ... you know, just in case.
|
||||
if options.junit_xml:
|
||||
py_version = f"{options.python_version[0]}_{options.python_version[1]}"
|
||||
write_junit_xml(dt, serious, messages, options.junit_xml, py_version, options.platform)
|
||||
if messages:
|
||||
print("\n".join(messages))
|
||||
|
||||
|
||||
def get_mypy_config(
|
||||
mypy_options: list[str],
|
||||
only_compile_paths: Iterable[str] | None,
|
||||
compiler_options: CompilerOptions,
|
||||
fscache: FileSystemCache | None,
|
||||
) -> tuple[list[BuildSource], list[BuildSource], Options]:
|
||||
"""Construct mypy BuildSources and Options from file and options lists"""
|
||||
all_sources, options = process_options(mypy_options, fscache=fscache)
|
||||
if only_compile_paths is not None:
|
||||
paths_set = set(only_compile_paths)
|
||||
mypyc_sources = [s for s in all_sources if s.path in paths_set]
|
||||
else:
|
||||
mypyc_sources = all_sources
|
||||
|
||||
if compiler_options.separate:
|
||||
mypyc_sources = [
|
||||
src for src in mypyc_sources if src.path and not src.path.endswith("__init__.py")
|
||||
]
|
||||
|
||||
if not mypyc_sources:
|
||||
return mypyc_sources, all_sources, options
|
||||
|
||||
# Override whatever python_version is inferred from the .ini file,
|
||||
# and set the python_version to be the currently used version.
|
||||
options.python_version = sys.version_info[:2]
|
||||
|
||||
if options.python_version[0] == 2:
|
||||
fail("Python 2 not supported")
|
||||
if not options.strict_optional:
|
||||
fail("Disabling strict optional checking not supported")
|
||||
options.show_traceback = True
|
||||
# Needed to get types for all AST nodes
|
||||
options.export_types = True
|
||||
# We use mypy incremental mode when doing separate/incremental mypyc compilation
|
||||
options.incremental = compiler_options.separate
|
||||
options.preserve_asts = True
|
||||
|
||||
for source in mypyc_sources:
|
||||
options.per_module_options.setdefault(source.module, {})["mypyc"] = True
|
||||
|
||||
return mypyc_sources, all_sources, options
|
||||
|
||||
|
||||
def generate_c_extension_shim(
|
||||
full_module_name: str, module_name: str, dir_name: str, group_name: str
|
||||
) -> str:
|
||||
"""Create a C extension shim with a passthrough PyInit function.
|
||||
|
||||
Arguments:
|
||||
full_module_name: the dotted full module name
|
||||
module_name: the final component of the module name
|
||||
dir_name: the directory to place source code
|
||||
group_name: the name of the group
|
||||
"""
|
||||
cname = "%s.c" % full_module_name.replace(".", os.sep)
|
||||
cpath = os.path.join(dir_name, cname)
|
||||
|
||||
# We load the C extension shim template from a file.
|
||||
# (So that the file could be reused as a bazel template also.)
|
||||
with open(os.path.join(include_dir(), "module_shim.tmpl")) as f:
|
||||
shim_template = f.read()
|
||||
|
||||
write_file(
|
||||
cpath,
|
||||
shim_template.format(
|
||||
modname=module_name,
|
||||
libname=shared_lib_name(group_name),
|
||||
full_modname=exported_name(full_module_name),
|
||||
),
|
||||
)
|
||||
|
||||
return cpath
|
||||
|
||||
|
||||
def group_name(modules: list[str]) -> str:
|
||||
"""Produce a probably unique name for a group from a list of module names."""
|
||||
if len(modules) == 1:
|
||||
return modules[0]
|
||||
|
||||
h = hashlib.sha1()
|
||||
h.update(",".join(modules).encode())
|
||||
return h.hexdigest()[:20]
|
||||
|
||||
|
||||
def include_dir() -> str:
|
||||
"""Find the path of the lib-rt dir that needs to be included"""
|
||||
return os.path.join(os.path.abspath(os.path.dirname(__file__)), "lib-rt")
|
||||
|
||||
|
||||
def generate_c(
|
||||
sources: list[BuildSource],
|
||||
options: Options,
|
||||
groups: emitmodule.Groups,
|
||||
fscache: FileSystemCache,
|
||||
compiler_options: CompilerOptions,
|
||||
) -> tuple[list[list[tuple[str, str]]], str]:
|
||||
"""Drive the actual core compilation step.
|
||||
|
||||
The groups argument describes how modules are assigned to C
|
||||
extension modules. See the comments on the Groups type in
|
||||
mypyc.emitmodule for details.
|
||||
|
||||
Returns the C source code and (for debugging) the pretty printed IR.
|
||||
"""
|
||||
t0 = time.time()
|
||||
|
||||
try:
|
||||
result = emitmodule.parse_and_typecheck(
|
||||
sources, options, compiler_options, groups, fscache
|
||||
)
|
||||
except CompileError as e:
|
||||
emit_messages(options, e.messages, time.time() - t0, serious=(not e.use_stdout))
|
||||
sys.exit(1)
|
||||
|
||||
t1 = time.time()
|
||||
if result.errors:
|
||||
emit_messages(options, result.errors, t1 - t0)
|
||||
sys.exit(1)
|
||||
|
||||
if compiler_options.verbose:
|
||||
print(f"Parsed and typechecked in {t1 - t0:.3f}s")
|
||||
|
||||
errors = Errors(options)
|
||||
modules, ctext = emitmodule.compile_modules_to_c(
|
||||
result, compiler_options=compiler_options, errors=errors, groups=groups
|
||||
)
|
||||
t2 = time.time()
|
||||
emit_messages(options, errors.new_messages(), t2 - t1)
|
||||
if errors.num_errors:
|
||||
# No need to stop the build if only warnings were emitted.
|
||||
sys.exit(1)
|
||||
|
||||
if compiler_options.verbose:
|
||||
print(f"Compiled to C in {t2 - t1:.3f}s")
|
||||
|
||||
return ctext, "\n".join(format_modules(modules))
|
||||
|
||||
|
||||
def build_using_shared_lib(
|
||||
sources: list[BuildSource],
|
||||
group_name: str,
|
||||
cfiles: list[str],
|
||||
deps: list[str],
|
||||
build_dir: str,
|
||||
extra_compile_args: list[str],
|
||||
) -> list[Extension]:
|
||||
"""Produce the list of extension modules when a shared library is needed.
|
||||
|
||||
This creates one shared library extension module that all of the
|
||||
others import and then one shim extension module for each
|
||||
module in the build, that simply calls an initialization function
|
||||
in the shared library.
|
||||
|
||||
The shared library (which lib_name is the name of) is a python
|
||||
extension module that exports the real initialization functions in
|
||||
Capsules stored in module attributes.
|
||||
"""
|
||||
extensions = [
|
||||
get_extension()(
|
||||
shared_lib_name(group_name),
|
||||
sources=cfiles,
|
||||
include_dirs=[include_dir(), build_dir],
|
||||
depends=deps,
|
||||
extra_compile_args=extra_compile_args,
|
||||
)
|
||||
]
|
||||
|
||||
for source in sources:
|
||||
module_name = source.module.split(".")[-1]
|
||||
shim_file = generate_c_extension_shim(source.module, module_name, build_dir, group_name)
|
||||
|
||||
# We include the __init__ in the "module name" we stick in the Extension,
|
||||
# since this seems to be needed for it to end up in the right place.
|
||||
full_module_name = source.module
|
||||
assert source.path
|
||||
if os.path.split(source.path)[1] == "__init__.py":
|
||||
full_module_name += ".__init__"
|
||||
extensions.append(
|
||||
get_extension()(
|
||||
full_module_name, sources=[shim_file], extra_compile_args=extra_compile_args
|
||||
)
|
||||
)
|
||||
|
||||
return extensions
|
||||
|
||||
|
||||
def build_single_module(
|
||||
sources: list[BuildSource], cfiles: list[str], extra_compile_args: list[str]
|
||||
) -> list[Extension]:
|
||||
"""Produce the list of extension modules for a standalone extension.
|
||||
|
||||
This contains just one module, since there is no need for a shared module.
|
||||
"""
|
||||
return [
|
||||
get_extension()(
|
||||
sources[0].module,
|
||||
sources=cfiles,
|
||||
include_dirs=[include_dir()],
|
||||
extra_compile_args=extra_compile_args,
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def write_file(path: str, contents: str) -> None:
|
||||
"""Write data into a file.
|
||||
|
||||
If the file already exists and has the same contents we
|
||||
want to write, skip writing so as to preserve the mtime
|
||||
and avoid triggering recompilation.
|
||||
"""
|
||||
# We encode it ourselves and open the files as binary to avoid windows
|
||||
# newline translation
|
||||
encoded_contents = contents.encode("utf-8")
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
old_contents: bytes | None = f.read()
|
||||
except OSError:
|
||||
old_contents = None
|
||||
if old_contents != encoded_contents:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "wb") as g:
|
||||
g.write(encoded_contents)
|
||||
|
||||
# Fudge the mtime forward because otherwise when two builds happen close
|
||||
# together (like in a test) setuptools might not realize the source is newer
|
||||
# than the new artifact.
|
||||
# XXX: This is bad though.
|
||||
new_mtime = os.stat(path).st_mtime + 1
|
||||
os.utime(path, times=(new_mtime, new_mtime))
|
||||
|
||||
|
||||
def construct_groups(
|
||||
sources: list[BuildSource],
|
||||
separate: bool | list[tuple[list[str], str | None]],
|
||||
use_shared_lib: bool,
|
||||
) -> emitmodule.Groups:
|
||||
"""Compute Groups given the input source list and separate configs.
|
||||
|
||||
separate is the user-specified configuration for how to assign
|
||||
modules to compilation groups (see mypycify docstring for details).
|
||||
|
||||
This takes that and expands it into our internal representation of
|
||||
group configuration, documented in mypyc.emitmodule's definition
|
||||
of Group.
|
||||
"""
|
||||
|
||||
if separate is True:
|
||||
groups: emitmodule.Groups = [([source], None) for source in sources]
|
||||
elif isinstance(separate, list):
|
||||
groups = []
|
||||
used_sources = set()
|
||||
for files, name in separate:
|
||||
group_sources = [src for src in sources if src.path in files]
|
||||
groups.append((group_sources, name))
|
||||
used_sources.update(group_sources)
|
||||
unused_sources = [src for src in sources if src not in used_sources]
|
||||
if unused_sources:
|
||||
groups.extend([([source], None) for source in unused_sources])
|
||||
else:
|
||||
groups = [(sources, None)]
|
||||
|
||||
# Generate missing names
|
||||
for i, (group, name) in enumerate(groups):
|
||||
if use_shared_lib and not name:
|
||||
name = group_name([source.module for source in group])
|
||||
groups[i] = (group, name)
|
||||
|
||||
return groups
|
||||
|
||||
|
||||
def get_header_deps(cfiles: list[tuple[str, str]]) -> list[str]:
|
||||
"""Find all the headers used by a group of cfiles.
|
||||
|
||||
We do this by just regexping the source, which is a bit simpler than
|
||||
properly plumbing the data through.
|
||||
|
||||
Arguments:
|
||||
cfiles: A list of (file name, file contents) pairs.
|
||||
"""
|
||||
headers: set[str] = set()
|
||||
for _, contents in cfiles:
|
||||
headers.update(re.findall(r'#include "(.*)"', contents))
|
||||
|
||||
return sorted(headers)
|
||||
|
||||
|
||||
def mypyc_build(
|
||||
paths: list[str],
|
||||
compiler_options: CompilerOptions,
|
||||
*,
|
||||
separate: bool | list[tuple[list[str], str | None]] = False,
|
||||
only_compile_paths: Iterable[str] | None = None,
|
||||
skip_cgen_input: Any | None = None,
|
||||
always_use_shared_lib: bool = False,
|
||||
) -> tuple[emitmodule.Groups, list[tuple[list[str], list[str]]]]:
|
||||
"""Do the front and middle end of mypyc building, producing and writing out C source."""
|
||||
fscache = FileSystemCache()
|
||||
mypyc_sources, all_sources, options = get_mypy_config(
|
||||
paths, only_compile_paths, compiler_options, fscache
|
||||
)
|
||||
|
||||
# We generate a shared lib if there are multiple modules or if any
|
||||
# of the modules are in package. (Because I didn't want to fuss
|
||||
# around with making the single module code handle packages.)
|
||||
use_shared_lib = (
|
||||
len(mypyc_sources) > 1
|
||||
or any("." in x.module for x in mypyc_sources)
|
||||
or always_use_shared_lib
|
||||
)
|
||||
|
||||
groups = construct_groups(mypyc_sources, separate, use_shared_lib)
|
||||
|
||||
# We let the test harness just pass in the c file contents instead
|
||||
# so that it can do a corner-cutting version without full stubs.
|
||||
if not skip_cgen_input:
|
||||
group_cfiles, ops_text = generate_c(
|
||||
all_sources, options, groups, fscache, compiler_options=compiler_options
|
||||
)
|
||||
# TODO: unique names?
|
||||
write_file(os.path.join(compiler_options.target_dir, "ops.txt"), ops_text)
|
||||
else:
|
||||
group_cfiles = skip_cgen_input
|
||||
|
||||
# Write out the generated C and collect the files for each group
|
||||
# Should this be here??
|
||||
group_cfilenames: list[tuple[list[str], list[str]]] = []
|
||||
for cfiles in group_cfiles:
|
||||
cfilenames = []
|
||||
for cfile, ctext in cfiles:
|
||||
cfile = os.path.join(compiler_options.target_dir, cfile)
|
||||
write_file(cfile, ctext)
|
||||
if os.path.splitext(cfile)[1] == ".c":
|
||||
cfilenames.append(cfile)
|
||||
|
||||
deps = [os.path.join(compiler_options.target_dir, dep) for dep in get_header_deps(cfiles)]
|
||||
group_cfilenames.append((cfilenames, deps))
|
||||
|
||||
return groups, group_cfilenames
|
||||
|
||||
|
||||
def mypycify(
|
||||
paths: list[str],
|
||||
*,
|
||||
only_compile_paths: Iterable[str] | None = None,
|
||||
verbose: bool = False,
|
||||
opt_level: str = "3",
|
||||
debug_level: str = "1",
|
||||
strip_asserts: bool = False,
|
||||
multi_file: bool = False,
|
||||
separate: bool | list[tuple[list[str], str | None]] = False,
|
||||
skip_cgen_input: Any | None = None,
|
||||
target_dir: str | None = None,
|
||||
include_runtime_files: bool | None = None,
|
||||
) -> list[Extension]:
|
||||
"""Main entry point to building using mypyc.
|
||||
|
||||
This produces a list of Extension objects that should be passed as the
|
||||
ext_modules parameter to setup.
|
||||
|
||||
Arguments:
|
||||
paths: A list of file paths to build. It may also contain mypy options.
|
||||
only_compile_paths: If not None, an iterable of paths that are to be
|
||||
the only modules compiled, even if other modules
|
||||
appear in the mypy command line given to paths.
|
||||
(These modules must still be passed to paths.)
|
||||
|
||||
verbose: Should mypyc be more verbose. Defaults to false.
|
||||
|
||||
opt_level: The optimization level, as a string. Defaults to '3' (meaning '-O3').
|
||||
debug_level: The debug level, as a string. Defaults to '1' (meaning '-g1').
|
||||
strip_asserts: Should asserts be stripped from the generated code.
|
||||
|
||||
multi_file: Should each Python module be compiled into its own C source file.
|
||||
This can reduce compile time and memory requirements at the likely
|
||||
cost of runtime performance of compiled code. Defaults to false.
|
||||
separate: Should compiled modules be placed in separate extension modules.
|
||||
If False, all modules are placed in a single shared library.
|
||||
If True, every module is placed in its own library.
|
||||
Otherwise separate should be a list of
|
||||
(file name list, optional shared library name) pairs specifying
|
||||
groups of files that should be placed in the same shared library
|
||||
(while all other modules will be placed in its own library).
|
||||
|
||||
Each group can be compiled independently, which can
|
||||
speed up compilation, but calls between groups can
|
||||
be slower than calls within a group and can't be
|
||||
inlined.
|
||||
target_dir: The directory to write C output files. Defaults to 'build'.
|
||||
include_runtime_files: If not None, whether the mypyc runtime library
|
||||
should be directly #include'd instead of linked
|
||||
separately in order to reduce compiler invocations.
|
||||
Defaults to False in multi_file mode, True otherwise.
|
||||
"""
|
||||
|
||||
# Figure out our configuration
|
||||
compiler_options = CompilerOptions(
|
||||
strip_asserts=strip_asserts,
|
||||
multi_file=multi_file,
|
||||
verbose=verbose,
|
||||
separate=separate is not False,
|
||||
target_dir=target_dir,
|
||||
include_runtime_files=include_runtime_files,
|
||||
)
|
||||
|
||||
# Generate all the actual important C code
|
||||
groups, group_cfilenames = mypyc_build(
|
||||
paths,
|
||||
only_compile_paths=only_compile_paths,
|
||||
compiler_options=compiler_options,
|
||||
separate=separate,
|
||||
skip_cgen_input=skip_cgen_input,
|
||||
)
|
||||
|
||||
# Mess around with setuptools and actually get the thing built
|
||||
setup_mypycify_vars()
|
||||
|
||||
# Create a compiler object so we can make decisions based on what
|
||||
# compiler is being used. typeshed is missing some attributes on the
|
||||
# compiler object so we give it type Any
|
||||
compiler: Any = ccompiler.new_compiler()
|
||||
sysconfig.customize_compiler(compiler)
|
||||
|
||||
build_dir = compiler_options.target_dir
|
||||
|
||||
cflags: list[str] = []
|
||||
if compiler.compiler_type == "unix":
|
||||
cflags += [
|
||||
f"-O{opt_level}",
|
||||
f"-g{debug_level}",
|
||||
"-Werror",
|
||||
"-Wno-unused-function",
|
||||
"-Wno-unused-label",
|
||||
"-Wno-unreachable-code",
|
||||
"-Wno-unused-variable",
|
||||
"-Wno-unused-command-line-argument",
|
||||
"-Wno-unknown-warning-option",
|
||||
"-Wno-unused-but-set-variable",
|
||||
"-Wno-ignored-optimization-argument",
|
||||
# Disables C Preprocessor (cpp) warnings
|
||||
# See https://github.com/mypyc/mypyc/issues/956
|
||||
"-Wno-cpp",
|
||||
]
|
||||
elif compiler.compiler_type == "msvc":
|
||||
# msvc doesn't have levels, '/O2' is full and '/Od' is disable
|
||||
if opt_level == "0":
|
||||
opt_level = "d"
|
||||
elif opt_level in ("1", "2", "3"):
|
||||
opt_level = "2"
|
||||
if debug_level == "0":
|
||||
debug_level = "NONE"
|
||||
elif debug_level == "1":
|
||||
debug_level = "FASTLINK"
|
||||
elif debug_level in ("2", "3"):
|
||||
debug_level = "FULL"
|
||||
cflags += [
|
||||
f"/O{opt_level}",
|
||||
f"/DEBUG:{debug_level}",
|
||||
"/wd4102", # unreferenced label
|
||||
"/wd4101", # unreferenced local variable
|
||||
"/wd4146", # negating unsigned int
|
||||
]
|
||||
if multi_file:
|
||||
# Disable whole program optimization in multi-file mode so
|
||||
# that we actually get the compilation speed and memory
|
||||
# use wins that multi-file mode is intended for.
|
||||
cflags += ["/GL-", "/wd9025"] # warning about overriding /GL
|
||||
|
||||
# If configured to (defaults to yes in multi-file mode), copy the
|
||||
# runtime library in. Otherwise it just gets #included to save on
|
||||
# compiler invocations.
|
||||
shared_cfilenames = []
|
||||
if not compiler_options.include_runtime_files:
|
||||
for name in RUNTIME_C_FILES:
|
||||
rt_file = os.path.join(build_dir, name)
|
||||
with open(os.path.join(include_dir(), name), encoding="utf-8") as f:
|
||||
write_file(rt_file, f.read())
|
||||
shared_cfilenames.append(rt_file)
|
||||
|
||||
extensions = []
|
||||
for (group_sources, lib_name), (cfilenames, deps) in zip(groups, group_cfilenames):
|
||||
if lib_name:
|
||||
extensions.extend(
|
||||
build_using_shared_lib(
|
||||
group_sources,
|
||||
lib_name,
|
||||
cfilenames + shared_cfilenames,
|
||||
deps,
|
||||
build_dir,
|
||||
cflags,
|
||||
)
|
||||
)
|
||||
else:
|
||||
extensions.extend(
|
||||
build_single_module(group_sources, cfilenames + shared_cfilenames, cflags)
|
||||
)
|
||||
|
||||
return extensions
|
||||
Binary file not shown.
Binary file not shown.
54
venv/lib/python3.12/site-packages/mypyc/codegen/cstring.py
Normal file
54
venv/lib/python3.12/site-packages/mypyc/codegen/cstring.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Encode valid C string literals from Python strings.
|
||||
|
||||
If a character is not allowed in C string literals, it is either emitted
|
||||
as a simple escape sequence (e.g. '\\n'), or an octal escape sequence
|
||||
with exactly three digits ('\\oXXX'). Question marks are escaped to
|
||||
prevent trigraphs in the string literal from being interpreted. Note
|
||||
that '\\?' is an invalid escape sequence in Python.
|
||||
|
||||
Consider the string literal "AB\\xCDEF". As one would expect, Python
|
||||
parses it as ['A', 'B', 0xCD, 'E', 'F']. However, the C standard
|
||||
specifies that all hexadecimal digits immediately following '\\x' will
|
||||
be interpreted as part of the escape sequence. Therefore, it is
|
||||
unexpectedly parsed as ['A', 'B', 0xCDEF].
|
||||
|
||||
Emitting ("AB\\xCD" "EF") would avoid this behaviour. However, we opt
|
||||
for simplicity and use octal escape sequences instead. They do not
|
||||
suffer from the same issue as they are defined to parse at most three
|
||||
octal digits.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import string
|
||||
from typing import Final
|
||||
|
||||
CHAR_MAP: Final = [f"\\{i:03o}" for i in range(256)]
|
||||
|
||||
# It is safe to use string.printable as it always uses the C locale.
|
||||
for c in string.printable:
|
||||
CHAR_MAP[ord(c)] = c
|
||||
|
||||
# These assignments must come last because we prioritize simple escape
|
||||
# sequences over any other representation.
|
||||
for c in ("'", '"', "\\", "a", "b", "f", "n", "r", "t", "v"):
|
||||
escaped = f"\\{c}"
|
||||
decoded = escaped.encode("ascii").decode("unicode_escape")
|
||||
CHAR_MAP[ord(decoded)] = escaped
|
||||
|
||||
# This escape sequence is invalid in Python.
|
||||
CHAR_MAP[ord("?")] = r"\?"
|
||||
|
||||
|
||||
def encode_bytes_as_c_string(b: bytes) -> str:
|
||||
"""Produce contents of a C string literal for a byte string, without quotes."""
|
||||
escaped = "".join([CHAR_MAP[i] for i in b])
|
||||
return escaped
|
||||
|
||||
|
||||
def c_string_initializer(value: bytes) -> str:
|
||||
"""Create initializer for a C char[]/ char * variable from a string.
|
||||
|
||||
For example, if value if b'foo', the result would be '"foo"'.
|
||||
"""
|
||||
return '"' + encode_bytes_as_c_string(value) + '"'
|
||||
Binary file not shown.
1193
venv/lib/python3.12/site-packages/mypyc/codegen/emit.py
Normal file
1193
venv/lib/python3.12/site-packages/mypyc/codegen/emit.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
1057
venv/lib/python3.12/site-packages/mypyc/codegen/emitclass.py
Normal file
1057
venv/lib/python3.12/site-packages/mypyc/codegen/emitclass.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
841
venv/lib/python3.12/site-packages/mypyc/codegen/emitfunc.py
Normal file
841
venv/lib/python3.12/site-packages/mypyc/codegen/emitfunc.py
Normal file
@@ -0,0 +1,841 @@
|
||||
"""Code generation for native function bodies."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
from mypyc.analysis.blockfreq import frequently_executed_blocks
|
||||
from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer
|
||||
from mypyc.common import MODULE_PREFIX, NATIVE_PREFIX, REG_PREFIX, STATIC_PREFIX, TYPE_PREFIX
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR, all_values
|
||||
from mypyc.ir.ops import (
|
||||
ERR_FALSE,
|
||||
NAMESPACE_MODULE,
|
||||
NAMESPACE_STATIC,
|
||||
NAMESPACE_TYPE,
|
||||
Assign,
|
||||
AssignMulti,
|
||||
BasicBlock,
|
||||
Box,
|
||||
Branch,
|
||||
Call,
|
||||
CallC,
|
||||
Cast,
|
||||
ComparisonOp,
|
||||
ControlOp,
|
||||
DecRef,
|
||||
Extend,
|
||||
Float,
|
||||
FloatComparisonOp,
|
||||
FloatNeg,
|
||||
FloatOp,
|
||||
GetAttr,
|
||||
GetElementPtr,
|
||||
Goto,
|
||||
IncRef,
|
||||
InitStatic,
|
||||
Integer,
|
||||
IntOp,
|
||||
KeepAlive,
|
||||
LoadAddress,
|
||||
LoadErrorValue,
|
||||
LoadGlobal,
|
||||
LoadLiteral,
|
||||
LoadMem,
|
||||
LoadStatic,
|
||||
MethodCall,
|
||||
Op,
|
||||
OpVisitor,
|
||||
RaiseStandardError,
|
||||
Register,
|
||||
Return,
|
||||
SetAttr,
|
||||
SetMem,
|
||||
Truncate,
|
||||
TupleGet,
|
||||
TupleSet,
|
||||
Unbox,
|
||||
Unreachable,
|
||||
Value,
|
||||
)
|
||||
from mypyc.ir.pprint import generate_names_for_ir
|
||||
from mypyc.ir.rtypes import (
|
||||
RArray,
|
||||
RStruct,
|
||||
RTuple,
|
||||
RType,
|
||||
is_int32_rprimitive,
|
||||
is_int64_rprimitive,
|
||||
is_int_rprimitive,
|
||||
is_pointer_rprimitive,
|
||||
is_tagged,
|
||||
)
|
||||
|
||||
|
||||
def native_function_type(fn: FuncIR, emitter: Emitter) -> str:
|
||||
args = ", ".join(emitter.ctype(arg.type) for arg in fn.args) or "void"
|
||||
ret = emitter.ctype(fn.ret_type)
|
||||
return f"{ret} (*)({args})"
|
||||
|
||||
|
||||
def native_function_header(fn: FuncDecl, emitter: Emitter) -> str:
|
||||
args = []
|
||||
for arg in fn.sig.args:
|
||||
args.append(f"{emitter.ctype_spaced(arg.type)}{REG_PREFIX}{arg.name}")
|
||||
|
||||
return "{ret_type}{name}({args})".format(
|
||||
ret_type=emitter.ctype_spaced(fn.sig.ret_type),
|
||||
name=emitter.native_function_name(fn),
|
||||
args=", ".join(args) or "void",
|
||||
)
|
||||
|
||||
|
||||
def generate_native_function(
|
||||
fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
|
||||
) -> None:
|
||||
declarations = Emitter(emitter.context)
|
||||
names = generate_names_for_ir(fn.arg_regs, fn.blocks)
|
||||
body = Emitter(emitter.context, names)
|
||||
visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name)
|
||||
|
||||
declarations.emit_line(f"{native_function_header(fn.decl, emitter)} {{")
|
||||
body.indent()
|
||||
|
||||
for r in all_values(fn.arg_regs, fn.blocks):
|
||||
if isinstance(r.type, RTuple):
|
||||
emitter.declare_tuple_struct(r.type)
|
||||
if isinstance(r.type, RArray):
|
||||
continue # Special: declared on first assignment
|
||||
|
||||
if r in fn.arg_regs:
|
||||
continue # Skip the arguments
|
||||
|
||||
ctype = emitter.ctype_spaced(r.type)
|
||||
init = ""
|
||||
declarations.emit_line(
|
||||
"{ctype}{prefix}{name}{init};".format(
|
||||
ctype=ctype, prefix=REG_PREFIX, name=names[r], init=init
|
||||
)
|
||||
)
|
||||
|
||||
# Before we emit the blocks, give them all labels
|
||||
blocks = fn.blocks
|
||||
for i, block in enumerate(blocks):
|
||||
block.label = i
|
||||
|
||||
# Find blocks that are never jumped to or are only jumped to from the
|
||||
# block directly above it. This allows for more labels and gotos to be
|
||||
# eliminated during code generation.
|
||||
for block in fn.blocks:
|
||||
terminator = block.terminator
|
||||
assert isinstance(terminator, ControlOp)
|
||||
|
||||
for target in terminator.targets():
|
||||
is_next_block = target.label == block.label + 1
|
||||
|
||||
# Always emit labels for GetAttr error checks since the emit code that
|
||||
# generates them will add instructions between the branch and the
|
||||
# next label, causing the label to be wrongly removed. A better
|
||||
# solution would be to change the IR so that it adds a basic block
|
||||
# inbetween the calls.
|
||||
is_problematic_op = isinstance(terminator, Branch) and any(
|
||||
isinstance(s, GetAttr) for s in terminator.sources()
|
||||
)
|
||||
|
||||
if not is_next_block or is_problematic_op:
|
||||
fn.blocks[target.label].referenced = True
|
||||
|
||||
common = frequently_executed_blocks(fn.blocks[0])
|
||||
|
||||
for i in range(len(blocks)):
|
||||
block = blocks[i]
|
||||
visitor.rare = block not in common
|
||||
next_block = None
|
||||
if i + 1 < len(blocks):
|
||||
next_block = blocks[i + 1]
|
||||
body.emit_label(block)
|
||||
visitor.next_block = next_block
|
||||
|
||||
ops = block.ops
|
||||
visitor.ops = ops
|
||||
visitor.op_index = 0
|
||||
while visitor.op_index < len(ops):
|
||||
ops[visitor.op_index].accept(visitor)
|
||||
visitor.op_index += 1
|
||||
|
||||
body.emit_line("}")
|
||||
|
||||
emitter.emit_from_emitter(declarations)
|
||||
emitter.emit_from_emitter(body)
|
||||
|
||||
|
||||
class FunctionEmitterVisitor(OpVisitor[None]):
|
||||
def __init__(
|
||||
self, emitter: Emitter, declarations: Emitter, source_path: str, module_name: str
|
||||
) -> None:
|
||||
self.emitter = emitter
|
||||
self.names = emitter.names
|
||||
self.declarations = declarations
|
||||
self.source_path = source_path
|
||||
self.module_name = module_name
|
||||
self.literals = emitter.context.literals
|
||||
self.rare = False
|
||||
# Next basic block to be processed after the current one (if any), set by caller
|
||||
self.next_block: BasicBlock | None = None
|
||||
# Ops in the basic block currently being processed, set by caller
|
||||
self.ops: list[Op] = []
|
||||
# Current index within ops; visit methods can increment this to skip/merge ops
|
||||
self.op_index = 0
|
||||
|
||||
def temp_name(self) -> str:
|
||||
return self.emitter.temp_name()
|
||||
|
||||
def visit_goto(self, op: Goto) -> None:
|
||||
if op.label is not self.next_block:
|
||||
self.emit_line("goto %s;" % self.label(op.label))
|
||||
|
||||
def visit_branch(self, op: Branch) -> None:
|
||||
true, false = op.true, op.false
|
||||
negated = op.negated
|
||||
negated_rare = False
|
||||
if true is self.next_block and op.traceback_entry is None:
|
||||
# Switch true/false since it avoids an else block.
|
||||
true, false = false, true
|
||||
negated = not negated
|
||||
negated_rare = True
|
||||
|
||||
neg = "!" if negated else ""
|
||||
cond = ""
|
||||
if op.op == Branch.BOOL:
|
||||
expr_result = self.reg(op.value)
|
||||
cond = f"{neg}{expr_result}"
|
||||
elif op.op == Branch.IS_ERROR:
|
||||
typ = op.value.type
|
||||
compare = "!=" if negated else "=="
|
||||
if isinstance(typ, RTuple):
|
||||
# TODO: What about empty tuple?
|
||||
cond = self.emitter.tuple_undefined_check_cond(
|
||||
typ, self.reg(op.value), self.c_error_value, compare
|
||||
)
|
||||
else:
|
||||
cond = f"{self.reg(op.value)} {compare} {self.c_error_value(typ)}"
|
||||
else:
|
||||
assert False, "Invalid branch"
|
||||
|
||||
# For error checks, tell the compiler the branch is unlikely
|
||||
if op.traceback_entry is not None or op.rare:
|
||||
if not negated_rare:
|
||||
cond = f"unlikely({cond})"
|
||||
else:
|
||||
cond = f"likely({cond})"
|
||||
|
||||
if false is self.next_block:
|
||||
if op.traceback_entry is None:
|
||||
if true is not self.next_block:
|
||||
self.emit_line(f"if ({cond}) goto {self.label(true)};")
|
||||
else:
|
||||
self.emit_line(f"if ({cond}) {{")
|
||||
self.emit_traceback(op)
|
||||
self.emit_lines("goto %s;" % self.label(true), "}")
|
||||
else:
|
||||
self.emit_line(f"if ({cond}) {{")
|
||||
self.emit_traceback(op)
|
||||
|
||||
if true is not self.next_block:
|
||||
self.emit_line("goto %s;" % self.label(true))
|
||||
|
||||
self.emit_lines("} else", " goto %s;" % self.label(false))
|
||||
|
||||
def visit_return(self, op: Return) -> None:
|
||||
value_str = self.reg(op.value)
|
||||
self.emit_line("return %s;" % value_str)
|
||||
|
||||
def visit_tuple_set(self, op: TupleSet) -> None:
|
||||
dest = self.reg(op)
|
||||
tuple_type = op.tuple_type
|
||||
self.emitter.declare_tuple_struct(tuple_type)
|
||||
if len(op.items) == 0: # empty tuple
|
||||
self.emit_line(f"{dest}.empty_struct_error_flag = 0;")
|
||||
else:
|
||||
for i, item in enumerate(op.items):
|
||||
self.emit_line(f"{dest}.f{i} = {self.reg(item)};")
|
||||
self.emit_inc_ref(dest, tuple_type)
|
||||
|
||||
def visit_assign(self, op: Assign) -> None:
|
||||
dest = self.reg(op.dest)
|
||||
src = self.reg(op.src)
|
||||
# clang whines about self assignment (which we might generate
|
||||
# for some casts), so don't emit it.
|
||||
if dest != src:
|
||||
# We sometimes assign from an integer prepresentation of a pointer
|
||||
# to a real pointer, and C compilers insist on a cast.
|
||||
if op.src.type.is_unboxed and not op.dest.type.is_unboxed:
|
||||
src = f"(void *){src}"
|
||||
self.emit_line(f"{dest} = {src};")
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> None:
|
||||
typ = op.dest.type
|
||||
assert isinstance(typ, RArray)
|
||||
dest = self.reg(op.dest)
|
||||
# RArray values can only be assigned to once, so we can always
|
||||
# declare them on initialization.
|
||||
self.emit_line(
|
||||
"%s%s[%d] = %s;"
|
||||
% (
|
||||
self.emitter.ctype_spaced(typ.item_type),
|
||||
dest,
|
||||
len(op.src),
|
||||
c_array_initializer([self.reg(s) for s in op.src], indented=True),
|
||||
)
|
||||
)
|
||||
|
||||
def visit_load_error_value(self, op: LoadErrorValue) -> None:
|
||||
if isinstance(op.type, RTuple):
|
||||
values = [self.c_undefined_value(item) for item in op.type.types]
|
||||
tmp = self.temp_name()
|
||||
self.emit_line("{} {} = {{ {} }};".format(self.ctype(op.type), tmp, ", ".join(values)))
|
||||
self.emit_line(f"{self.reg(op)} = {tmp};")
|
||||
else:
|
||||
self.emit_line(f"{self.reg(op)} = {self.c_error_value(op.type)};")
|
||||
|
||||
def visit_load_literal(self, op: LoadLiteral) -> None:
|
||||
index = self.literals.literal_index(op.value)
|
||||
if not is_int_rprimitive(op.type):
|
||||
self.emit_line("%s = CPyStatics[%d];" % (self.reg(op), index), ann=op.value)
|
||||
else:
|
||||
self.emit_line(
|
||||
"%s = (CPyTagged)CPyStatics[%d] | 1;" % (self.reg(op), index), ann=op.value
|
||||
)
|
||||
|
||||
def get_attr_expr(self, obj: str, op: GetAttr | SetAttr, decl_cl: ClassIR) -> str:
|
||||
"""Generate attribute accessor for normal (non-property) access.
|
||||
|
||||
This either has a form like obj->attr_name for attributes defined in non-trait
|
||||
classes, and *(obj + attr_offset) for attributes defined by traits. We also
|
||||
insert all necessary C casts here.
|
||||
"""
|
||||
cast = f"({op.class_type.struct_name(self.emitter.names)} *)"
|
||||
if decl_cl.is_trait and op.class_type.class_ir.is_trait:
|
||||
# For pure trait access find the offset first, offsets
|
||||
# are ordered by attribute position in the cl.attributes dict.
|
||||
# TODO: pre-calculate the mapping to make this faster.
|
||||
trait_attr_index = list(decl_cl.attributes).index(op.attr)
|
||||
# TODO: reuse these names somehow?
|
||||
offset = self.emitter.temp_name()
|
||||
self.declarations.emit_line(f"size_t {offset};")
|
||||
self.emitter.emit_line(
|
||||
"{} = {};".format(
|
||||
offset,
|
||||
"CPy_FindAttrOffset({}, {}, {})".format(
|
||||
self.emitter.type_struct_name(decl_cl),
|
||||
f"({cast}{obj})->vtable",
|
||||
trait_attr_index,
|
||||
),
|
||||
)
|
||||
)
|
||||
attr_cast = f"({self.ctype(op.class_type.attr_type(op.attr))} *)"
|
||||
return f"*{attr_cast}((char *){obj} + {offset})"
|
||||
else:
|
||||
# Cast to something non-trait. Note: for this to work, all struct
|
||||
# members for non-trait classes must obey monotonic linear growth.
|
||||
if op.class_type.class_ir.is_trait:
|
||||
assert not decl_cl.is_trait
|
||||
cast = f"({decl_cl.struct_name(self.emitter.names)} *)"
|
||||
return f"({cast}{obj})->{self.emitter.attr(op.attr)}"
|
||||
|
||||
def visit_get_attr(self, op: GetAttr) -> None:
|
||||
dest = self.reg(op)
|
||||
obj = self.reg(op.obj)
|
||||
rtype = op.class_type
|
||||
cl = rtype.class_ir
|
||||
attr_rtype, decl_cl = cl.attr_details(op.attr)
|
||||
prefer_method = cl.is_trait and attr_rtype.error_overlap
|
||||
if cl.get_method(op.attr, prefer_method=prefer_method):
|
||||
# Properties are essentially methods, so use vtable access for them.
|
||||
version = "_TRAIT" if cl.is_trait else ""
|
||||
self.emit_line(
|
||||
"%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */"
|
||||
% (
|
||||
dest,
|
||||
version,
|
||||
obj,
|
||||
self.emitter.type_struct_name(rtype.class_ir),
|
||||
rtype.getter_index(op.attr),
|
||||
rtype.struct_name(self.names),
|
||||
self.ctype(rtype.attr_type(op.attr)),
|
||||
op.attr,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Otherwise, use direct or offset struct access.
|
||||
attr_expr = self.get_attr_expr(obj, op, decl_cl)
|
||||
self.emitter.emit_line(f"{dest} = {attr_expr};")
|
||||
always_defined = cl.is_always_defined(op.attr)
|
||||
merged_branch = None
|
||||
if not always_defined:
|
||||
self.emitter.emit_undefined_attr_check(
|
||||
attr_rtype, dest, "==", obj, op.attr, cl, unlikely=True
|
||||
)
|
||||
branch = self.next_branch()
|
||||
if branch is not None:
|
||||
if (
|
||||
branch.value is op
|
||||
and branch.op == Branch.IS_ERROR
|
||||
and branch.traceback_entry is not None
|
||||
and not branch.negated
|
||||
):
|
||||
# Generate code for the following branch here to avoid
|
||||
# redundant branches in the generated code.
|
||||
self.emit_attribute_error(branch, cl.name, op.attr)
|
||||
self.emit_line("goto %s;" % self.label(branch.true))
|
||||
merged_branch = branch
|
||||
self.emitter.emit_line("}")
|
||||
if not merged_branch:
|
||||
exc_class = "PyExc_AttributeError"
|
||||
self.emitter.emit_line(
|
||||
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
|
||||
exc_class, repr(op.attr), repr(cl.name)
|
||||
)
|
||||
)
|
||||
|
||||
if attr_rtype.is_refcounted and not op.is_borrowed:
|
||||
if not merged_branch and not always_defined:
|
||||
self.emitter.emit_line("} else {")
|
||||
self.emitter.emit_inc_ref(dest, attr_rtype)
|
||||
if merged_branch:
|
||||
if merged_branch.false is not self.next_block:
|
||||
self.emit_line("goto %s;" % self.label(merged_branch.false))
|
||||
self.op_index += 1
|
||||
elif not always_defined:
|
||||
self.emitter.emit_line("}")
|
||||
|
||||
def next_branch(self) -> Branch | None:
|
||||
if self.op_index + 1 < len(self.ops):
|
||||
next_op = self.ops[self.op_index + 1]
|
||||
if isinstance(next_op, Branch):
|
||||
return next_op
|
||||
return None
|
||||
|
||||
def visit_set_attr(self, op: SetAttr) -> None:
|
||||
if op.error_kind == ERR_FALSE:
|
||||
dest = self.reg(op)
|
||||
obj = self.reg(op.obj)
|
||||
src = self.reg(op.src)
|
||||
rtype = op.class_type
|
||||
cl = rtype.class_ir
|
||||
attr_rtype, decl_cl = cl.attr_details(op.attr)
|
||||
if cl.get_method(op.attr):
|
||||
# Again, use vtable access for properties...
|
||||
assert not op.is_init and op.error_kind == ERR_FALSE, "%s %d %d %s" % (
|
||||
op.attr,
|
||||
op.is_init,
|
||||
op.error_kind,
|
||||
rtype,
|
||||
)
|
||||
version = "_TRAIT" if cl.is_trait else ""
|
||||
self.emit_line(
|
||||
"%s = CPY_SET_ATTR%s(%s, %s, %d, %s, %s, %s); /* %s */"
|
||||
% (
|
||||
dest,
|
||||
version,
|
||||
obj,
|
||||
self.emitter.type_struct_name(rtype.class_ir),
|
||||
rtype.setter_index(op.attr),
|
||||
src,
|
||||
rtype.struct_name(self.names),
|
||||
self.ctype(rtype.attr_type(op.attr)),
|
||||
op.attr,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# ...and struct access for normal attributes.
|
||||
attr_expr = self.get_attr_expr(obj, op, decl_cl)
|
||||
if not op.is_init and attr_rtype.is_refcounted:
|
||||
# This is not an initialization (where we know that the attribute was
|
||||
# previously undefined), so decref the old value.
|
||||
always_defined = cl.is_always_defined(op.attr)
|
||||
if not always_defined:
|
||||
self.emitter.emit_undefined_attr_check(
|
||||
attr_rtype, attr_expr, "!=", obj, op.attr, cl
|
||||
)
|
||||
self.emitter.emit_dec_ref(attr_expr, attr_rtype)
|
||||
if not always_defined:
|
||||
self.emitter.emit_line("}")
|
||||
elif attr_rtype.error_overlap and not cl.is_always_defined(op.attr):
|
||||
# If there is overlap with the error value, update bitmap to mark
|
||||
# attribute as defined.
|
||||
self.emitter.emit_attr_bitmap_set(src, obj, attr_rtype, cl, op.attr)
|
||||
|
||||
# This steals the reference to src, so we don't need to increment the arg
|
||||
self.emitter.emit_line(f"{attr_expr} = {src};")
|
||||
if op.error_kind == ERR_FALSE:
|
||||
self.emitter.emit_line(f"{dest} = 1;")
|
||||
|
||||
PREFIX_MAP: Final = {
|
||||
NAMESPACE_STATIC: STATIC_PREFIX,
|
||||
NAMESPACE_TYPE: TYPE_PREFIX,
|
||||
NAMESPACE_MODULE: MODULE_PREFIX,
|
||||
}
|
||||
|
||||
def visit_load_static(self, op: LoadStatic) -> None:
|
||||
dest = self.reg(op)
|
||||
prefix = self.PREFIX_MAP[op.namespace]
|
||||
name = self.emitter.static_name(op.identifier, op.module_name, prefix)
|
||||
if op.namespace == NAMESPACE_TYPE:
|
||||
name = "(PyObject *)%s" % name
|
||||
self.emit_line(f"{dest} = {name};", ann=op.ann)
|
||||
|
||||
def visit_init_static(self, op: InitStatic) -> None:
|
||||
value = self.reg(op.value)
|
||||
prefix = self.PREFIX_MAP[op.namespace]
|
||||
name = self.emitter.static_name(op.identifier, op.module_name, prefix)
|
||||
if op.namespace == NAMESPACE_TYPE:
|
||||
value = "(PyTypeObject *)%s" % value
|
||||
self.emit_line(f"{name} = {value};")
|
||||
self.emit_inc_ref(name, op.value.type)
|
||||
|
||||
def visit_tuple_get(self, op: TupleGet) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
self.emit_line(f"{dest} = {src}.f{op.index};")
|
||||
self.emit_inc_ref(dest, op.type)
|
||||
|
||||
def get_dest_assign(self, dest: Value) -> str:
|
||||
if not dest.is_void:
|
||||
return self.reg(dest) + " = "
|
||||
else:
|
||||
return ""
|
||||
|
||||
def visit_call(self, op: Call) -> None:
|
||||
"""Call native function."""
|
||||
dest = self.get_dest_assign(op)
|
||||
args = ", ".join(self.reg(arg) for arg in op.args)
|
||||
lib = self.emitter.get_group_prefix(op.fn)
|
||||
cname = op.fn.cname(self.names)
|
||||
self.emit_line(f"{dest}{lib}{NATIVE_PREFIX}{cname}({args});")
|
||||
|
||||
def visit_method_call(self, op: MethodCall) -> None:
|
||||
"""Call native method."""
|
||||
dest = self.get_dest_assign(op)
|
||||
obj = self.reg(op.obj)
|
||||
|
||||
rtype = op.receiver_type
|
||||
class_ir = rtype.class_ir
|
||||
name = op.method
|
||||
method = rtype.class_ir.get_method(name)
|
||||
assert method is not None
|
||||
|
||||
# Can we call the method directly, bypassing vtable?
|
||||
is_direct = class_ir.is_method_final(name)
|
||||
|
||||
# The first argument gets omitted for static methods and
|
||||
# turned into the class for class methods
|
||||
obj_args = (
|
||||
[]
|
||||
if method.decl.kind == FUNC_STATICMETHOD
|
||||
else [f"(PyObject *)Py_TYPE({obj})"]
|
||||
if method.decl.kind == FUNC_CLASSMETHOD
|
||||
else [obj]
|
||||
)
|
||||
args = ", ".join(obj_args + [self.reg(arg) for arg in op.args])
|
||||
mtype = native_function_type(method, self.emitter)
|
||||
version = "_TRAIT" if rtype.class_ir.is_trait else ""
|
||||
if is_direct:
|
||||
# Directly call method, without going through the vtable.
|
||||
lib = self.emitter.get_group_prefix(method.decl)
|
||||
self.emit_line(f"{dest}{lib}{NATIVE_PREFIX}{method.cname(self.names)}({args});")
|
||||
else:
|
||||
# Call using vtable.
|
||||
method_idx = rtype.method_index(name)
|
||||
self.emit_line(
|
||||
"{}CPY_GET_METHOD{}({}, {}, {}, {}, {})({}); /* {} */".format(
|
||||
dest,
|
||||
version,
|
||||
obj,
|
||||
self.emitter.type_struct_name(rtype.class_ir),
|
||||
method_idx,
|
||||
rtype.struct_name(self.names),
|
||||
mtype,
|
||||
args,
|
||||
op.method,
|
||||
)
|
||||
)
|
||||
|
||||
def visit_inc_ref(self, op: IncRef) -> None:
|
||||
src = self.reg(op.src)
|
||||
self.emit_inc_ref(src, op.src.type)
|
||||
|
||||
def visit_dec_ref(self, op: DecRef) -> None:
|
||||
src = self.reg(op.src)
|
||||
self.emit_dec_ref(src, op.src.type, is_xdec=op.is_xdec)
|
||||
|
||||
def visit_box(self, op: Box) -> None:
|
||||
self.emitter.emit_box(self.reg(op.src), self.reg(op), op.src.type, can_borrow=True)
|
||||
|
||||
def visit_cast(self, op: Cast) -> None:
|
||||
branch = self.next_branch()
|
||||
handler = None
|
||||
if branch is not None:
|
||||
if (
|
||||
branch.value is op
|
||||
and branch.op == Branch.IS_ERROR
|
||||
and branch.traceback_entry is not None
|
||||
and not branch.negated
|
||||
and branch.false is self.next_block
|
||||
):
|
||||
# Generate code also for the following branch here to avoid
|
||||
# redundant branches in the generated code.
|
||||
handler = TracebackAndGotoHandler(
|
||||
self.label(branch.true),
|
||||
self.source_path,
|
||||
self.module_name,
|
||||
branch.traceback_entry,
|
||||
)
|
||||
self.op_index += 1
|
||||
|
||||
self.emitter.emit_cast(
|
||||
self.reg(op.src), self.reg(op), op.type, src_type=op.src.type, error=handler
|
||||
)
|
||||
|
||||
def visit_unbox(self, op: Unbox) -> None:
|
||||
self.emitter.emit_unbox(self.reg(op.src), self.reg(op), op.type)
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> None:
|
||||
self.emitter.emit_line("CPy_Unreachable();")
|
||||
|
||||
def visit_raise_standard_error(self, op: RaiseStandardError) -> None:
|
||||
# TODO: Better escaping of backspaces and such
|
||||
if op.value is not None:
|
||||
if isinstance(op.value, str):
|
||||
message = op.value.replace('"', '\\"')
|
||||
self.emitter.emit_line(f'PyErr_SetString(PyExc_{op.class_name}, "{message}");')
|
||||
elif isinstance(op.value, Value):
|
||||
self.emitter.emit_line(
|
||||
"PyErr_SetObject(PyExc_{}, {});".format(
|
||||
op.class_name, self.emitter.reg(op.value)
|
||||
)
|
||||
)
|
||||
else:
|
||||
assert False, "op value type must be either str or Value"
|
||||
else:
|
||||
self.emitter.emit_line(f"PyErr_SetNone(PyExc_{op.class_name});")
|
||||
self.emitter.emit_line(f"{self.reg(op)} = 0;")
|
||||
|
||||
def visit_call_c(self, op: CallC) -> None:
|
||||
if op.is_void:
|
||||
dest = ""
|
||||
else:
|
||||
dest = self.get_dest_assign(op)
|
||||
args = ", ".join(self.reg(arg) for arg in op.args)
|
||||
self.emitter.emit_line(f"{dest}{op.function_name}({args});")
|
||||
|
||||
def visit_truncate(self, op: Truncate) -> None:
|
||||
dest = self.reg(op)
|
||||
value = self.reg(op.src)
|
||||
# for C backend the generated code are straight assignments
|
||||
self.emit_line(f"{dest} = {value};")
|
||||
|
||||
def visit_extend(self, op: Extend) -> None:
|
||||
dest = self.reg(op)
|
||||
value = self.reg(op.src)
|
||||
if op.signed:
|
||||
src_cast = self.emit_signed_int_cast(op.src.type)
|
||||
else:
|
||||
src_cast = self.emit_unsigned_int_cast(op.src.type)
|
||||
self.emit_line(f"{dest} = {src_cast}{value};")
|
||||
|
||||
def visit_load_global(self, op: LoadGlobal) -> None:
|
||||
dest = self.reg(op)
|
||||
self.emit_line(f"{dest} = {op.identifier};", ann=op.ann)
|
||||
|
||||
def visit_int_op(self, op: IntOp) -> None:
|
||||
dest = self.reg(op)
|
||||
lhs = self.reg(op.lhs)
|
||||
rhs = self.reg(op.rhs)
|
||||
if op.op == IntOp.RIGHT_SHIFT:
|
||||
# Signed right shift
|
||||
lhs = self.emit_signed_int_cast(op.lhs.type) + lhs
|
||||
rhs = self.emit_signed_int_cast(op.rhs.type) + rhs
|
||||
self.emit_line(f"{dest} = {lhs} {op.op_str[op.op]} {rhs};")
|
||||
|
||||
def visit_comparison_op(self, op: ComparisonOp) -> None:
|
||||
dest = self.reg(op)
|
||||
lhs = self.reg(op.lhs)
|
||||
rhs = self.reg(op.rhs)
|
||||
lhs_cast = ""
|
||||
rhs_cast = ""
|
||||
if op.op in (ComparisonOp.SLT, ComparisonOp.SGT, ComparisonOp.SLE, ComparisonOp.SGE):
|
||||
# Always signed comparison op
|
||||
lhs_cast = self.emit_signed_int_cast(op.lhs.type)
|
||||
rhs_cast = self.emit_signed_int_cast(op.rhs.type)
|
||||
elif op.op in (ComparisonOp.ULT, ComparisonOp.UGT, ComparisonOp.ULE, ComparisonOp.UGE):
|
||||
# Always unsigned comparison op
|
||||
lhs_cast = self.emit_unsigned_int_cast(op.lhs.type)
|
||||
rhs_cast = self.emit_unsigned_int_cast(op.rhs.type)
|
||||
elif isinstance(op.lhs, Integer) and op.lhs.value < 0:
|
||||
# Force signed ==/!= with negative operand
|
||||
rhs_cast = self.emit_signed_int_cast(op.rhs.type)
|
||||
elif isinstance(op.rhs, Integer) and op.rhs.value < 0:
|
||||
# Force signed ==/!= with negative operand
|
||||
lhs_cast = self.emit_signed_int_cast(op.lhs.type)
|
||||
self.emit_line(f"{dest} = {lhs_cast}{lhs} {op.op_str[op.op]} {rhs_cast}{rhs};")
|
||||
|
||||
def visit_float_op(self, op: FloatOp) -> None:
|
||||
dest = self.reg(op)
|
||||
lhs = self.reg(op.lhs)
|
||||
rhs = self.reg(op.rhs)
|
||||
if op.op != FloatOp.MOD:
|
||||
self.emit_line(f"{dest} = {lhs} {op.op_str[op.op]} {rhs};")
|
||||
else:
|
||||
# TODO: This may set errno as a side effect, that is a little sketchy.
|
||||
self.emit_line(f"{dest} = fmod({lhs}, {rhs});")
|
||||
|
||||
def visit_float_neg(self, op: FloatNeg) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
self.emit_line(f"{dest} = -{src};")
|
||||
|
||||
def visit_float_comparison_op(self, op: FloatComparisonOp) -> None:
|
||||
dest = self.reg(op)
|
||||
lhs = self.reg(op.lhs)
|
||||
rhs = self.reg(op.rhs)
|
||||
self.emit_line(f"{dest} = {lhs} {op.op_str[op.op]} {rhs};")
|
||||
|
||||
def visit_load_mem(self, op: LoadMem) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
# TODO: we shouldn't dereference to type that are pointer type so far
|
||||
type = self.ctype(op.type)
|
||||
self.emit_line(f"{dest} = *({type} *){src};")
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> None:
|
||||
dest = self.reg(op.dest)
|
||||
src = self.reg(op.src)
|
||||
dest_type = self.ctype(op.dest_type)
|
||||
# clang whines about self assignment (which we might generate
|
||||
# for some casts), so don't emit it.
|
||||
if dest != src:
|
||||
self.emit_line(f"*({dest_type} *){dest} = {src};")
|
||||
|
||||
def visit_get_element_ptr(self, op: GetElementPtr) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
# TODO: support tuple type
|
||||
assert isinstance(op.src_type, RStruct)
|
||||
assert op.field in op.src_type.names, "Invalid field name."
|
||||
self.emit_line(
|
||||
"{} = ({})&(({} *){})->{};".format(
|
||||
dest, op.type._ctype, op.src_type.name, src, op.field
|
||||
)
|
||||
)
|
||||
|
||||
def visit_load_address(self, op: LoadAddress) -> None:
|
||||
typ = op.type
|
||||
dest = self.reg(op)
|
||||
if isinstance(op.src, Register):
|
||||
src = self.reg(op.src)
|
||||
elif isinstance(op.src, LoadStatic):
|
||||
prefix = self.PREFIX_MAP[op.src.namespace]
|
||||
src = self.emitter.static_name(op.src.identifier, op.src.module_name, prefix)
|
||||
else:
|
||||
src = op.src
|
||||
self.emit_line(f"{dest} = ({typ._ctype})&{src};")
|
||||
|
||||
def visit_keep_alive(self, op: KeepAlive) -> None:
|
||||
# This is a no-op.
|
||||
pass
|
||||
|
||||
# Helpers
|
||||
|
||||
def label(self, label: BasicBlock) -> str:
|
||||
return self.emitter.label(label)
|
||||
|
||||
def reg(self, reg: Value) -> str:
|
||||
if isinstance(reg, Integer):
|
||||
val = reg.value
|
||||
if val == 0 and is_pointer_rprimitive(reg.type):
|
||||
return "NULL"
|
||||
s = str(val)
|
||||
if val >= (1 << 31):
|
||||
# Avoid overflowing signed 32-bit int
|
||||
if val >= (1 << 63):
|
||||
s += "ULL"
|
||||
else:
|
||||
s += "LL"
|
||||
elif val == -(1 << 63):
|
||||
# Avoid overflowing C integer literal
|
||||
s = "(-9223372036854775807LL - 1)"
|
||||
elif val <= -(1 << 31):
|
||||
s += "LL"
|
||||
return s
|
||||
elif isinstance(reg, Float):
|
||||
r = repr(reg.value)
|
||||
if r == "inf":
|
||||
return "INFINITY"
|
||||
elif r == "-inf":
|
||||
return "-INFINITY"
|
||||
elif r == "nan":
|
||||
return "NAN"
|
||||
return r
|
||||
else:
|
||||
return self.emitter.reg(reg)
|
||||
|
||||
def ctype(self, rtype: RType) -> str:
|
||||
return self.emitter.ctype(rtype)
|
||||
|
||||
def c_error_value(self, rtype: RType) -> str:
|
||||
return self.emitter.c_error_value(rtype)
|
||||
|
||||
def c_undefined_value(self, rtype: RType) -> str:
|
||||
return self.emitter.c_undefined_value(rtype)
|
||||
|
||||
def emit_line(self, line: str, *, ann: object = None) -> None:
|
||||
self.emitter.emit_line(line, ann=ann)
|
||||
|
||||
def emit_lines(self, *lines: str) -> None:
|
||||
self.emitter.emit_lines(*lines)
|
||||
|
||||
def emit_inc_ref(self, dest: str, rtype: RType) -> None:
|
||||
self.emitter.emit_inc_ref(dest, rtype, rare=self.rare)
|
||||
|
||||
def emit_dec_ref(self, dest: str, rtype: RType, is_xdec: bool) -> None:
|
||||
self.emitter.emit_dec_ref(dest, rtype, is_xdec=is_xdec, rare=self.rare)
|
||||
|
||||
def emit_declaration(self, line: str) -> None:
|
||||
self.declarations.emit_line(line)
|
||||
|
||||
def emit_traceback(self, op: Branch) -> None:
|
||||
if op.traceback_entry is not None:
|
||||
self.emitter.emit_traceback(self.source_path, self.module_name, op.traceback_entry)
|
||||
|
||||
def emit_attribute_error(self, op: Branch, class_name: str, attr: str) -> None:
|
||||
assert op.traceback_entry is not None
|
||||
globals_static = self.emitter.static_name("globals", self.module_name)
|
||||
self.emit_line(
|
||||
'CPy_AttributeError("%s", "%s", "%s", "%s", %d, %s);'
|
||||
% (
|
||||
self.source_path.replace("\\", "\\\\"),
|
||||
op.traceback_entry[0],
|
||||
class_name,
|
||||
attr,
|
||||
op.traceback_entry[1],
|
||||
globals_static,
|
||||
)
|
||||
)
|
||||
if DEBUG_ERRORS:
|
||||
self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");')
|
||||
|
||||
def emit_signed_int_cast(self, type: RType) -> str:
|
||||
if is_tagged(type):
|
||||
return "(Py_ssize_t)"
|
||||
else:
|
||||
return ""
|
||||
|
||||
def emit_unsigned_int_cast(self, type: RType) -> str:
|
||||
if is_int32_rprimitive(type):
|
||||
return "(uint32_t)"
|
||||
elif is_int64_rprimitive(type):
|
||||
return "(uint64_t)"
|
||||
else:
|
||||
return ""
|
||||
Binary file not shown.
1131
venv/lib/python3.12/site-packages/mypyc/codegen/emitmodule.py
Normal file
1131
venv/lib/python3.12/site-packages/mypyc/codegen/emitmodule.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
979
venv/lib/python3.12/site-packages/mypyc/codegen/emitwrapper.py
Normal file
979
venv/lib/python3.12/site-packages/mypyc/codegen/emitwrapper.py
Normal file
@@ -0,0 +1,979 @@
|
||||
"""Generate CPython API wrapper functions for native functions.
|
||||
|
||||
The wrapper functions are used by the CPython runtime when calling
|
||||
native functions from interpreted code, and when the called function
|
||||
can't be determined statically in compiled code. They validate, match,
|
||||
unbox and type check function arguments, and box return values as
|
||||
needed. All wrappers accept and return 'PyObject *' (boxed) values.
|
||||
|
||||
The wrappers aren't used for most calls between two native functions
|
||||
or methods in a single compilation unit.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
from mypy.nodes import ARG_NAMED, ARG_NAMED_OPT, ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, ArgKind
|
||||
from mypy.operators import op_methods_to_symbols, reverse_op_method_names, reverse_op_methods
|
||||
from mypyc.codegen.emit import AssignHandler, Emitter, ErrorHandler, GotoHandler, ReturnHandler
|
||||
from mypyc.common import (
|
||||
BITMAP_BITS,
|
||||
BITMAP_TYPE,
|
||||
DUNDER_PREFIX,
|
||||
NATIVE_PREFIX,
|
||||
PREFIX,
|
||||
bitmap_name,
|
||||
use_vectorcall,
|
||||
)
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.func_ir import FUNC_STATICMETHOD, FuncIR, RuntimeArg
|
||||
from mypyc.ir.rtypes import (
|
||||
RInstance,
|
||||
RType,
|
||||
is_bool_rprimitive,
|
||||
is_int_rprimitive,
|
||||
is_object_rprimitive,
|
||||
object_rprimitive,
|
||||
)
|
||||
from mypyc.namegen import NameGenerator
|
||||
|
||||
# Generic vectorcall wrapper functions (Python 3.7+)
|
||||
#
|
||||
# A wrapper function has a signature like this:
|
||||
#
|
||||
# PyObject *fn(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
#
|
||||
# The function takes a self object, pointer to an array of arguments,
|
||||
# the number of positional arguments, and a tuple of keyword argument
|
||||
# names (that are stored starting in args[nargs]).
|
||||
#
|
||||
# It returns the returned object, or NULL on an exception.
|
||||
#
|
||||
# These are more efficient than legacy wrapper functions, since
|
||||
# usually no tuple or dict objects need to be created for the
|
||||
# arguments. Vectorcalls also use pre-constructed str objects for
|
||||
# keyword argument names and other pre-computed information, instead
|
||||
# of processing the argument format string on each call.
|
||||
|
||||
|
||||
def wrapper_function_header(fn: FuncIR, names: NameGenerator) -> str:
|
||||
"""Return header of a vectorcall wrapper function.
|
||||
|
||||
See comment above for a summary of the arguments.
|
||||
"""
|
||||
return (
|
||||
"PyObject *{prefix}{name}("
|
||||
"PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames)"
|
||||
).format(prefix=PREFIX, name=fn.cname(names))
|
||||
|
||||
|
||||
def generate_traceback_code(
|
||||
fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
|
||||
) -> str:
|
||||
# If we hit an error while processing arguments, then we emit a
|
||||
# traceback frame to make it possible to debug where it happened.
|
||||
# Unlike traceback frames added for exceptions seen in IR, we do this
|
||||
# even if there is no `traceback_name`. This is because the error will
|
||||
# have originated here and so we need it in the traceback.
|
||||
globals_static = emitter.static_name("globals", module_name)
|
||||
traceback_code = 'CPy_AddTraceback("%s", "%s", %d, %s);' % (
|
||||
source_path.replace("\\", "\\\\"),
|
||||
fn.traceback_name or fn.name,
|
||||
fn.line,
|
||||
globals_static,
|
||||
)
|
||||
return traceback_code
|
||||
|
||||
|
||||
def make_arg_groups(args: list[RuntimeArg]) -> dict[ArgKind, list[RuntimeArg]]:
|
||||
"""Group arguments by kind."""
|
||||
return {k: [arg for arg in args if arg.kind == k] for k in ArgKind}
|
||||
|
||||
|
||||
def reorder_arg_groups(groups: dict[ArgKind, list[RuntimeArg]]) -> list[RuntimeArg]:
|
||||
"""Reorder argument groups to match their order in a format string."""
|
||||
return groups[ARG_POS] + groups[ARG_OPT] + groups[ARG_NAMED_OPT] + groups[ARG_NAMED]
|
||||
|
||||
|
||||
def make_static_kwlist(args: list[RuntimeArg]) -> str:
|
||||
arg_names = "".join(f'"{arg.name}", ' for arg in args)
|
||||
return f"static const char * const kwlist[] = {{{arg_names}0}};"
|
||||
|
||||
|
||||
def make_format_string(func_name: str | None, groups: dict[ArgKind, list[RuntimeArg]]) -> str:
|
||||
"""Return a format string that specifies the accepted arguments.
|
||||
|
||||
The format string is an extended subset of what is supported by
|
||||
PyArg_ParseTupleAndKeywords(). Only the type 'O' is used, and we
|
||||
also support some extensions:
|
||||
|
||||
- Required keyword-only arguments are introduced after '@'
|
||||
- If the function receives *args or **kwargs, we add a '%' prefix
|
||||
|
||||
Each group requires the previous groups' delimiters to be present
|
||||
first.
|
||||
|
||||
These are used by both vectorcall and legacy wrapper functions.
|
||||
"""
|
||||
format = ""
|
||||
if groups[ARG_STAR] or groups[ARG_STAR2]:
|
||||
format += "%"
|
||||
format += "O" * len(groups[ARG_POS])
|
||||
if groups[ARG_OPT] or groups[ARG_NAMED_OPT] or groups[ARG_NAMED]:
|
||||
format += "|" + "O" * len(groups[ARG_OPT])
|
||||
if groups[ARG_NAMED_OPT] or groups[ARG_NAMED]:
|
||||
format += "$" + "O" * len(groups[ARG_NAMED_OPT])
|
||||
if groups[ARG_NAMED]:
|
||||
format += "@" + "O" * len(groups[ARG_NAMED])
|
||||
if func_name is not None:
|
||||
format += f":{func_name}"
|
||||
return format
|
||||
|
||||
|
||||
def generate_wrapper_function(
|
||||
fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
|
||||
) -> None:
|
||||
"""Generate a CPython-compatible vectorcall wrapper for a native function.
|
||||
|
||||
In particular, this handles unboxing the arguments, calling the native function, and
|
||||
then boxing the return value.
|
||||
"""
|
||||
emitter.emit_line(f"{wrapper_function_header(fn, emitter.names)} {{")
|
||||
|
||||
# If fn is a method, then the first argument is a self param
|
||||
real_args = list(fn.args)
|
||||
if fn.sig.num_bitmap_args:
|
||||
real_args = real_args[: -fn.sig.num_bitmap_args]
|
||||
if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD:
|
||||
arg = real_args.pop(0)
|
||||
emitter.emit_line(f"PyObject *obj_{arg.name} = self;")
|
||||
|
||||
# Need to order args as: required, optional, kwonly optional, kwonly required
|
||||
# This is because CPyArg_ParseStackAndKeywords format string requires
|
||||
# them grouped in that way.
|
||||
groups = make_arg_groups(real_args)
|
||||
reordered_args = reorder_arg_groups(groups)
|
||||
|
||||
emitter.emit_line(make_static_kwlist(reordered_args))
|
||||
fmt = make_format_string(fn.name, groups)
|
||||
# Define the arguments the function accepts (but no types yet)
|
||||
emitter.emit_line(f'static CPyArg_Parser parser = {{"{fmt}", kwlist, 0}};')
|
||||
|
||||
for arg in real_args:
|
||||
emitter.emit_line(
|
||||
"PyObject *obj_{}{};".format(arg.name, " = NULL" if arg.optional else "")
|
||||
)
|
||||
|
||||
cleanups = [f"CPy_DECREF(obj_{arg.name});" for arg in groups[ARG_STAR] + groups[ARG_STAR2]]
|
||||
|
||||
arg_ptrs: list[str] = []
|
||||
if groups[ARG_STAR] or groups[ARG_STAR2]:
|
||||
arg_ptrs += [f"&obj_{groups[ARG_STAR][0].name}" if groups[ARG_STAR] else "NULL"]
|
||||
arg_ptrs += [f"&obj_{groups[ARG_STAR2][0].name}" if groups[ARG_STAR2] else "NULL"]
|
||||
arg_ptrs += [f"&obj_{arg.name}" for arg in reordered_args]
|
||||
|
||||
if fn.name == "__call__" and use_vectorcall(emitter.capi_version):
|
||||
nargs = "PyVectorcall_NARGS(nargs)"
|
||||
else:
|
||||
nargs = "nargs"
|
||||
parse_fn = "CPyArg_ParseStackAndKeywords"
|
||||
# Special case some common signatures
|
||||
if not real_args:
|
||||
# No args
|
||||
parse_fn = "CPyArg_ParseStackAndKeywordsNoArgs"
|
||||
elif len(real_args) == 1 and len(groups[ARG_POS]) == 1:
|
||||
# Single positional arg
|
||||
parse_fn = "CPyArg_ParseStackAndKeywordsOneArg"
|
||||
elif len(real_args) == len(groups[ARG_POS]) + len(groups[ARG_OPT]):
|
||||
# No keyword-only args, *args or **kwargs
|
||||
parse_fn = "CPyArg_ParseStackAndKeywordsSimple"
|
||||
emitter.emit_lines(
|
||||
"if (!{}(args, {}, kwnames, &parser{})) {{".format(
|
||||
parse_fn, nargs, "".join(", " + n for n in arg_ptrs)
|
||||
),
|
||||
"return NULL;",
|
||||
"}",
|
||||
)
|
||||
for i in range(fn.sig.num_bitmap_args):
|
||||
name = bitmap_name(i)
|
||||
emitter.emit_line(f"{BITMAP_TYPE} {name} = 0;")
|
||||
traceback_code = generate_traceback_code(fn, emitter, source_path, module_name)
|
||||
generate_wrapper_core(
|
||||
fn,
|
||||
emitter,
|
||||
groups[ARG_OPT] + groups[ARG_NAMED_OPT],
|
||||
cleanups=cleanups,
|
||||
traceback_code=traceback_code,
|
||||
)
|
||||
|
||||
emitter.emit_line("}")
|
||||
|
||||
|
||||
# Legacy generic wrapper functions
|
||||
#
|
||||
# These take a self object, a Python tuple of positional arguments,
|
||||
# and a dict of keyword arguments. These are a lot slower than
|
||||
# vectorcall wrappers, especially in calls involving keyword
|
||||
# arguments.
|
||||
|
||||
|
||||
def legacy_wrapper_function_header(fn: FuncIR, names: NameGenerator) -> str:
|
||||
return "PyObject *{prefix}{name}(PyObject *self, PyObject *args, PyObject *kw)".format(
|
||||
prefix=PREFIX, name=fn.cname(names)
|
||||
)
|
||||
|
||||
|
||||
def generate_legacy_wrapper_function(
|
||||
fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
|
||||
) -> None:
|
||||
"""Generates a CPython-compatible legacy wrapper for a native function.
|
||||
|
||||
In particular, this handles unboxing the arguments, calling the native function, and
|
||||
then boxing the return value.
|
||||
"""
|
||||
emitter.emit_line(f"{legacy_wrapper_function_header(fn, emitter.names)} {{")
|
||||
|
||||
# If fn is a method, then the first argument is a self param
|
||||
real_args = list(fn.args)
|
||||
if fn.sig.num_bitmap_args:
|
||||
real_args = real_args[: -fn.sig.num_bitmap_args]
|
||||
if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD:
|
||||
arg = real_args.pop(0)
|
||||
emitter.emit_line(f"PyObject *obj_{arg.name} = self;")
|
||||
|
||||
# Need to order args as: required, optional, kwonly optional, kwonly required
|
||||
# This is because CPyArg_ParseTupleAndKeywords format string requires
|
||||
# them grouped in that way.
|
||||
groups = make_arg_groups(real_args)
|
||||
reordered_args = reorder_arg_groups(groups)
|
||||
|
||||
emitter.emit_line(make_static_kwlist(reordered_args))
|
||||
for arg in real_args:
|
||||
emitter.emit_line(
|
||||
"PyObject *obj_{}{};".format(arg.name, " = NULL" if arg.optional else "")
|
||||
)
|
||||
|
||||
cleanups = [f"CPy_DECREF(obj_{arg.name});" for arg in groups[ARG_STAR] + groups[ARG_STAR2]]
|
||||
|
||||
arg_ptrs: list[str] = []
|
||||
if groups[ARG_STAR] or groups[ARG_STAR2]:
|
||||
arg_ptrs += [f"&obj_{groups[ARG_STAR][0].name}" if groups[ARG_STAR] else "NULL"]
|
||||
arg_ptrs += [f"&obj_{groups[ARG_STAR2][0].name}" if groups[ARG_STAR2] else "NULL"]
|
||||
arg_ptrs += [f"&obj_{arg.name}" for arg in reordered_args]
|
||||
|
||||
emitter.emit_lines(
|
||||
'if (!CPyArg_ParseTupleAndKeywords(args, kw, "{}", "{}", kwlist{})) {{'.format(
|
||||
make_format_string(None, groups), fn.name, "".join(", " + n for n in arg_ptrs)
|
||||
),
|
||||
"return NULL;",
|
||||
"}",
|
||||
)
|
||||
for i in range(fn.sig.num_bitmap_args):
|
||||
name = bitmap_name(i)
|
||||
emitter.emit_line(f"{BITMAP_TYPE} {name} = 0;")
|
||||
traceback_code = generate_traceback_code(fn, emitter, source_path, module_name)
|
||||
generate_wrapper_core(
|
||||
fn,
|
||||
emitter,
|
||||
groups[ARG_OPT] + groups[ARG_NAMED_OPT],
|
||||
cleanups=cleanups,
|
||||
traceback_code=traceback_code,
|
||||
)
|
||||
|
||||
emitter.emit_line("}")
|
||||
|
||||
|
||||
# Specialized wrapper functions
|
||||
|
||||
|
||||
def generate_dunder_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __dunder__ methods to be able to fit into the mapping
|
||||
protocol slot. This specifically means that the arguments are taken as *PyObjects and returned
|
||||
as *PyObjects.
|
||||
"""
|
||||
gen = WrapperGenerator(cl, emitter)
|
||||
gen.set_target(fn)
|
||||
gen.emit_header()
|
||||
gen.emit_arg_processing()
|
||||
gen.emit_call()
|
||||
gen.finish()
|
||||
return gen.wrapper_name()
|
||||
|
||||
|
||||
def generate_ipow_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generate a wrapper for native __ipow__.
|
||||
|
||||
Since __ipow__ fills a ternary slot, but almost no one defines __ipow__ to take three
|
||||
arguments, the wrapper needs to tweaked to force it to accept three arguments.
|
||||
"""
|
||||
gen = WrapperGenerator(cl, emitter)
|
||||
gen.set_target(fn)
|
||||
assert len(fn.args) in (2, 3), "__ipow__ should only take 2 or 3 arguments"
|
||||
gen.arg_names = ["self", "exp", "mod"]
|
||||
gen.emit_header()
|
||||
gen.emit_arg_processing()
|
||||
handle_third_pow_argument(
|
||||
fn,
|
||||
emitter,
|
||||
gen,
|
||||
if_unsupported=[
|
||||
'PyErr_SetString(PyExc_TypeError, "__ipow__ takes 2 positional arguments but 3 were given");',
|
||||
"return NULL;",
|
||||
],
|
||||
)
|
||||
gen.emit_call()
|
||||
gen.finish()
|
||||
return gen.wrapper_name()
|
||||
|
||||
|
||||
def generate_bin_op_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for a native binary dunder method.
|
||||
|
||||
The same wrapper that handles the forward method (e.g. __add__) also handles
|
||||
the corresponding reverse method (e.g. __radd__), if defined.
|
||||
|
||||
Both arguments and the return value are PyObject *.
|
||||
"""
|
||||
gen = WrapperGenerator(cl, emitter)
|
||||
gen.set_target(fn)
|
||||
if fn.name in ("__pow__", "__rpow__"):
|
||||
gen.arg_names = ["left", "right", "mod"]
|
||||
else:
|
||||
gen.arg_names = ["left", "right"]
|
||||
wrapper_name = gen.wrapper_name()
|
||||
|
||||
gen.emit_header()
|
||||
if fn.name not in reverse_op_methods and fn.name in reverse_op_method_names:
|
||||
# There's only a reverse operator method.
|
||||
generate_bin_op_reverse_only_wrapper(fn, emitter, gen)
|
||||
else:
|
||||
rmethod = reverse_op_methods[fn.name]
|
||||
fn_rev = cl.get_method(rmethod)
|
||||
if fn_rev is None:
|
||||
# There's only a forward operator method.
|
||||
generate_bin_op_forward_only_wrapper(fn, emitter, gen)
|
||||
else:
|
||||
# There's both a forward and a reverse operator method.
|
||||
generate_bin_op_both_wrappers(cl, fn, fn_rev, emitter, gen)
|
||||
return wrapper_name
|
||||
|
||||
|
||||
def generate_bin_op_forward_only_wrapper(
|
||||
fn: FuncIR, emitter: Emitter, gen: WrapperGenerator
|
||||
) -> None:
|
||||
gen.emit_arg_processing(error=GotoHandler("typefail"), raise_exception=False)
|
||||
handle_third_pow_argument(fn, emitter, gen, if_unsupported=["goto typefail;"])
|
||||
gen.emit_call(not_implemented_handler="goto typefail;")
|
||||
gen.emit_error_handling()
|
||||
emitter.emit_label("typefail")
|
||||
# If some argument has an incompatible type, treat this the same as
|
||||
# returning NotImplemented, and try to call the reverse operator method.
|
||||
#
|
||||
# Note that in normal Python you'd instead of an explicit
|
||||
# return of NotImplemented, but it doesn't generally work here
|
||||
# the body won't be executed at all if there is an argument
|
||||
# type check failure.
|
||||
#
|
||||
# The recommended way is to still use a type check in the
|
||||
# body. This will only be used in interpreted mode:
|
||||
#
|
||||
# def __add__(self, other: int) -> Foo:
|
||||
# if not isinstance(other, int):
|
||||
# return NotImplemented
|
||||
# ...
|
||||
generate_bin_op_reverse_dunder_call(fn, emitter, reverse_op_methods[fn.name])
|
||||
gen.finish()
|
||||
|
||||
|
||||
def generate_bin_op_reverse_only_wrapper(
|
||||
fn: FuncIR, emitter: Emitter, gen: WrapperGenerator
|
||||
) -> None:
|
||||
gen.arg_names = ["right", "left"]
|
||||
gen.emit_arg_processing(error=GotoHandler("typefail"), raise_exception=False)
|
||||
handle_third_pow_argument(fn, emitter, gen, if_unsupported=["goto typefail;"])
|
||||
gen.emit_call()
|
||||
gen.emit_error_handling()
|
||||
emitter.emit_label("typefail")
|
||||
emitter.emit_line("Py_INCREF(Py_NotImplemented);")
|
||||
emitter.emit_line("return Py_NotImplemented;")
|
||||
gen.finish()
|
||||
|
||||
|
||||
def generate_bin_op_both_wrappers(
|
||||
cl: ClassIR, fn: FuncIR, fn_rev: FuncIR, emitter: Emitter, gen: WrapperGenerator
|
||||
) -> None:
|
||||
# There's both a forward and a reverse operator method. First
|
||||
# check if we should try calling the forward one. If the
|
||||
# argument type check fails, fall back to the reverse method.
|
||||
#
|
||||
# Similar to above, we can't perfectly match Python semantics.
|
||||
# In regular Python code you'd return NotImplemented if the
|
||||
# operand has the wrong type, but in compiled code we'll never
|
||||
# get to execute the type check.
|
||||
emitter.emit_line(
|
||||
"if (PyObject_IsInstance(obj_left, (PyObject *){})) {{".format(
|
||||
emitter.type_struct_name(cl)
|
||||
)
|
||||
)
|
||||
gen.emit_arg_processing(error=GotoHandler("typefail"), raise_exception=False)
|
||||
handle_third_pow_argument(fn, emitter, gen, if_unsupported=["goto typefail2;"])
|
||||
# Ternary __rpow__ calls aren't a thing so immediately bail
|
||||
# if ternary __pow__ returns NotImplemented.
|
||||
if fn.name == "__pow__" and len(fn.args) == 3:
|
||||
fwd_not_implemented_handler = "goto typefail2;"
|
||||
else:
|
||||
fwd_not_implemented_handler = "goto typefail;"
|
||||
gen.emit_call(not_implemented_handler=fwd_not_implemented_handler)
|
||||
gen.emit_error_handling()
|
||||
emitter.emit_line("}")
|
||||
emitter.emit_label("typefail")
|
||||
emitter.emit_line(
|
||||
"if (PyObject_IsInstance(obj_right, (PyObject *){})) {{".format(
|
||||
emitter.type_struct_name(cl)
|
||||
)
|
||||
)
|
||||
gen.set_target(fn_rev)
|
||||
gen.arg_names = ["right", "left"]
|
||||
gen.emit_arg_processing(error=GotoHandler("typefail2"), raise_exception=False)
|
||||
handle_third_pow_argument(fn_rev, emitter, gen, if_unsupported=["goto typefail2;"])
|
||||
gen.emit_call()
|
||||
gen.emit_error_handling()
|
||||
emitter.emit_line("} else {")
|
||||
generate_bin_op_reverse_dunder_call(fn, emitter, fn_rev.name)
|
||||
emitter.emit_line("}")
|
||||
emitter.emit_label("typefail2")
|
||||
emitter.emit_line("Py_INCREF(Py_NotImplemented);")
|
||||
emitter.emit_line("return Py_NotImplemented;")
|
||||
gen.finish()
|
||||
|
||||
|
||||
def generate_bin_op_reverse_dunder_call(fn: FuncIR, emitter: Emitter, rmethod: str) -> None:
|
||||
if fn.name in ("__pow__", "__rpow__"):
|
||||
# Ternary pow() will never call the reverse dunder.
|
||||
emitter.emit_line("if (obj_mod == Py_None) {")
|
||||
emitter.emit_line(f"_Py_IDENTIFIER({rmethod});")
|
||||
emitter.emit_line(
|
||||
'return CPy_CallReverseOpMethod(obj_left, obj_right, "{}", &PyId_{});'.format(
|
||||
op_methods_to_symbols[fn.name], rmethod
|
||||
)
|
||||
)
|
||||
if fn.name in ("__pow__", "__rpow__"):
|
||||
emitter.emit_line("} else {")
|
||||
emitter.emit_line("Py_INCREF(Py_NotImplemented);")
|
||||
emitter.emit_line("return Py_NotImplemented;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
|
||||
def handle_third_pow_argument(
|
||||
fn: FuncIR, emitter: Emitter, gen: WrapperGenerator, *, if_unsupported: list[str]
|
||||
) -> None:
|
||||
if fn.name not in ("__pow__", "__rpow__", "__ipow__"):
|
||||
return
|
||||
|
||||
if (fn.name in ("__pow__", "__ipow__") and len(fn.args) == 2) or fn.name == "__rpow__":
|
||||
# If the power dunder only supports two arguments and the third
|
||||
# argument (AKA mod) is set to a non-default value, simply bail.
|
||||
#
|
||||
# Importantly, this prevents any ternary __rpow__ calls from
|
||||
# happening (as per the language specification).
|
||||
emitter.emit_line("if (obj_mod != Py_None) {")
|
||||
for line in if_unsupported:
|
||||
emitter.emit_line(line)
|
||||
emitter.emit_line("}")
|
||||
# The slot wrapper will receive three arguments, but the call only
|
||||
# supports two so make sure that the third argument isn't passed
|
||||
# along. This is needed as two-argument __(i)pow__ is allowed and
|
||||
# rather common.
|
||||
if len(gen.arg_names) == 3:
|
||||
gen.arg_names.pop()
|
||||
|
||||
|
||||
RICHCOMPARE_OPS = {
|
||||
"__lt__": "Py_LT",
|
||||
"__gt__": "Py_GT",
|
||||
"__le__": "Py_LE",
|
||||
"__ge__": "Py_GE",
|
||||
"__eq__": "Py_EQ",
|
||||
"__ne__": "Py_NE",
|
||||
}
|
||||
|
||||
|
||||
def generate_richcompare_wrapper(cl: ClassIR, emitter: Emitter) -> str | None:
|
||||
"""Generates a wrapper for richcompare dunder methods."""
|
||||
# Sort for determinism on Python 3.5
|
||||
matches = sorted(name for name in RICHCOMPARE_OPS if cl.has_method(name))
|
||||
if not matches:
|
||||
return None
|
||||
|
||||
name = f"{DUNDER_PREFIX}_RichCompare_{cl.name_prefix(emitter.names)}"
|
||||
emitter.emit_line(
|
||||
"static PyObject *{name}(PyObject *obj_lhs, PyObject *obj_rhs, int op) {{".format(
|
||||
name=name
|
||||
)
|
||||
)
|
||||
emitter.emit_line("switch (op) {")
|
||||
for func in matches:
|
||||
emitter.emit_line(f"case {RICHCOMPARE_OPS[func]}: {{")
|
||||
method = cl.get_method(func)
|
||||
assert method is not None
|
||||
generate_wrapper_core(method, emitter, arg_names=["lhs", "rhs"])
|
||||
emitter.emit_line("}")
|
||||
emitter.emit_line("}")
|
||||
|
||||
emitter.emit_line("Py_INCREF(Py_NotImplemented);")
|
||||
emitter.emit_line("return Py_NotImplemented;")
|
||||
|
||||
emitter.emit_line("}")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def generate_get_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __get__ methods."""
|
||||
name = f"{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}"
|
||||
emitter.emit_line(
|
||||
"static PyObject *{name}(PyObject *self, PyObject *instance, PyObject *owner) {{".format(
|
||||
name=name
|
||||
)
|
||||
)
|
||||
emitter.emit_line("instance = instance ? instance : Py_None;")
|
||||
emitter.emit_line(f"return {NATIVE_PREFIX}{fn.cname(emitter.names)}(self, instance, owner);")
|
||||
emitter.emit_line("}")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def generate_hash_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __hash__ methods."""
|
||||
name = f"{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}"
|
||||
emitter.emit_line(f"static Py_ssize_t {name}(PyObject *self) {{")
|
||||
emitter.emit_line(
|
||||
"{}retval = {}{}{}(self);".format(
|
||||
emitter.ctype_spaced(fn.ret_type),
|
||||
emitter.get_group_prefix(fn.decl),
|
||||
NATIVE_PREFIX,
|
||||
fn.cname(emitter.names),
|
||||
)
|
||||
)
|
||||
emitter.emit_error_check("retval", fn.ret_type, "return -1;")
|
||||
if is_int_rprimitive(fn.ret_type):
|
||||
emitter.emit_line("Py_ssize_t val = CPyTagged_AsSsize_t(retval);")
|
||||
else:
|
||||
emitter.emit_line("Py_ssize_t val = PyLong_AsSsize_t(retval);")
|
||||
emitter.emit_dec_ref("retval", fn.ret_type)
|
||||
emitter.emit_line("if (PyErr_Occurred()) return -1;")
|
||||
# We can't return -1 from a hash function..
|
||||
emitter.emit_line("if (val == -1) return -2;")
|
||||
emitter.emit_line("return val;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def generate_len_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __len__ methods."""
|
||||
name = f"{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}"
|
||||
emitter.emit_line(f"static Py_ssize_t {name}(PyObject *self) {{")
|
||||
emitter.emit_line(
|
||||
"{}retval = {}{}{}(self);".format(
|
||||
emitter.ctype_spaced(fn.ret_type),
|
||||
emitter.get_group_prefix(fn.decl),
|
||||
NATIVE_PREFIX,
|
||||
fn.cname(emitter.names),
|
||||
)
|
||||
)
|
||||
emitter.emit_error_check("retval", fn.ret_type, "return -1;")
|
||||
if is_int_rprimitive(fn.ret_type):
|
||||
emitter.emit_line("Py_ssize_t val = CPyTagged_AsSsize_t(retval);")
|
||||
else:
|
||||
emitter.emit_line("Py_ssize_t val = PyLong_AsSsize_t(retval);")
|
||||
emitter.emit_dec_ref("retval", fn.ret_type)
|
||||
emitter.emit_line("if (PyErr_Occurred()) return -1;")
|
||||
emitter.emit_line("return val;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def generate_bool_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __bool__ methods."""
|
||||
name = f"{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}"
|
||||
emitter.emit_line(f"static int {name}(PyObject *self) {{")
|
||||
emitter.emit_line(
|
||||
"{}val = {}{}(self);".format(
|
||||
emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX, fn.cname(emitter.names)
|
||||
)
|
||||
)
|
||||
emitter.emit_error_check("val", fn.ret_type, "return -1;")
|
||||
# This wouldn't be that hard to fix but it seems unimportant and
|
||||
# getting error handling and unboxing right would be fiddly. (And
|
||||
# way easier to do in IR!)
|
||||
assert is_bool_rprimitive(fn.ret_type), "Only bool return supported for __bool__"
|
||||
emitter.emit_line("return val;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def generate_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __delitem__.
|
||||
|
||||
This is only called from a combined __delitem__/__setitem__ wrapper.
|
||||
"""
|
||||
name = "{}{}{}".format(DUNDER_PREFIX, "__delitem__", cl.name_prefix(emitter.names))
|
||||
input_args = ", ".join(f"PyObject *obj_{arg.name}" for arg in fn.args)
|
||||
emitter.emit_line(f"static int {name}({input_args}) {{")
|
||||
generate_set_del_item_wrapper_inner(fn, emitter, fn.args)
|
||||
return name
|
||||
|
||||
|
||||
def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for native __setitem__ method (also works for __delitem__).
|
||||
|
||||
This is used with the mapping protocol slot. Arguments are taken as *PyObjects and we
|
||||
return a negative C int on error.
|
||||
|
||||
Create a separate wrapper function for __delitem__ as needed and have the
|
||||
__setitem__ wrapper call it if the value is NULL. Return the name
|
||||
of the outer (__setitem__) wrapper.
|
||||
"""
|
||||
method_cls = cl.get_method_and_class("__delitem__")
|
||||
del_name = None
|
||||
if method_cls and method_cls[1] == cl:
|
||||
# Generate a separate wrapper for __delitem__
|
||||
del_name = generate_del_item_wrapper(cl, method_cls[0], emitter)
|
||||
|
||||
args = fn.args
|
||||
if fn.name == "__delitem__":
|
||||
# Add an extra argument for value that we expect to be NULL.
|
||||
args = list(args) + [RuntimeArg("___value", object_rprimitive, ARG_POS)]
|
||||
|
||||
name = "{}{}{}".format(DUNDER_PREFIX, "__setitem__", cl.name_prefix(emitter.names))
|
||||
input_args = ", ".join(f"PyObject *obj_{arg.name}" for arg in args)
|
||||
emitter.emit_line(f"static int {name}({input_args}) {{")
|
||||
|
||||
# First check if this is __delitem__
|
||||
emitter.emit_line(f"if (obj_{args[2].name} == NULL) {{")
|
||||
if del_name is not None:
|
||||
# We have a native implementation, so call it
|
||||
emitter.emit_line(f"return {del_name}(obj_{args[0].name}, obj_{args[1].name});")
|
||||
else:
|
||||
# Try to call superclass method instead
|
||||
emitter.emit_line(f"PyObject *super = CPy_Super(CPyModule_builtins, obj_{args[0].name});")
|
||||
emitter.emit_line("if (super == NULL) return -1;")
|
||||
emitter.emit_line(
|
||||
'PyObject *result = PyObject_CallMethod(super, "__delitem__", "O", obj_{});'.format(
|
||||
args[1].name
|
||||
)
|
||||
)
|
||||
emitter.emit_line("Py_DECREF(super);")
|
||||
emitter.emit_line("Py_XDECREF(result);")
|
||||
emitter.emit_line("return result == NULL ? -1 : 0;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
method_cls = cl.get_method_and_class("__setitem__")
|
||||
if method_cls and method_cls[1] == cl:
|
||||
generate_set_del_item_wrapper_inner(fn, emitter, args)
|
||||
else:
|
||||
emitter.emit_line(f"PyObject *super = CPy_Super(CPyModule_builtins, obj_{args[0].name});")
|
||||
emitter.emit_line("if (super == NULL) return -1;")
|
||||
emitter.emit_line("PyObject *result;")
|
||||
|
||||
if method_cls is None and cl.builtin_base is None:
|
||||
msg = f"'{cl.name}' object does not support item assignment"
|
||||
emitter.emit_line(f'PyErr_SetString(PyExc_TypeError, "{msg}");')
|
||||
emitter.emit_line("result = NULL;")
|
||||
else:
|
||||
# A base class may have __setitem__
|
||||
emitter.emit_line(
|
||||
'result = PyObject_CallMethod(super, "__setitem__", "OO", obj_{}, obj_{});'.format(
|
||||
args[1].name, args[2].name
|
||||
)
|
||||
)
|
||||
emitter.emit_line("Py_DECREF(super);")
|
||||
emitter.emit_line("Py_XDECREF(result);")
|
||||
emitter.emit_line("return result == NULL ? -1 : 0;")
|
||||
emitter.emit_line("}")
|
||||
return name
|
||||
|
||||
|
||||
def generate_set_del_item_wrapper_inner(
|
||||
fn: FuncIR, emitter: Emitter, args: Sequence[RuntimeArg]
|
||||
) -> None:
|
||||
for arg in args:
|
||||
generate_arg_check(arg.name, arg.type, emitter, GotoHandler("fail"))
|
||||
native_args = ", ".join(f"arg_{arg.name}" for arg in args)
|
||||
emitter.emit_line(
|
||||
"{}val = {}{}({});".format(
|
||||
emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX, fn.cname(emitter.names), native_args
|
||||
)
|
||||
)
|
||||
emitter.emit_error_check("val", fn.ret_type, "goto fail;")
|
||||
emitter.emit_dec_ref("val", fn.ret_type)
|
||||
emitter.emit_line("return 0;")
|
||||
emitter.emit_label("fail")
|
||||
emitter.emit_line("return -1;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
|
||||
def generate_contains_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
|
||||
"""Generates a wrapper for a native __contains__ method."""
|
||||
name = f"{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}"
|
||||
emitter.emit_line(f"static int {name}(PyObject *self, PyObject *obj_item) {{")
|
||||
generate_arg_check("item", fn.args[1].type, emitter, ReturnHandler("-1"))
|
||||
emitter.emit_line(
|
||||
"{}val = {}{}(self, arg_item);".format(
|
||||
emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX, fn.cname(emitter.names)
|
||||
)
|
||||
)
|
||||
emitter.emit_error_check("val", fn.ret_type, "return -1;")
|
||||
if is_bool_rprimitive(fn.ret_type):
|
||||
emitter.emit_line("return val;")
|
||||
else:
|
||||
emitter.emit_line("int boolval = PyObject_IsTrue(val);")
|
||||
emitter.emit_dec_ref("val", fn.ret_type)
|
||||
emitter.emit_line("return boolval;")
|
||||
emitter.emit_line("}")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
# Helpers
|
||||
|
||||
|
||||
def generate_wrapper_core(
|
||||
fn: FuncIR,
|
||||
emitter: Emitter,
|
||||
optional_args: list[RuntimeArg] | None = None,
|
||||
arg_names: list[str] | None = None,
|
||||
cleanups: list[str] | None = None,
|
||||
traceback_code: str | None = None,
|
||||
) -> None:
|
||||
"""Generates the core part of a wrapper function for a native function.
|
||||
|
||||
This expects each argument as a PyObject * named obj_{arg} as a precondition.
|
||||
It converts the PyObject *s to the necessary types, checking and unboxing if necessary,
|
||||
makes the call, then boxes the result if necessary and returns it.
|
||||
"""
|
||||
gen = WrapperGenerator(None, emitter)
|
||||
gen.set_target(fn)
|
||||
if arg_names:
|
||||
gen.arg_names = arg_names
|
||||
gen.cleanups = cleanups or []
|
||||
gen.optional_args = optional_args or []
|
||||
gen.traceback_code = traceback_code or ""
|
||||
|
||||
error = ReturnHandler("NULL") if not gen.use_goto() else GotoHandler("fail")
|
||||
gen.emit_arg_processing(error=error)
|
||||
gen.emit_call()
|
||||
gen.emit_error_handling()
|
||||
|
||||
|
||||
def generate_arg_check(
|
||||
name: str,
|
||||
typ: RType,
|
||||
emitter: Emitter,
|
||||
error: ErrorHandler | None = None,
|
||||
*,
|
||||
optional: bool = False,
|
||||
raise_exception: bool = True,
|
||||
bitmap_arg_index: int = 0,
|
||||
) -> None:
|
||||
"""Insert a runtime check for argument and unbox if necessary.
|
||||
|
||||
The object is named PyObject *obj_{}. This is expected to generate
|
||||
a value of name arg_{} (unboxed if necessary). For each primitive a runtime
|
||||
check ensures the correct type.
|
||||
"""
|
||||
error = error or AssignHandler()
|
||||
if typ.is_unboxed:
|
||||
if typ.error_overlap and optional:
|
||||
# Update bitmap is value is provided.
|
||||
init = emitter.c_undefined_value(typ)
|
||||
emitter.emit_line(f"{emitter.ctype(typ)} arg_{name} = {init};")
|
||||
emitter.emit_line(f"if (obj_{name} != NULL) {{")
|
||||
bitmap = bitmap_name(bitmap_arg_index // BITMAP_BITS)
|
||||
emitter.emit_line(f"{bitmap} |= 1 << {bitmap_arg_index & (BITMAP_BITS - 1)};")
|
||||
emitter.emit_unbox(
|
||||
f"obj_{name}",
|
||||
f"arg_{name}",
|
||||
typ,
|
||||
declare_dest=False,
|
||||
raise_exception=raise_exception,
|
||||
error=error,
|
||||
borrow=True,
|
||||
)
|
||||
emitter.emit_line("}")
|
||||
else:
|
||||
# Borrow when unboxing to avoid reference count manipulation.
|
||||
emitter.emit_unbox(
|
||||
f"obj_{name}",
|
||||
f"arg_{name}",
|
||||
typ,
|
||||
declare_dest=True,
|
||||
raise_exception=raise_exception,
|
||||
error=error,
|
||||
borrow=True,
|
||||
optional=optional,
|
||||
)
|
||||
elif is_object_rprimitive(typ):
|
||||
# Object is trivial since any object is valid
|
||||
if optional:
|
||||
emitter.emit_line(f"PyObject *arg_{name};")
|
||||
emitter.emit_line(f"if (obj_{name} == NULL) {{")
|
||||
emitter.emit_line(f"arg_{name} = {emitter.c_error_value(typ)};")
|
||||
emitter.emit_lines("} else {", f"arg_{name} = obj_{name}; ", "}")
|
||||
else:
|
||||
emitter.emit_line(f"PyObject *arg_{name} = obj_{name};")
|
||||
else:
|
||||
emitter.emit_cast(
|
||||
f"obj_{name}",
|
||||
f"arg_{name}",
|
||||
typ,
|
||||
declare_dest=True,
|
||||
raise_exception=raise_exception,
|
||||
error=error,
|
||||
optional=optional,
|
||||
)
|
||||
|
||||
|
||||
class WrapperGenerator:
|
||||
"""Helper that simplifies the generation of wrapper functions."""
|
||||
|
||||
# TODO: Use this for more wrappers
|
||||
|
||||
def __init__(self, cl: ClassIR | None, emitter: Emitter) -> None:
|
||||
self.cl = cl
|
||||
self.emitter = emitter
|
||||
self.cleanups: list[str] = []
|
||||
self.optional_args: list[RuntimeArg] = []
|
||||
self.traceback_code = ""
|
||||
|
||||
def set_target(self, fn: FuncIR) -> None:
|
||||
"""Set the wrapped function.
|
||||
|
||||
It's fine to modify the attributes initialized here later to customize
|
||||
the wrapper function.
|
||||
"""
|
||||
self.target_name = fn.name
|
||||
self.target_cname = fn.cname(self.emitter.names)
|
||||
self.num_bitmap_args = fn.sig.num_bitmap_args
|
||||
if self.num_bitmap_args:
|
||||
self.args = fn.args[: -self.num_bitmap_args]
|
||||
else:
|
||||
self.args = fn.args
|
||||
self.arg_names = [arg.name for arg in self.args]
|
||||
self.ret_type = fn.ret_type
|
||||
|
||||
def wrapper_name(self) -> str:
|
||||
"""Return the name of the wrapper function."""
|
||||
return "{}{}{}".format(
|
||||
DUNDER_PREFIX,
|
||||
self.target_name,
|
||||
self.cl.name_prefix(self.emitter.names) if self.cl else "",
|
||||
)
|
||||
|
||||
def use_goto(self) -> bool:
|
||||
"""Do we use a goto for error handling (instead of straight return)?"""
|
||||
return bool(self.cleanups or self.traceback_code)
|
||||
|
||||
def emit_header(self) -> None:
|
||||
"""Emit the function header of the wrapper implementation."""
|
||||
input_args = ", ".join(f"PyObject *obj_{arg}" for arg in self.arg_names)
|
||||
self.emitter.emit_line(
|
||||
"static PyObject *{name}({input_args}) {{".format(
|
||||
name=self.wrapper_name(), input_args=input_args
|
||||
)
|
||||
)
|
||||
|
||||
def emit_arg_processing(
|
||||
self, error: ErrorHandler | None = None, raise_exception: bool = True
|
||||
) -> None:
|
||||
"""Emit validation and unboxing of arguments."""
|
||||
error = error or self.error()
|
||||
bitmap_arg_index = 0
|
||||
for arg_name, arg in zip(self.arg_names, self.args):
|
||||
# Suppress the argument check for *args/**kwargs, since we know it must be right.
|
||||
typ = arg.type if arg.kind not in (ARG_STAR, ARG_STAR2) else object_rprimitive
|
||||
optional = arg in self.optional_args
|
||||
generate_arg_check(
|
||||
arg_name,
|
||||
typ,
|
||||
self.emitter,
|
||||
error,
|
||||
raise_exception=raise_exception,
|
||||
optional=optional,
|
||||
bitmap_arg_index=bitmap_arg_index,
|
||||
)
|
||||
if optional and typ.error_overlap:
|
||||
bitmap_arg_index += 1
|
||||
|
||||
def emit_call(self, not_implemented_handler: str = "") -> None:
|
||||
"""Emit call to the wrapper function.
|
||||
|
||||
If not_implemented_handler is non-empty, use this C code to handle
|
||||
a NotImplemented return value (if it's possible based on the return type).
|
||||
"""
|
||||
native_args = ", ".join(f"arg_{arg}" for arg in self.arg_names)
|
||||
if self.num_bitmap_args:
|
||||
bitmap_args = ", ".join(
|
||||
[bitmap_name(i) for i in reversed(range(self.num_bitmap_args))]
|
||||
)
|
||||
native_args = f"{native_args}, {bitmap_args}"
|
||||
|
||||
ret_type = self.ret_type
|
||||
emitter = self.emitter
|
||||
if ret_type.is_unboxed or self.use_goto():
|
||||
# TODO: The Py_RETURN macros return the correct PyObject * with reference count
|
||||
# handling. Are they relevant?
|
||||
emitter.emit_line(
|
||||
"{}retval = {}{}({});".format(
|
||||
emitter.ctype_spaced(ret_type), NATIVE_PREFIX, self.target_cname, native_args
|
||||
)
|
||||
)
|
||||
emitter.emit_lines(*self.cleanups)
|
||||
if ret_type.is_unboxed:
|
||||
emitter.emit_error_check("retval", ret_type, "return NULL;")
|
||||
emitter.emit_box("retval", "retbox", ret_type, declare_dest=True)
|
||||
|
||||
emitter.emit_line("return {};".format("retbox" if ret_type.is_unboxed else "retval"))
|
||||
else:
|
||||
if not_implemented_handler and not isinstance(ret_type, RInstance):
|
||||
# The return value type may overlap with NotImplemented.
|
||||
emitter.emit_line(
|
||||
"PyObject *retbox = {}{}({});".format(
|
||||
NATIVE_PREFIX, self.target_cname, native_args
|
||||
)
|
||||
)
|
||||
emitter.emit_lines(
|
||||
"if (retbox == Py_NotImplemented) {",
|
||||
not_implemented_handler,
|
||||
"}",
|
||||
"return retbox;",
|
||||
)
|
||||
else:
|
||||
emitter.emit_line(f"return {NATIVE_PREFIX}{self.target_cname}({native_args});")
|
||||
# TODO: Tracebacks?
|
||||
|
||||
def error(self) -> ErrorHandler:
|
||||
"""Figure out how to deal with errors in the wrapper."""
|
||||
if self.cleanups or self.traceback_code:
|
||||
# We'll have a label at the end with error handling code.
|
||||
return GotoHandler("fail")
|
||||
else:
|
||||
# Nothing special needs to done to handle errors, so just return.
|
||||
return ReturnHandler("NULL")
|
||||
|
||||
def emit_error_handling(self) -> None:
|
||||
"""Emit error handling block at the end of the wrapper, if needed."""
|
||||
emitter = self.emitter
|
||||
if self.use_goto():
|
||||
emitter.emit_label("fail")
|
||||
emitter.emit_lines(*self.cleanups)
|
||||
if self.traceback_code:
|
||||
emitter.emit_line(self.traceback_code)
|
||||
emitter.emit_line("return NULL;")
|
||||
|
||||
def finish(self) -> None:
|
||||
self.emitter.emit_line("}")
|
||||
Binary file not shown.
302
venv/lib/python3.12/site-packages/mypyc/codegen/literals.py
Normal file
302
venv/lib/python3.12/site-packages/mypyc/codegen/literals.py
Normal file
@@ -0,0 +1,302 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final, FrozenSet, Tuple, Union
|
||||
from typing_extensions import TypeGuard
|
||||
|
||||
# Supported Python literal types. All tuple / frozenset items must have supported
|
||||
# literal types as well, but we can't represent the type precisely.
|
||||
LiteralValue = Union[
|
||||
str, bytes, int, bool, float, complex, Tuple[object, ...], FrozenSet[object], None
|
||||
]
|
||||
|
||||
|
||||
def _is_literal_value(obj: object) -> TypeGuard[LiteralValue]:
|
||||
return isinstance(obj, (str, bytes, int, float, complex, tuple, frozenset, type(None)))
|
||||
|
||||
|
||||
# Some literals are singletons and handled specially (None, False and True)
|
||||
NUM_SINGLETONS: Final = 3
|
||||
|
||||
|
||||
class Literals:
|
||||
"""Collection of literal values used in a compilation group and related helpers."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
# Each dict maps value to literal index (0, 1, ...)
|
||||
self.str_literals: dict[str, int] = {}
|
||||
self.bytes_literals: dict[bytes, int] = {}
|
||||
self.int_literals: dict[int, int] = {}
|
||||
self.float_literals: dict[float, int] = {}
|
||||
self.complex_literals: dict[complex, int] = {}
|
||||
self.tuple_literals: dict[tuple[object, ...], int] = {}
|
||||
self.frozenset_literals: dict[frozenset[object], int] = {}
|
||||
|
||||
def record_literal(self, value: LiteralValue) -> None:
|
||||
"""Ensure that the literal value is available in generated code."""
|
||||
if value is None or value is True or value is False:
|
||||
# These are special cased and always present
|
||||
return
|
||||
if isinstance(value, str):
|
||||
str_literals = self.str_literals
|
||||
if value not in str_literals:
|
||||
str_literals[value] = len(str_literals)
|
||||
elif isinstance(value, bytes):
|
||||
bytes_literals = self.bytes_literals
|
||||
if value not in bytes_literals:
|
||||
bytes_literals[value] = len(bytes_literals)
|
||||
elif isinstance(value, int):
|
||||
int_literals = self.int_literals
|
||||
if value not in int_literals:
|
||||
int_literals[value] = len(int_literals)
|
||||
elif isinstance(value, float):
|
||||
float_literals = self.float_literals
|
||||
if value not in float_literals:
|
||||
float_literals[value] = len(float_literals)
|
||||
elif isinstance(value, complex):
|
||||
complex_literals = self.complex_literals
|
||||
if value not in complex_literals:
|
||||
complex_literals[value] = len(complex_literals)
|
||||
elif isinstance(value, tuple):
|
||||
tuple_literals = self.tuple_literals
|
||||
if value not in tuple_literals:
|
||||
for item in value:
|
||||
assert _is_literal_value(item)
|
||||
self.record_literal(item)
|
||||
tuple_literals[value] = len(tuple_literals)
|
||||
elif isinstance(value, frozenset):
|
||||
frozenset_literals = self.frozenset_literals
|
||||
if value not in frozenset_literals:
|
||||
for item in value:
|
||||
assert _is_literal_value(item)
|
||||
self.record_literal(item)
|
||||
frozenset_literals[value] = len(frozenset_literals)
|
||||
else:
|
||||
assert False, "invalid literal: %r" % value
|
||||
|
||||
def literal_index(self, value: LiteralValue) -> int:
|
||||
"""Return the index to the literals array for given value."""
|
||||
# The array contains first None and booleans, followed by all str values,
|
||||
# followed by bytes values, etc.
|
||||
if value is None:
|
||||
return 0
|
||||
elif value is False:
|
||||
return 1
|
||||
elif value is True:
|
||||
return 2
|
||||
n = NUM_SINGLETONS
|
||||
if isinstance(value, str):
|
||||
return n + self.str_literals[value]
|
||||
n += len(self.str_literals)
|
||||
if isinstance(value, bytes):
|
||||
return n + self.bytes_literals[value]
|
||||
n += len(self.bytes_literals)
|
||||
if isinstance(value, int):
|
||||
return n + self.int_literals[value]
|
||||
n += len(self.int_literals)
|
||||
if isinstance(value, float):
|
||||
return n + self.float_literals[value]
|
||||
n += len(self.float_literals)
|
||||
if isinstance(value, complex):
|
||||
return n + self.complex_literals[value]
|
||||
n += len(self.complex_literals)
|
||||
if isinstance(value, tuple):
|
||||
return n + self.tuple_literals[value]
|
||||
n += len(self.tuple_literals)
|
||||
if isinstance(value, frozenset):
|
||||
return n + self.frozenset_literals[value]
|
||||
assert False, "invalid literal: %r" % value
|
||||
|
||||
def num_literals(self) -> int:
|
||||
# The first three are for None, True and False
|
||||
return (
|
||||
NUM_SINGLETONS
|
||||
+ len(self.str_literals)
|
||||
+ len(self.bytes_literals)
|
||||
+ len(self.int_literals)
|
||||
+ len(self.float_literals)
|
||||
+ len(self.complex_literals)
|
||||
+ len(self.tuple_literals)
|
||||
+ len(self.frozenset_literals)
|
||||
)
|
||||
|
||||
# The following methods return the C encodings of literal values
|
||||
# of different types
|
||||
|
||||
def encoded_str_values(self) -> list[bytes]:
|
||||
return _encode_str_values(self.str_literals)
|
||||
|
||||
def encoded_int_values(self) -> list[bytes]:
|
||||
return _encode_int_values(self.int_literals)
|
||||
|
||||
def encoded_bytes_values(self) -> list[bytes]:
|
||||
return _encode_bytes_values(self.bytes_literals)
|
||||
|
||||
def encoded_float_values(self) -> list[str]:
|
||||
return _encode_float_values(self.float_literals)
|
||||
|
||||
def encoded_complex_values(self) -> list[str]:
|
||||
return _encode_complex_values(self.complex_literals)
|
||||
|
||||
def encoded_tuple_values(self) -> list[str]:
|
||||
return self._encode_collection_values(self.tuple_literals)
|
||||
|
||||
def encoded_frozenset_values(self) -> list[str]:
|
||||
return self._encode_collection_values(self.frozenset_literals)
|
||||
|
||||
def _encode_collection_values(
|
||||
self, values: dict[tuple[object, ...], int] | dict[frozenset[object], int]
|
||||
) -> list[str]:
|
||||
"""Encode tuple/frozenset values into a C array.
|
||||
|
||||
The format of the result is like this:
|
||||
|
||||
<number of collections>
|
||||
<length of the first collection>
|
||||
<literal index of first item>
|
||||
...
|
||||
<literal index of last item>
|
||||
<length of the second collection>
|
||||
...
|
||||
"""
|
||||
value_by_index = {index: value for value, index in values.items()}
|
||||
result = []
|
||||
count = len(values)
|
||||
result.append(str(count))
|
||||
for i in range(count):
|
||||
value = value_by_index[i]
|
||||
result.append(str(len(value)))
|
||||
for item in value:
|
||||
assert _is_literal_value(item)
|
||||
index = self.literal_index(item)
|
||||
result.append(str(index))
|
||||
return result
|
||||
|
||||
|
||||
def _encode_str_values(values: dict[str, int]) -> list[bytes]:
|
||||
value_by_index = {index: value for value, index in values.items()}
|
||||
result = []
|
||||
line: list[bytes] = []
|
||||
line_len = 0
|
||||
for i in range(len(values)):
|
||||
value = value_by_index[i]
|
||||
c_literal = format_str_literal(value)
|
||||
c_len = len(c_literal)
|
||||
if line_len > 0 and line_len + c_len > 70:
|
||||
result.append(format_int(len(line)) + b"".join(line))
|
||||
line = []
|
||||
line_len = 0
|
||||
line.append(c_literal)
|
||||
line_len += c_len
|
||||
if line:
|
||||
result.append(format_int(len(line)) + b"".join(line))
|
||||
result.append(b"")
|
||||
return result
|
||||
|
||||
|
||||
def _encode_bytes_values(values: dict[bytes, int]) -> list[bytes]:
|
||||
value_by_index = {index: value for value, index in values.items()}
|
||||
result = []
|
||||
line: list[bytes] = []
|
||||
line_len = 0
|
||||
for i in range(len(values)):
|
||||
value = value_by_index[i]
|
||||
c_init = format_int(len(value))
|
||||
c_len = len(c_init) + len(value)
|
||||
if line_len > 0 and line_len + c_len > 70:
|
||||
result.append(format_int(len(line)) + b"".join(line))
|
||||
line = []
|
||||
line_len = 0
|
||||
line.append(c_init + value)
|
||||
line_len += c_len
|
||||
if line:
|
||||
result.append(format_int(len(line)) + b"".join(line))
|
||||
result.append(b"")
|
||||
return result
|
||||
|
||||
|
||||
def format_int(n: int) -> bytes:
|
||||
"""Format an integer using a variable-length binary encoding."""
|
||||
if n < 128:
|
||||
a = [n]
|
||||
else:
|
||||
a = []
|
||||
while n > 0:
|
||||
a.insert(0, n & 0x7F)
|
||||
n >>= 7
|
||||
for i in range(len(a) - 1):
|
||||
# If the highest bit is set, more 7-bit digits follow
|
||||
a[i] |= 0x80
|
||||
return bytes(a)
|
||||
|
||||
|
||||
def format_str_literal(s: str) -> bytes:
|
||||
utf8 = s.encode("utf-8")
|
||||
return format_int(len(utf8)) + utf8
|
||||
|
||||
|
||||
def _encode_int_values(values: dict[int, int]) -> list[bytes]:
|
||||
"""Encode int values into C strings.
|
||||
|
||||
Values are stored in base 10 and separated by 0 bytes.
|
||||
"""
|
||||
value_by_index = {index: value for value, index in values.items()}
|
||||
result = []
|
||||
line: list[bytes] = []
|
||||
line_len = 0
|
||||
for i in range(len(values)):
|
||||
value = value_by_index[i]
|
||||
encoded = b"%d" % value
|
||||
if line_len > 0 and line_len + len(encoded) > 70:
|
||||
result.append(format_int(len(line)) + b"\0".join(line))
|
||||
line = []
|
||||
line_len = 0
|
||||
line.append(encoded)
|
||||
line_len += len(encoded)
|
||||
if line:
|
||||
result.append(format_int(len(line)) + b"\0".join(line))
|
||||
result.append(b"")
|
||||
return result
|
||||
|
||||
|
||||
def float_to_c(x: float) -> str:
|
||||
"""Return C literal representation of a float value."""
|
||||
s = str(x)
|
||||
if s == "inf":
|
||||
return "INFINITY"
|
||||
elif s == "-inf":
|
||||
return "-INFINITY"
|
||||
elif s == "nan":
|
||||
return "NAN"
|
||||
return s
|
||||
|
||||
|
||||
def _encode_float_values(values: dict[float, int]) -> list[str]:
|
||||
"""Encode float values into a C array values.
|
||||
|
||||
The result contains the number of values followed by individual values.
|
||||
"""
|
||||
value_by_index = {index: value for value, index in values.items()}
|
||||
result = []
|
||||
num = len(values)
|
||||
result.append(str(num))
|
||||
for i in range(num):
|
||||
value = value_by_index[i]
|
||||
result.append(float_to_c(value))
|
||||
return result
|
||||
|
||||
|
||||
def _encode_complex_values(values: dict[complex, int]) -> list[str]:
|
||||
"""Encode float values into a C array values.
|
||||
|
||||
The result contains the number of values followed by pairs of doubles
|
||||
representing complex numbers.
|
||||
"""
|
||||
value_by_index = {index: value for value, index in values.items()}
|
||||
result = []
|
||||
num = len(values)
|
||||
result.append(str(num))
|
||||
for i in range(num):
|
||||
value = value_by_index[i]
|
||||
result.append(float_to_c(value.real))
|
||||
result.append(float_to_c(value.imag))
|
||||
return result
|
||||
BIN
venv/lib/python3.12/site-packages/mypyc/common.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/mypyc/common.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
136
venv/lib/python3.12/site-packages/mypyc/common.py
Normal file
136
venv/lib/python3.12/site-packages/mypyc/common.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import sysconfig
|
||||
from typing import Any, Dict, Final
|
||||
|
||||
from mypy.util import unnamed_function
|
||||
|
||||
PREFIX: Final = "CPyPy_" # Python wrappers
|
||||
NATIVE_PREFIX: Final = "CPyDef_" # Native functions etc.
|
||||
DUNDER_PREFIX: Final = "CPyDunder_" # Wrappers for exposing dunder methods to the API
|
||||
REG_PREFIX: Final = "cpy_r_" # Registers
|
||||
STATIC_PREFIX: Final = "CPyStatic_" # Static variables (for literals etc.)
|
||||
TYPE_PREFIX: Final = "CPyType_" # Type object struct
|
||||
MODULE_PREFIX: Final = "CPyModule_" # Cached modules
|
||||
ATTR_PREFIX: Final = "_" # Attributes
|
||||
|
||||
ENV_ATTR_NAME: Final = "__mypyc_env__"
|
||||
NEXT_LABEL_ATTR_NAME: Final = "__mypyc_next_label__"
|
||||
TEMP_ATTR_NAME: Final = "__mypyc_temp__"
|
||||
LAMBDA_NAME: Final = "__mypyc_lambda__"
|
||||
PROPSET_PREFIX: Final = "__mypyc_setter__"
|
||||
SELF_NAME: Final = "__mypyc_self__"
|
||||
|
||||
# Max short int we accept as a literal is based on 32-bit platforms,
|
||||
# so that we can just always emit the same code.
|
||||
|
||||
TOP_LEVEL_NAME: Final = "__top_level__" # Special function representing module top level
|
||||
|
||||
# Maximal number of subclasses for a class to trigger fast path in isinstance() checks.
|
||||
FAST_ISINSTANCE_MAX_SUBCLASSES: Final = 2
|
||||
|
||||
# Size of size_t, if configured.
|
||||
SIZEOF_SIZE_T_SYSCONFIG: Final = sysconfig.get_config_var("SIZEOF_SIZE_T")
|
||||
|
||||
SIZEOF_SIZE_T: Final = (
|
||||
int(SIZEOF_SIZE_T_SYSCONFIG)
|
||||
if SIZEOF_SIZE_T_SYSCONFIG is not None
|
||||
else (sys.maxsize + 1).bit_length() // 8
|
||||
)
|
||||
|
||||
IS_32_BIT_PLATFORM: Final = int(SIZEOF_SIZE_T) == 4
|
||||
|
||||
PLATFORM_SIZE = 4 if IS_32_BIT_PLATFORM else 8
|
||||
|
||||
# Maximum value for a short tagged integer.
|
||||
MAX_SHORT_INT: Final = 2 ** (8 * int(SIZEOF_SIZE_T) - 2) - 1
|
||||
|
||||
# Minimum value for a short tagged integer.
|
||||
MIN_SHORT_INT: Final = -(MAX_SHORT_INT) - 1
|
||||
|
||||
# Maximum value for a short tagged integer represented as a C integer literal.
|
||||
#
|
||||
# Note: Assume that the compiled code uses the same bit width as mypyc
|
||||
MAX_LITERAL_SHORT_INT: Final = MAX_SHORT_INT
|
||||
MIN_LITERAL_SHORT_INT: Final = -MAX_LITERAL_SHORT_INT - 1
|
||||
|
||||
# Description of the C type used to track the definedness of attributes and
|
||||
# the presence of argument default values that have types with overlapping
|
||||
# error values. Each tracked attribute/argument has a dedicated bit in the
|
||||
# relevant bitmap.
|
||||
BITMAP_TYPE: Final = "uint32_t"
|
||||
BITMAP_BITS: Final = 32
|
||||
|
||||
# Runtime C library files
|
||||
RUNTIME_C_FILES: Final = [
|
||||
"init.c",
|
||||
"getargs.c",
|
||||
"getargsfast.c",
|
||||
"int_ops.c",
|
||||
"float_ops.c",
|
||||
"str_ops.c",
|
||||
"bytes_ops.c",
|
||||
"list_ops.c",
|
||||
"dict_ops.c",
|
||||
"set_ops.c",
|
||||
"tuple_ops.c",
|
||||
"exc_ops.c",
|
||||
"misc_ops.c",
|
||||
"generic_ops.c",
|
||||
]
|
||||
|
||||
|
||||
JsonDict = Dict[str, Any]
|
||||
|
||||
|
||||
def shared_lib_name(group_name: str) -> str:
|
||||
"""Given a group name, return the actual name of its extension module.
|
||||
|
||||
(This just adds a suffix to the final component.)
|
||||
"""
|
||||
return f"{group_name}__mypyc"
|
||||
|
||||
|
||||
def short_name(name: str) -> str:
|
||||
if name.startswith("builtins."):
|
||||
return name[9:]
|
||||
return name
|
||||
|
||||
|
||||
def use_vectorcall(capi_version: tuple[int, int]) -> bool:
|
||||
# We can use vectorcalls to make calls on Python 3.8+ (PEP 590).
|
||||
return capi_version >= (3, 8)
|
||||
|
||||
|
||||
def use_method_vectorcall(capi_version: tuple[int, int]) -> bool:
|
||||
# We can use a dedicated vectorcall API to call methods on Python 3.9+.
|
||||
return capi_version >= (3, 9)
|
||||
|
||||
|
||||
def get_id_from_name(name: str, fullname: str, line: int) -> str:
|
||||
"""Create a unique id for a function.
|
||||
|
||||
This creates an id that is unique for any given function definition, so that it can be used as
|
||||
a dictionary key. This is usually the fullname of the function, but this is different in that
|
||||
it handles the case where the function is named '_', in which case multiple different functions
|
||||
could have the same name."""
|
||||
if unnamed_function(name):
|
||||
return f"{fullname}.{line}"
|
||||
else:
|
||||
return fullname
|
||||
|
||||
|
||||
def short_id_from_name(func_name: str, shortname: str, line: int | None) -> str:
|
||||
if unnamed_function(func_name):
|
||||
assert line is not None
|
||||
partial_name = f"{shortname}.{line}"
|
||||
else:
|
||||
partial_name = shortname
|
||||
return partial_name
|
||||
|
||||
|
||||
def bitmap_name(index: int) -> str:
|
||||
if index == 0:
|
||||
return "__bitmap"
|
||||
return f"__bitmap{index + 1}"
|
||||
BIN
venv/lib/python3.12/site-packages/mypyc/crash.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/mypyc/crash.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
31
venv/lib/python3.12/site-packages/mypyc/crash.py
Normal file
31
venv/lib/python3.12/site-packages/mypyc/crash.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
from contextlib import contextmanager
|
||||
from typing import Iterator, NoReturn
|
||||
|
||||
|
||||
@contextmanager
|
||||
def catch_errors(module_path: str, line: int) -> Iterator[None]:
|
||||
try:
|
||||
yield
|
||||
except Exception:
|
||||
crash_report(module_path, line)
|
||||
|
||||
|
||||
def crash_report(module_path: str, line: int) -> NoReturn:
|
||||
# Adapted from report_internal_error in mypy
|
||||
err = sys.exc_info()[1]
|
||||
tb = traceback.extract_stack()[:-4]
|
||||
# Excise all the traceback from the test runner
|
||||
for i, x in enumerate(tb):
|
||||
if x.name == "pytest_runtest_call":
|
||||
tb = tb[i + 1 :]
|
||||
break
|
||||
tb2 = traceback.extract_tb(sys.exc_info()[2])[1:]
|
||||
print("Traceback (most recent call last):")
|
||||
for s in traceback.format_list(tb + tb2):
|
||||
print(s.rstrip("\n"))
|
||||
print(f"{module_path}:{line}: {type(err).__name__}: {err}")
|
||||
raise SystemExit(2)
|
||||
20
venv/lib/python3.12/site-packages/mypyc/doc/Makefile
Normal file
20
venv/lib/python3.12/site-packages/mypyc/doc/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,27 @@
|
||||
.. _bool-ops:
|
||||
|
||||
Native boolean operations
|
||||
=========================
|
||||
|
||||
Operations on ``bool`` values that are listed here have fast,
|
||||
optimized implementations.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
* ``True``
|
||||
* ``False``
|
||||
* ``bool(obj)``
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* ``b1 and b2``
|
||||
* ``b1 or b2``
|
||||
* ``not b``
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``any(expr for ... in ...)``
|
||||
* ``all(expr for ... in ...)``
|
||||
@@ -0,0 +1,20 @@
|
||||
.. _compilation-units:
|
||||
|
||||
Compilation units
|
||||
=================
|
||||
|
||||
When you run mypyc to compile a set of modules, these modules form a
|
||||
*compilation unit*. Mypyc will use early binding for references within
|
||||
the compilation unit.
|
||||
|
||||
If you run mypyc multiple times to compile multiple sets of modules,
|
||||
each invocation will result in a new compilation unit. References
|
||||
between separate compilation units will fall back to late binding,
|
||||
i.e. looking up names using Python namespace dictionaries. Also, all
|
||||
calls will use the slower Python calling convention, where arguments
|
||||
and the return value will be boxed (and potentially unboxed again in
|
||||
the called function).
|
||||
|
||||
For maximal performance, minimize interactions across compilation
|
||||
units. The simplest way to achieve this is to compile your entire
|
||||
program as a single compilation unit.
|
||||
59
venv/lib/python3.12/site-packages/mypyc/doc/conf.py
Normal file
59
venv/lib/python3.12/site-packages/mypyc/doc/conf.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
|
||||
from mypy.version import __version__ as mypy_version
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "mypyc"
|
||||
copyright = "2020-2022, mypyc team"
|
||||
author = "mypyc team"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = mypy_version.split("-")[0]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = mypy_version
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [] # type: ignore[var-annotated]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = "furo"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
@@ -0,0 +1,25 @@
|
||||
# Timings of CPython Operations
|
||||
|
||||
Here are some *very rough* approximate timings of CPython interpreter
|
||||
operations:
|
||||
|
||||
* `f(1)` (empty function body): 70-90ns
|
||||
* `f(n=1)` (empty function body): 90-110ns
|
||||
* `o.x`: 30-40ns
|
||||
* `o.f(1)` (empty method body): 80-160ns
|
||||
* `Cls(1)` (initialize attribute in `__init__`): 290-330ns
|
||||
* `x + y` (integers): 20-35ns
|
||||
* `a[i]` (list) : 20-40ns
|
||||
* `[i]` (also dealloc): 35-55ns
|
||||
* `a.append(i)` (list, average over 5 appends): 70ns
|
||||
* `d[s]` (dict, shared str key): 20ns
|
||||
* `d[s] = i` (dict, shared str key): 40ns
|
||||
* `isinstance(x, A)`: 100ns
|
||||
* `(x, y)`: 20-35ns
|
||||
* `x, y = t` (tuple expand): 10ns
|
||||
|
||||
Note that these results are very imprecise due to many factors, but
|
||||
these should give a rough idea of the relative costs of various
|
||||
operations.
|
||||
|
||||
Details: CPython 3.6.2, Macbook Pro 15" (Mid 2015), macOS Sierra
|
||||
548
venv/lib/python3.12/site-packages/mypyc/doc/dev-intro.md
Normal file
548
venv/lib/python3.12/site-packages/mypyc/doc/dev-intro.md
Normal file
@@ -0,0 +1,548 @@
|
||||
# Introduction for Mypyc Contributors
|
||||
|
||||
This is a short introduction aimed at anybody who is interested in
|
||||
contributing to mypyc, or anybody who is curious to understand how
|
||||
mypyc works internally.
|
||||
|
||||
## Key Differences from Python
|
||||
|
||||
Code compiled using mypyc is often much faster than CPython since it
|
||||
does these things differently:
|
||||
|
||||
* Mypyc generates C that is compiled to native code, instead of
|
||||
compiling to interpreted byte code, which CPython uses. Interpreted
|
||||
byte code always has some interpreter overhead, which slows things
|
||||
down.
|
||||
|
||||
* Mypyc doesn't let you arbitrarily monkey patch classes and functions
|
||||
in compiled modules. This allows *early binding* -- mypyc
|
||||
statically binds calls to compiled functions, instead of going
|
||||
through a namespace dictionary. Mypyc can also call methods of
|
||||
compiled classes using vtables, which are more efficient than
|
||||
dictionary lookups used by CPython.
|
||||
|
||||
* Mypyc compiles classes to C extension classes, which are generally
|
||||
more efficient than normal Python classes. They use an efficient,
|
||||
fixed memory representation (essentially a C struct). This lets us
|
||||
use direct memory access instead of (typically) two hash table
|
||||
lookups to access an attribute.
|
||||
|
||||
* As a result of early binding, compiled code can use C calls to call
|
||||
compiled functions. Keyword arguments can be translated to
|
||||
positional arguments during compilation. Thus most calls to native
|
||||
functions and methods directly map to simple C calls. CPython calls
|
||||
are quite expensive, since mapping of keyword arguments, `*args`,
|
||||
and so on has to mostly happen at runtime.
|
||||
|
||||
* Compiled code has runtime type checks to ensure that runtimes types
|
||||
match the declared static types. Compiled code can thus make
|
||||
assumptions about the types of expressions, resulting in both faster
|
||||
and smaller code, since many runtime type checks performed by the
|
||||
CPython interpreter can be omitted.
|
||||
|
||||
* Compiled code can often use unboxed (not heap allocated)
|
||||
representations for integers, booleans and tuples.
|
||||
|
||||
## Supported Python Features
|
||||
|
||||
Mypyc supports a large subset of Python. Note that if you try to
|
||||
compile something that is not supported, you may not always get a very
|
||||
good error message.
|
||||
|
||||
Here are some major things that aren't yet supported in compiled code:
|
||||
|
||||
* Many dunder methods (only some work, such as `__init__` and `__eq__`)
|
||||
* Monkey patching compiled functions or classes
|
||||
* General multiple inheritance (a limited form is supported)
|
||||
* Named tuple defined using the class-based syntax
|
||||
* Defining protocols
|
||||
|
||||
We are generally happy to accept contributions that implement new Python
|
||||
features.
|
||||
|
||||
## Development Environment
|
||||
|
||||
First you should set up the mypy development environment as described in
|
||||
the [mypy docs](https://github.com/python/mypy/blob/master/README.md).
|
||||
macOS, Linux and Windows are supported.
|
||||
|
||||
## Compiling and Running Programs
|
||||
|
||||
When working on a mypyc feature or a fix, you'll often need to run
|
||||
compiled code. For example, you may want to do interactive testing or
|
||||
to run benchmarks. This is also handy if you want to inspect the
|
||||
generated C code (see Inspecting Generated C).
|
||||
|
||||
Run `mypyc` to compile a module to a C extension using your
|
||||
development version of mypyc:
|
||||
|
||||
```
|
||||
$ mypyc program.py
|
||||
```
|
||||
|
||||
This will generate a C extension for `program` in the current working
|
||||
directory. For example, on a Linux system the generated file may be
|
||||
called `program.cpython-37m-x86_64-linux-gnu.so`.
|
||||
|
||||
Since C extensions can't be run as programs, use `python3 -c` to run
|
||||
the compiled module as a program:
|
||||
|
||||
```
|
||||
$ python3 -c "import program"
|
||||
```
|
||||
|
||||
Note that `__name__` in `program.py` will now be `program`, not
|
||||
`__main__`!
|
||||
|
||||
You can manually delete the C extension to get back to an interpreted
|
||||
version (this example works on Linux):
|
||||
|
||||
```
|
||||
$ rm program.*.so
|
||||
```
|
||||
|
||||
Another option is to invoke mypyc through tests (see Testing below).
|
||||
|
||||
## High-level Overview of Mypyc
|
||||
|
||||
Mypyc compiles a Python module (or a set of modules) to C, and
|
||||
compiles the generated C to a Python C extension module (or
|
||||
modules). You can compile only a subset of your program to C --
|
||||
compiled and interpreted code can freely and transparently
|
||||
interact. You can also freely use any Python libraries (including C
|
||||
extensions) in compiled code.
|
||||
|
||||
Mypyc will only make compiled code faster. To see a significant
|
||||
speedup, you must make sure that most of the time is spent in compiled
|
||||
code -- and not in libraries, for example.
|
||||
|
||||
Mypyc has these passes:
|
||||
|
||||
* Type check the code using mypy and infer types for variables and
|
||||
expressions. This produces a mypy AST (defined in `mypy.nodes`) and
|
||||
a type map that describes the inferred types (`mypy.types.Type`) of
|
||||
all expressions (as PEP 484 types).
|
||||
|
||||
* Translate the mypy AST into a mypyc-specific intermediate representation (IR).
|
||||
* The IR is defined in `mypyc.ir` (see below for an explanation of the IR).
|
||||
* Various primitive operations used in the IR are defined in `mypyc.primitives`.
|
||||
* The translation to IR happens in `mypyc.irbuild`. The top-level logic is in
|
||||
`mypyc.irbuild.main`.
|
||||
|
||||
* Insert checks for uses of potentially uninitialized variables
|
||||
(`mypyc.transform.uninit`).
|
||||
|
||||
* Insert exception handling (`mypyc.transform.exceptions`).
|
||||
|
||||
* Insert explicit reference count inc/dec opcodes (`mypyc.transform.refcount`).
|
||||
|
||||
* Translate the IR into C (`mypyc.codegen`).
|
||||
|
||||
* Compile the generated C code using a C compiler (`mypyc.build`).
|
||||
|
||||
## Useful Background Information
|
||||
|
||||
Beyond the mypy documentation, here are some things that are helpful to
|
||||
know for mypyc contributors:
|
||||
|
||||
* Experience with C
|
||||
([The C Programming Language](https://en.wikipedia.org/wiki/The_C_Programming_Language)
|
||||
is a classic book about C)
|
||||
* Basic familiarity with the Python C API (see
|
||||
[Python C API documentation](https://docs.python.org/3/c-api/intro.html)). [Extending and Embedding the Python Interpreter](https://docs.python.org/3/extending/index.html) is a good tutorial for beginners.
|
||||
* Basics of compilers (see the
|
||||
[mypy wiki](https://github.com/python/mypy/wiki/Learning-Resources)
|
||||
for some ideas)
|
||||
|
||||
## Mypyc Intermediate Representation (IR)
|
||||
|
||||
The mypyc IR is defined in `mypyc.ir`. It covers several key concepts
|
||||
that are essential to understand by all mypyc contributors:
|
||||
|
||||
* `mypyc.ir.ops.Op` is an Abstract Base Class for all IR
|
||||
operations. These are low-level and generally map to simple
|
||||
fragments of C each. Mypy expressions are translated to
|
||||
linear sequences of these ops.
|
||||
|
||||
* `mypyc.ir.ops.BasicBlock` is a container of a sequence of ops with a
|
||||
branch/goto/return at the end, and no branch/goto/return ops in the
|
||||
middle. Each function is compiled to a bunch of basic blocks.
|
||||
|
||||
* `mypyc.ir.rtypes.RType` and its subclasses are the types used for
|
||||
everything in the IR. These are lower-level and simpler than mypy or
|
||||
PEP 484 types. For example, there are no general-purpose generic
|
||||
types types here. Each `List[X]` type (for any `X`) is represented
|
||||
by a single `list` type, for example.
|
||||
|
||||
* Primitive types are special RTypes of which mypyc has some special
|
||||
understanding, and there are typically some specialized
|
||||
ops. Examples include `int` (referred to as `int_rprimitive` in the
|
||||
code) and `list` (`list_rprimitive`). Python types for which there
|
||||
is no specific RType type will be represented by the catch-all
|
||||
`object_rprimitive` type.
|
||||
|
||||
* Instances of compiled classes are generally represented using the
|
||||
`RInstance` type. Classes are compiled to C extension classes and
|
||||
contain vtables for fast method calls and fast attribute access.
|
||||
|
||||
* IR representations of functions and classes live in
|
||||
`mypyc.ir.func_ir` and `mypyc.ir.class_ir`, respectively.
|
||||
|
||||
Look at the docstrings and comments in `mypyc.ir` for additional
|
||||
information. See the test cases in
|
||||
`mypyc/test-data/irbuild-basic.test` for examples of what the IR looks
|
||||
like in a pretty-printed form.
|
||||
|
||||
## Testing overview
|
||||
|
||||
Most mypyc test cases are defined in the same format (`.test`) as used
|
||||
for test cases for mypy. Look at mypy developer documentation for a
|
||||
general overview of how things work. Test cases live under
|
||||
`mypyc/test-data/`, and you can run all mypyc tests via `pytest
|
||||
-q mypyc`. If you don't make changes to code under `mypy/`, it's not
|
||||
important to regularly run mypy tests during development.
|
||||
|
||||
When you create a PR, we have Continuous Integration jobs set up that
|
||||
compile mypy using mypyc and run the mypy test suite using the
|
||||
compiled mypy. This will sometimes catch additional issues not caught
|
||||
by the mypyc test suite. It's okay to not do this in your local
|
||||
development environment.
|
||||
|
||||
We discuss writing tests in more detail later in this document.
|
||||
|
||||
## Inspecting Generated IR
|
||||
|
||||
It's often useful to look at the generated IR when debugging issues or
|
||||
when trying to understand how mypyc compiles some code. When you
|
||||
compile some module by running `mypyc`, mypyc will write the
|
||||
pretty-printed IR into `build/ops.txt`. This is the final IR that
|
||||
includes the output from exception and reference count handling
|
||||
insertion passes.
|
||||
|
||||
We also have tests that verify the generate IR
|
||||
(`mypyc/test-data/irbuild-*.text`).
|
||||
|
||||
## Type-checking Mypyc
|
||||
|
||||
`./runtests.py self` type checks mypy and mypyc. This is pretty slow,
|
||||
however, since it's using an uncompiled mypy.
|
||||
|
||||
Installing a released version of mypy using `pip` (which is compiled)
|
||||
and using `dmypy` (mypy daemon) is a much, much faster way to type
|
||||
check mypyc during development.
|
||||
|
||||
## Value Representation
|
||||
|
||||
Mypyc uses a tagged pointer representation for values of type `int`
|
||||
(`CPyTagged`), `char` for booleans, and C structs for tuples. For most
|
||||
other objects mypyc uses the CPython `PyObject *`.
|
||||
|
||||
Python integers that fit in 31/63 bits (depending on whether we are on
|
||||
a 32-bit or 64-bit platform) are represented as C integers
|
||||
(`CPyTagged`) shifted left by 1. Integers that don't fit in this
|
||||
representation are represented as pointers to a `PyObject *` (this is
|
||||
always a Python `int` object) with the least significant bit
|
||||
set. Tagged integer operations are defined in `mypyc/lib-rt/int_ops.c`
|
||||
and `mypyc/lib-rt/CPy.h`.
|
||||
|
||||
There are also low-level integer types, such as `int32` (see
|
||||
`mypyc.ir.rtypes`), that don't use the tagged representation. These
|
||||
types are not exposed to users, but they are used in generated code.
|
||||
|
||||
## Overview of Generated C
|
||||
|
||||
Mypyc compiles a function into two functions, a native function and
|
||||
a wrapper function:
|
||||
|
||||
* The native function takes a fixed number of C arguments with the
|
||||
correct C types. It assumes that all argument have correct types.
|
||||
|
||||
* The wrapper function conforms to the Python C API calling convention
|
||||
and takes an arbitrary set of arguments. It processes the arguments,
|
||||
checks their types, unboxes values with special representations and
|
||||
calls the native function. The return value from the native function
|
||||
is translated back to a Python object ("boxing").
|
||||
|
||||
Calls to other compiled functions don't go through the Python module
|
||||
namespace but directly call the target native C function. This makes
|
||||
calls very fast compared to CPython.
|
||||
|
||||
The generated code does runtime checking so that it can assume that
|
||||
values always have the declared types. Whenever accessing CPython
|
||||
values which might have unexpected types we need to insert a runtime
|
||||
type check operation. For example, when getting a list item we need to
|
||||
insert a runtime type check (an unbox or a cast operation), since
|
||||
Python lists can contain arbitrary objects.
|
||||
|
||||
The generated code uses various helpers defined in
|
||||
`mypyc/lib-rt/CPy.h`. The implementations are in various `.c` files
|
||||
under `mypyc/lib-rt`.
|
||||
|
||||
## Inspecting Generated C
|
||||
|
||||
It's often useful to inspect the C code genenerate by mypyc to debug
|
||||
issues. Mypyc stores the generated C code as `build/__native.c`.
|
||||
Compiled native functions have the prefix `CPyDef_`, while wrapper
|
||||
functions used for calling functions from interpreted Python code have
|
||||
the `CPyPy_` prefix.
|
||||
|
||||
## Other Important Limitations
|
||||
|
||||
All of these limitations will likely be fixed in the future:
|
||||
|
||||
* We don't detect stack overflows.
|
||||
|
||||
* We don't handle Ctrl-C in compiled code.
|
||||
|
||||
## Hints for Implementing Typical Mypyc Features
|
||||
|
||||
This section gives an overview of where to look for and
|
||||
what to do to implement specific kinds of mypyc features.
|
||||
|
||||
### Testing
|
||||
|
||||
Our bread-and-butter testing strategy is compiling code with mypyc and
|
||||
running it. There are downsides to this (kind of slow, tests a huge
|
||||
number of components at once, insensitive to the particular details of
|
||||
the IR), but there really is no substitute for running code. You can
|
||||
also write tests that test the generated IR, however.
|
||||
|
||||
### Tests that compile and run code
|
||||
|
||||
Test cases that compile and run code are located in
|
||||
`mypyc/test-data/run*.test` and the test runner is in
|
||||
`mypyc.test.test_run`. The code to compile comes after `[case
|
||||
test<name>]`. The code gets saved into the file `native.py`, and it
|
||||
gets compiled into the module `native`.
|
||||
|
||||
Each test case uses a non-compiled Python driver that imports the
|
||||
`native` module and typically calls some compiled functions. Some
|
||||
tests also perform assertions and print messages in the driver.
|
||||
|
||||
If you don't provide a driver, a default driver is used. The default
|
||||
driver just calls each module-level function that is prefixed with
|
||||
`test_` and reports any uncaught exceptions as failures. (Failure to
|
||||
build or a segfault also count as failures.) `testStringOps` in
|
||||
`mypyc/test-data/run-strings.test` is an example of a test that uses
|
||||
the default driver.
|
||||
|
||||
You should usually use the default driver (don't include
|
||||
`driver.py`). It's the simplest way to write most tests.
|
||||
|
||||
Here's an example test case that uses the default driver:
|
||||
|
||||
```
|
||||
[case testConcatenateLists]
|
||||
def test_concat_lists() -> None:
|
||||
assert [1, 2] + [5, 6] == [1, 2, 5, 6]
|
||||
|
||||
def test_concat_empty_lists() -> None:
|
||||
assert [] + [] == []
|
||||
```
|
||||
|
||||
There is one test case, `testConcatenateLists`. It has two sub-cases,
|
||||
`test_concat_lists` and `test_concat_empty_lists`. Note that you can
|
||||
use the pytest -k argument to only run `testConcetanateLists`, but you
|
||||
can't filter tests at the sub-case level.
|
||||
|
||||
It's recommended to have multiple sub-cases per test case, since each
|
||||
test case has significant fixed overhead. Each test case is run in a
|
||||
fresh Python subprocess.
|
||||
|
||||
Many of the existing test cases provide a custom driver by having
|
||||
`[file driver.py]`, followed by the driver implementation. Here the
|
||||
driver is not compiled, which is useful if you want to test
|
||||
interactions between compiled and non-compiled code. However, many of
|
||||
the tests don't have a good reason to use a custom driver -- when they
|
||||
were written, the default driver wasn't available.
|
||||
|
||||
Test cases can also have a `[out]` section, which specifies the
|
||||
expected contents of stdout the test case should produce. New test
|
||||
cases should prefer assert statements to `[out]` sections.
|
||||
|
||||
### IR tests
|
||||
|
||||
If the specifics of the generated IR of a change is important
|
||||
(because, for example, you want to make sure a particular optimization
|
||||
is triggering), you should add a `mypyc.irbuild` test as well. Test
|
||||
cases are located in `mypyc/test-data/irbuild-*.test` and the test
|
||||
driver is in `mypyc.test.test_irbuild`. IR build tests do a direct
|
||||
comparison of the IR output, so try to make the test as targeted as
|
||||
possible so as to capture only the important details. (Many of our
|
||||
existing IR build tests do not follow this advice, unfortunately!)
|
||||
|
||||
If you pass the `--update-data` flag to pytest, it will automatically
|
||||
update the expected output of any tests to match the actual
|
||||
output. This is very useful for changing or creating IR build tests,
|
||||
but make sure to carefully inspect the diff!
|
||||
|
||||
You may also need to add some definitions to the stubs used for
|
||||
builtins during tests (`mypyc/test-data/fixtures/ir.py`). We don't use
|
||||
full typeshed stubs to run tests since they would seriously slow down
|
||||
tests.
|
||||
|
||||
### Benchmarking
|
||||
|
||||
Many mypyc improvements attempt to make some operations faster. For
|
||||
any such change, you should run some measurements to verify that
|
||||
there actually is a measurable performance impact.
|
||||
|
||||
A typical benchmark would initialize some data to be operated on, and
|
||||
then measure time spent in some function. In particular, you should
|
||||
not measure time needed to run the entire benchmark program, as this
|
||||
would include Python startup overhead and other things that aren't
|
||||
relevant. In general, for microbenchmarks, you want to do as little as
|
||||
possible in the timed portion. So ideally you'll just have some loops
|
||||
and the code under test. Be ready to provide your benchmark in code
|
||||
review so that mypyc developers can check that the benchmark is fine
|
||||
(writing a good benchmark is non-trivial).
|
||||
|
||||
You should run a benchmark at least five times, in both original and
|
||||
changed versions, ignore outliers, and report the average
|
||||
runtime. Actual performance of a typical desktop or laptop computer is
|
||||
quite variable, due to dynamic CPU clock frequency changes, background
|
||||
processes, etc. If you observe a high variance in timings, you'll need
|
||||
to run the benchmark more times. Also try closing most applications,
|
||||
including web browsers.
|
||||
|
||||
Interleave original and changed runs. Don't run 10 runs with variant A
|
||||
followed by 10 runs with variant B, but run an A run, a B run, an A
|
||||
run, etc. Otherwise you risk that the CPU frequency will be different
|
||||
between variants. You can also try adding a delay of 5 to 20s between
|
||||
runs to avoid CPU frequency changes.
|
||||
|
||||
Instead of averaging over many measurements, you can try to adjust
|
||||
your environment to provide more stable measurements. However, this
|
||||
can be hard to do with some hardware, including many laptops. Victor
|
||||
Stinner has written a series of blog posts about making measurements
|
||||
stable:
|
||||
|
||||
* https://vstinner.github.io/journey-to-stable-benchmark-system.html
|
||||
* https://vstinner.github.io/journey-to-stable-benchmark-average.html
|
||||
|
||||
### Adding C Helpers
|
||||
|
||||
If you add an operation that compiles into a lot of C code, you may
|
||||
also want to add a C helper function for the operation to make the
|
||||
generated code smaller. Here is how to do this:
|
||||
|
||||
* Declare the operation in `mypyc/lib-rt/CPy.h`. We avoid macros, and
|
||||
we generally avoid inline functions to make it easier to target
|
||||
additional backends in the future.
|
||||
|
||||
* Consider adding a unit test for your C helper in `mypyc/lib-rt/test_capi.cc`.
|
||||
We use
|
||||
[Google Test](https://github.com/google/googletest) for writing
|
||||
tests in C++. The framework is included in the repository under the
|
||||
directory `googletest/`. The C unit tests are run as part of the
|
||||
pytest test suite (`test_c_unit_test`).
|
||||
|
||||
### Adding a Specialized Primitive Operation
|
||||
|
||||
Mypyc speeds up operations on primitive types such as `list` and `int`
|
||||
by having primitive operations specialized for specific types. These
|
||||
operations are declared in `mypyc.primitives` (and
|
||||
`mypyc/lib-rt/CPy.h`). For example, `mypyc.primitives.list_ops`
|
||||
contains primitives that target list objects.
|
||||
|
||||
The operation definitions are data driven: you specify the kind of
|
||||
operation (such as a call to `builtins.len` or a binary addition) and
|
||||
the operand types (such as `list_primitive`), and what code should be
|
||||
generated for the operation. Mypyc does AST matching to find the most
|
||||
suitable primitive operation automatically.
|
||||
|
||||
Look at the existing primitive definitions and the docstrings in
|
||||
`mypyc.primitives.registry` for examples and more information.
|
||||
|
||||
### Adding a New Primitive Type
|
||||
|
||||
Some types (typically Python Python built-in types), such as `int` and
|
||||
`list`, are special cased in mypyc to generate optimized operations
|
||||
specific to these types. We'll occasionally want to add additional
|
||||
primitive types.
|
||||
|
||||
Here are some hints about how to add support for a new primitive type
|
||||
(this may be incomplete):
|
||||
|
||||
* Decide whether the primitive type has an "unboxed" representation (a
|
||||
representation that is not just `PyObject *`). For most types we'll
|
||||
use a boxed representation, as it's easier to implement and more
|
||||
closely matches Python semantics.
|
||||
|
||||
* Create a new instance of `RPrimitive` to support the primitive type
|
||||
and add it to `mypyc.ir.rtypes`. Make sure all the attributes are
|
||||
set correctly and also define `<foo>_rprimitive` and
|
||||
`is_<foo>_rprimitive`.
|
||||
|
||||
* Update `mypyc.irbuild.mapper.Mapper.type_to_rtype()`.
|
||||
|
||||
* If the type is not unboxed, update `emit_cast` in `mypyc.codegen.emit`.
|
||||
|
||||
If the type is unboxed, there are some additional steps:
|
||||
|
||||
* Update `emit_box` in `mypyc.codegen.emit`.
|
||||
|
||||
* Update `emit_unbox` in `mypyc.codegen.emit`.
|
||||
|
||||
* Update `emit_inc_ref` and `emit_dec_ref` in `mypypc.codegen.emit`.
|
||||
If the unboxed representation does not need reference counting,
|
||||
these can be no-ops.
|
||||
|
||||
* Update `emit_error_check` in `mypyc.codegen.emit`.
|
||||
|
||||
* Update `emit_gc_visit` and `emit_gc_clear` in `mypyc.codegen.emit`
|
||||
if the type has an unboxed representation with pointers.
|
||||
|
||||
The above may be enough to allow you to declare variables with the
|
||||
type, pass values around, perform runtime type checks, and use generic
|
||||
fallback primitive operations to perform method calls, binary
|
||||
operations, and so on. You likely also want to add some faster,
|
||||
specialized primitive operations for the type (see Adding a
|
||||
Specialized Primitive Operation above for how to do this).
|
||||
|
||||
Add a test case to `mypyc/test-data/run*.test` to test compilation and
|
||||
running compiled code. Ideas for things to test:
|
||||
|
||||
* Test using the type as an argument.
|
||||
|
||||
* Test using the type as a return value.
|
||||
|
||||
* Test passing a value of the type to a function both within
|
||||
compiled code and from regular Python code. Also test this
|
||||
for return values.
|
||||
|
||||
* Test using the type as list item type. Test both getting a list item
|
||||
and setting a list item.
|
||||
|
||||
### Supporting More Python Syntax
|
||||
|
||||
Mypyc supports most Python syntax, but there are still some gaps.
|
||||
|
||||
Support for syntactic sugar that doesn't need additional IR operations
|
||||
typically only requires changes to `mypyc.irbuild`.
|
||||
|
||||
Some new syntax also needs new IR primitives to be added to
|
||||
`mypyc.primitives`. See `mypyc.primitives.registry` for documentation
|
||||
about how to do this.
|
||||
|
||||
### Other Hints
|
||||
|
||||
* This developer documentation is not aimed to be very complete. Much
|
||||
of our documentation is in comments and docstring in the code. If
|
||||
something is unclear, study the code.
|
||||
|
||||
* It can be useful to look through some recent PRs to get an idea of
|
||||
what typical code changes, test cases, etc. look like.
|
||||
|
||||
* Feel free to open GitHub issues with questions if you need help when
|
||||
contributing, or ask questions in existing issues. Note that we only
|
||||
support contributors. Mypyc is not (yet) an end-user product. You
|
||||
can also ask questions in our Gitter chat
|
||||
(https://gitter.im/mypyc-dev/community).
|
||||
|
||||
## Undocumented Workflows
|
||||
|
||||
These workflows would be useful for mypyc contributors. We should add
|
||||
them to mypyc developer documentation:
|
||||
|
||||
* How to inspect the generated IR before some transform passes.
|
||||
@@ -0,0 +1,59 @@
|
||||
.. _dict-ops:
|
||||
|
||||
Native dict operations
|
||||
======================
|
||||
|
||||
These ``dict`` operations have fast, optimized implementations. Other
|
||||
dictionary operations use generic implementations that are often slower.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
Construct dict from keys and values:
|
||||
|
||||
* ``{key: value, ...}``
|
||||
|
||||
Construct empty dict:
|
||||
|
||||
* ``{}``
|
||||
* ``dict()``
|
||||
|
||||
Construct dict from another object:
|
||||
|
||||
* ``dict(d: dict)``
|
||||
* ``dict(x: Iterable)``
|
||||
|
||||
Dict comprehensions:
|
||||
|
||||
* ``{...: ... for ... in ...}``
|
||||
* ``{...: ... for ... in ... if ...}``
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* ``d[key]``
|
||||
* ``value in d``
|
||||
|
||||
Statements
|
||||
----------
|
||||
|
||||
* ``d[key] = value``
|
||||
* ``for key in d:``
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
* ``d.get(key)``
|
||||
* ``d.get(key, default)``
|
||||
* ``d.keys()``
|
||||
* ``d.values()``
|
||||
* ``d.items()``
|
||||
* ``d.copy()``
|
||||
* ``d.clear()``
|
||||
* ``d1.update(d2: dict)``
|
||||
* ``d.update(x: Iterable)``
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``len(d: dict)``
|
||||
@@ -0,0 +1,332 @@
|
||||
.. _differences-from-python:
|
||||
|
||||
Differences from Python
|
||||
=======================
|
||||
|
||||
Mypyc aims to be sufficiently compatible with Python semantics so that
|
||||
migrating code to mypyc often doesn't require major code
|
||||
changes. There are various differences to enable performance gains
|
||||
that you need to be aware of, however.
|
||||
|
||||
This section documents notable differences from Python. We discuss
|
||||
many of them also elsewhere, but it's convenient to have them here in
|
||||
one place.
|
||||
|
||||
Running compiled modules
|
||||
------------------------
|
||||
|
||||
You can't use ``python3 <module>.py`` or ``python3 -m <module>``
|
||||
to run compiled modules. Use ``python3 -c "import <module>"`` instead,
|
||||
or write a wrapper script that imports your module.
|
||||
|
||||
As a side effect, you can't rely on checking the ``__name__`` attribute in compiled
|
||||
code, like this::
|
||||
|
||||
if __name__ == "__main__": # Can't be used in compiled code
|
||||
main()
|
||||
|
||||
Type errors prevent compilation
|
||||
-------------------------------
|
||||
|
||||
You can't compile code that generates mypy type check errors. You can
|
||||
sometimes ignore these with a ``# type: ignore`` comment, but this can
|
||||
result in bad code being generated, and it's considered dangerous.
|
||||
|
||||
.. note::
|
||||
|
||||
In the future, mypyc may reject ``# type: ignore`` comments that
|
||||
may be unsafe.
|
||||
|
||||
Runtime type checking
|
||||
---------------------
|
||||
|
||||
Non-erased types in annotations will be type checked at runtime. For example,
|
||||
consider this function::
|
||||
|
||||
def twice(x: int) -> int:
|
||||
return x * 2
|
||||
|
||||
If you try to call this function with a ``float`` or ``str`` argument,
|
||||
you'll get a type error on the call site, even if the call site is not
|
||||
being type checked::
|
||||
|
||||
twice(5) # OK
|
||||
twice(2.2) # TypeError
|
||||
twice("blah") # TypeError
|
||||
|
||||
Also, values with *inferred* types will be type checked. For example,
|
||||
consider a call to the stdlib function ``socket.gethostname()`` in
|
||||
compiled code. This function is not compiled (no stdlib modules are
|
||||
compiled with mypyc), but mypyc uses a *library stub file* to infer
|
||||
the return type as ``str``. Compiled code calling ``gethostname()``
|
||||
will fail with ``TypeError`` if ``gethostname()`` would return an
|
||||
incompatible value, such as ``None``::
|
||||
|
||||
import socket
|
||||
|
||||
# Fail if returned value is not a str
|
||||
name = socket.gethostname()
|
||||
|
||||
Note that ``gethostname()`` is defined like this in the stub file for
|
||||
``socket`` (in typeshed)::
|
||||
|
||||
def gethostname() -> str: ...
|
||||
|
||||
Thus mypyc verifies that library stub files and annotations in
|
||||
non-compiled code match runtime values. This adds an extra layer of
|
||||
type safety.
|
||||
|
||||
Casts such as ``cast(str, x)`` will also result in strict type
|
||||
checks. Consider this example::
|
||||
|
||||
from typing import cast
|
||||
...
|
||||
x = cast(str, y)
|
||||
|
||||
The last line is essentially equivalent to this Python code when compiled::
|
||||
|
||||
if not isinstance(y, str):
|
||||
raise TypeError(...)
|
||||
x = y
|
||||
|
||||
In interpreted mode ``cast`` does not perform a runtime type check.
|
||||
|
||||
Native classes
|
||||
--------------
|
||||
|
||||
Native classes behave differently from Python classes. See
|
||||
:ref:`native-classes` for the details.
|
||||
|
||||
Primitive types
|
||||
---------------
|
||||
|
||||
Some primitive types behave differently in compiled code to improve
|
||||
performance.
|
||||
|
||||
``int`` objects use an unboxed (non-heap-allocated) representation for small
|
||||
integer values. A side effect of this is that the exact runtime type of
|
||||
``int`` values is lost. For example, consider this simple function::
|
||||
|
||||
def first_int(x: List[int]) -> int:
|
||||
return x[0]
|
||||
|
||||
print(first_int([True])) # Output is 1, instead of True!
|
||||
|
||||
``bool`` is a subclass of ``int``, so the above code is
|
||||
valid. However, when the list value is converted to ``int``, ``True``
|
||||
is converted to the corresponding ``int`` value, which is ``1``.
|
||||
|
||||
Note that integers still have an arbitrary precision in compiled code,
|
||||
similar to normal Python integers.
|
||||
|
||||
Fixed-length tuples are unboxed, similar to integers. The exact type
|
||||
and identity of fixed-length tuples is not preserved, and you can't
|
||||
reliably use ``is`` checks to compare tuples that are used in compiled
|
||||
code.
|
||||
|
||||
.. _early-binding:
|
||||
|
||||
Early binding
|
||||
-------------
|
||||
|
||||
References to functions, types, most attributes, and methods in the
|
||||
same :ref:`compilation unit <compilation-units>` use *early binding*:
|
||||
the target of the reference is decided at compile time, whenever
|
||||
possible. This contrasts with normal Python behavior of *late
|
||||
binding*, where the target is found by a namespace lookup at
|
||||
runtime. Omitting these namespace lookups improves performance, but
|
||||
some Python idioms don't work without changes.
|
||||
|
||||
Note that non-final module-level variables still use late binding.
|
||||
You may want to avoid these in very performance-critical code.
|
||||
|
||||
Examples of early and late binding::
|
||||
|
||||
from typing import Final
|
||||
|
||||
import lib # "lib" is not compiled
|
||||
|
||||
x = 0
|
||||
y: Final = 1
|
||||
|
||||
def func() -> None:
|
||||
pass
|
||||
|
||||
class Cls:
|
||||
def __init__(self, attr: int) -> None:
|
||||
self.attr = attr
|
||||
|
||||
def method(self) -> None:
|
||||
pass
|
||||
|
||||
def example() -> None:
|
||||
# Early binding:
|
||||
var = y
|
||||
func()
|
||||
o = Cls()
|
||||
o.x
|
||||
o.method()
|
||||
|
||||
# Late binding:
|
||||
var = x # Module-level variable
|
||||
lib.func() # Accessing library that is not compiled
|
||||
|
||||
Pickling and copying objects
|
||||
----------------------------
|
||||
|
||||
Mypyc tries to enforce that instances native classes are properly
|
||||
initialized by calling ``__init__`` implicitly when constructing
|
||||
objects, even if objects are constructed through ``pickle``,
|
||||
``copy.copy`` or ``copy.deepcopy``, for example.
|
||||
|
||||
If a native class doesn't support calling ``__init__`` without arguments,
|
||||
you can't pickle or copy instances of the class. Use the
|
||||
``mypy_extensions.mypyc_attr`` class decorator to override this behavior
|
||||
and enable pickling through the ``serializable`` flag::
|
||||
|
||||
from mypy_extensions import mypyc_attr
|
||||
import pickle
|
||||
|
||||
@mypyc_attr(serializable=True)
|
||||
class Cls:
|
||||
def __init__(self, n: int) -> None:
|
||||
self.n = n
|
||||
|
||||
data = pickle.dumps(Cls(5))
|
||||
obj = pickle.loads(data) # OK
|
||||
|
||||
Additional notes:
|
||||
|
||||
* All subclasses inherit the ``serializable`` flag.
|
||||
* If a class has the ``allow_interpreted_subclasses`` attribute, it
|
||||
implicitly supports serialization.
|
||||
* Enabling serialization may slow down attribute access, since compiled
|
||||
code has to be always prepared to raise ``AttributeError`` in case an
|
||||
attribute is not defined at runtime.
|
||||
* If you try to pickle an object without setting the ``serializable``
|
||||
flag, you'll get a ``TypeError`` about missing arguments to
|
||||
``__init__``.
|
||||
|
||||
|
||||
Monkey patching
|
||||
---------------
|
||||
|
||||
Since mypyc function and class definitions are immutable, you can't
|
||||
perform arbitrary monkey patching, such as replacing functions or
|
||||
methods with mocks in tests.
|
||||
|
||||
.. note::
|
||||
|
||||
Each compiled module has a Python namespace that is initialized to
|
||||
point to compiled functions and type objects. This namespace is a
|
||||
regular ``dict`` object, and it *can* be modified. However,
|
||||
compiled code generally doesn't use this namespace, so any changes
|
||||
will only be visible to non-compiled code.
|
||||
|
||||
Stack overflows
|
||||
---------------
|
||||
|
||||
Compiled code currently doesn't check for stack overflows. Your
|
||||
program may crash in an unrecoverable fashion if you have too many
|
||||
nested function calls, typically due to out-of-control recursion.
|
||||
|
||||
.. note::
|
||||
|
||||
This limitation will be fixed in the future.
|
||||
|
||||
Final values
|
||||
------------
|
||||
|
||||
Compiled code replaces a reference to an attribute declared ``Final`` with
|
||||
the value of the attribute computed at compile time. This is an example of
|
||||
:ref:`early binding <early-binding>`. Example::
|
||||
|
||||
MAX: Final = 100
|
||||
|
||||
def limit_to_max(x: int) -> int:
|
||||
if x > MAX:
|
||||
return MAX
|
||||
return x
|
||||
|
||||
The two references to ``MAX`` don't involve any module namespace lookups,
|
||||
and are equivalent to this code::
|
||||
|
||||
def limit_to_max(x: int) -> int:
|
||||
if x > 100:
|
||||
return 100
|
||||
return x
|
||||
|
||||
When run as interpreted, the first example will execute slower due to
|
||||
the extra namespace lookups. In interpreted code final attributes can
|
||||
also be modified.
|
||||
|
||||
Unsupported features
|
||||
--------------------
|
||||
|
||||
Some Python features are not supported by mypyc (yet). They can't be
|
||||
used in compiled code, or there are some limitations. You can
|
||||
partially work around some of these limitations by running your code
|
||||
in interpreted mode.
|
||||
|
||||
Nested classes
|
||||
**************
|
||||
|
||||
Nested classes are not supported.
|
||||
|
||||
Conditional functions or classes
|
||||
********************************
|
||||
|
||||
Function and class definitions guarded by an if-statement are not supported.
|
||||
|
||||
Dunder methods
|
||||
**************
|
||||
|
||||
Native classes **cannot** use these dunders. If defined, they will not
|
||||
work as expected.
|
||||
|
||||
* ``__del__``
|
||||
* ``__index__``
|
||||
* ``__getattr__``, ``__getattribute__``
|
||||
* ``__setattr__``
|
||||
* ``__delattr__``
|
||||
|
||||
Generator expressions
|
||||
*********************
|
||||
|
||||
Generator expressions are not supported. To make it easier to compile
|
||||
existing code, they are implicitly replaced with list comprehensions.
|
||||
*This does not always produce the same behavior.*
|
||||
|
||||
To work around this limitation, you can usually use a generator
|
||||
function instead. You can sometimes replace the generator expression
|
||||
with an explicit list comprehension.
|
||||
|
||||
Descriptors
|
||||
***********
|
||||
|
||||
Native classes can't contain arbitrary descriptors. Properties, static
|
||||
methods and class methods are supported.
|
||||
|
||||
Introspection
|
||||
*************
|
||||
|
||||
Various methods of introspection may break by using mypyc. Here's an
|
||||
non-exhaustive list of what won't work:
|
||||
|
||||
- Instance ``__annotations__`` is usually not kept
|
||||
- Frames of compiled functions can't be inspected using ``inspect``
|
||||
- Compiled methods aren't considered methods by ``inspect.ismethod``
|
||||
- ``inspect.signature`` chokes on compiled functions
|
||||
|
||||
Profiling hooks and tracing
|
||||
***************************
|
||||
|
||||
Compiled functions don't trigger profiling and tracing hooks, such as
|
||||
when using the ``profile``, ``cProfile``, or ``trace`` modules.
|
||||
|
||||
Debuggers
|
||||
*********
|
||||
|
||||
You can't set breakpoints in compiled functions or step through
|
||||
compiled functions using ``pdb``. Often you can debug your code in
|
||||
interpreted mode instead.
|
||||
@@ -0,0 +1,50 @@
|
||||
.. _float-ops:
|
||||
|
||||
Native float operations
|
||||
========================
|
||||
|
||||
These ``float`` operations have fast, optimized implementations. Other
|
||||
floating point operations use generic implementations that are often
|
||||
slower.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
* Float literal
|
||||
* ``float(x: int)``
|
||||
* ``float(x: i64)``
|
||||
* ``float(x: i32)``
|
||||
* ``float(x: i16)``
|
||||
* ``float(x: u8)``
|
||||
* ``float(x: str)``
|
||||
* ``float(x: float)`` (no-op)
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* Arithmetic (``+``, ``-``, ``*``, ``/``, ``//``, ``%``)
|
||||
* Comparisons (``==``, ``!=``, ``<``, etc.)
|
||||
* Augmented assignment (``x += y``, etc.)
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``int(f)``
|
||||
* ``i64(f)`` (convert to 64-bit signed integer)
|
||||
* ``i32(f)`` (convert to 32-bit signed integer)
|
||||
* ``i16(f)`` (convert to 16-bit signed integer)
|
||||
* ``u8(f)`` (convert to 8-bit unsigned integer)
|
||||
* ``abs(f)``
|
||||
* ``math.sin(f)``
|
||||
* ``math.cos(f)``
|
||||
* ``math.tan(f)``
|
||||
* ``math.sqrt(f)``
|
||||
* ``math.exp(f)``
|
||||
* ``math.log(f)``
|
||||
* ``math.floor(f)``
|
||||
* ``math.ceil(f)``
|
||||
* ``math.fabs(f)``
|
||||
* ``math.pow(x, y)``
|
||||
* ``math.copysign(x, y)``
|
||||
* ``math.isinf(f)``
|
||||
* ``math.isnan(f)``
|
||||
42
venv/lib/python3.12/site-packages/mypyc/doc/future.md
Normal file
42
venv/lib/python3.12/site-packages/mypyc/doc/future.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Future
|
||||
|
||||
This document introduces some ideas for future improvements.
|
||||
|
||||
## Basic Optimizations
|
||||
|
||||
Implement basic optimizations such as common subexpression elimination and
|
||||
loop invariant code motion.
|
||||
|
||||
Importantly, common subexpression elimination could be used to avoid
|
||||
redundant type checks.
|
||||
|
||||
## Operation-specific Optimizations
|
||||
|
||||
Some operations or combinations of successive operations can be
|
||||
replaced with more efficient operations. Examples:
|
||||
|
||||
* If `s` is a string, `s[i] == 'x'` doesn't need to construct the
|
||||
intermediate single-character string object `s[i]` but just compare
|
||||
the character value to `ord('x')`.
|
||||
|
||||
* `a + ':' + b` (two string concetenations) can be implemented as
|
||||
single three-operand concatenation that doesn't construct an
|
||||
intermediate object.
|
||||
|
||||
* `x in {1, 3}` can be translated into `x == 1 or x == 3` (more
|
||||
generally we need to evaluate all right-hand-side items).
|
||||
|
||||
## Integer Range Analysis
|
||||
|
||||
Implement integer range analysis. This can be used in various ways:
|
||||
|
||||
* Use untagged representations for some registers.
|
||||
* Use faster integer arithmetic operations for operations that
|
||||
only deal with short integers or that can't overflow.
|
||||
* Remove redundant list and string index checks.
|
||||
|
||||
## Always Defined Attributes
|
||||
|
||||
Somehow make it possible to enforce that attributes in a class are always
|
||||
defined. This makes attribute access faster since we don't need to explicitly
|
||||
check if the attribute is defined.
|
||||
240
venv/lib/python3.12/site-packages/mypyc/doc/getting_started.rst
Normal file
240
venv/lib/python3.12/site-packages/mypyc/doc/getting_started.rst
Normal file
@@ -0,0 +1,240 @@
|
||||
Getting started
|
||||
===============
|
||||
|
||||
Here you will learn some basic things you need to know to get started with mypyc.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
You need a Python C extension development environment. The way to set this up
|
||||
depends on your operating system.
|
||||
|
||||
macOS
|
||||
*****
|
||||
|
||||
Install Xcode command line tools:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ xcode-select --install
|
||||
|
||||
Linux
|
||||
*****
|
||||
|
||||
You need a C compiler and CPython headers and libraries. The specifics
|
||||
of how to install these varies by distribution. Here are instructions for
|
||||
Ubuntu 18.04, for example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ sudo apt install python3-dev
|
||||
|
||||
Windows
|
||||
*******
|
||||
|
||||
From `Build Tools for Visual Studio 2022 <https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2022>`_, install MSVC C++ build tools for your architecture and a Windows SDK. (latest versions recommended)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Mypyc is shipped as part of the mypy distribution. Install mypy like
|
||||
this (you need Python 3.5 or later):
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ python3 -m pip install -U mypy
|
||||
|
||||
On some systems you need to use this instead:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ python -m pip install -U mypy
|
||||
|
||||
Example program
|
||||
---------------
|
||||
|
||||
Let's start with a classic micro-benchmark, recursive fibonacci. Save
|
||||
this file as ``fib.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
|
||||
def fib(n: int) -> int:
|
||||
if n <= 1:
|
||||
return n
|
||||
else:
|
||||
return fib(n - 2) + fib(n - 1)
|
||||
|
||||
t0 = time.time()
|
||||
fib(32)
|
||||
print(time.time() - t0)
|
||||
|
||||
Note that we gave the ``fib`` function a type annotation. Without it,
|
||||
performance won't be as impressive after compilation.
|
||||
|
||||
.. note::
|
||||
|
||||
`Mypy documentation
|
||||
<https://mypy.readthedocs.io/en/stable/index.html>`_ is a good
|
||||
introduction if you are new to type annotations or mypy. Mypyc uses
|
||||
mypy to perform type checking and type inference, so some familiarity
|
||||
with mypy is very useful.
|
||||
|
||||
Compiling and running
|
||||
---------------------
|
||||
|
||||
We can run ``fib.py`` as a regular, interpreted program using CPython:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python3 fib.py
|
||||
0.4125328063964844
|
||||
|
||||
It took about 0.41s to run on my computer.
|
||||
|
||||
Run ``mypyc`` to compile the program to a binary C extension:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mypyc fib.py
|
||||
|
||||
This will generate a C extension for ``fib`` in the current working
|
||||
directory. For example, on a Linux system the generated file may be
|
||||
called ``fib.cpython-37m-x86_64-linux-gnu.so``.
|
||||
|
||||
Since C extensions can't be run as programs, use ``python3 -c`` to run
|
||||
the compiled module as a program:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python3 -c "import fib"
|
||||
0.04097270965576172
|
||||
|
||||
After compilation, the program is about 10x faster. Nice!
|
||||
|
||||
.. note::
|
||||
|
||||
``__name__`` in ``fib.py`` would now be ``"fib"``, not ``"__main__"``.
|
||||
|
||||
You can also pass most
|
||||
`mypy command line options <https://mypy.readthedocs.io/en/stable/command_line.html>`_
|
||||
to ``mypyc``.
|
||||
|
||||
Deleting compiled binary
|
||||
------------------------
|
||||
|
||||
You can manually delete the C extension to get back to an interpreted
|
||||
version (this example works on Linux):
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ rm fib.*.so
|
||||
|
||||
Using setup.py
|
||||
--------------
|
||||
|
||||
You can also use ``setup.py`` to compile modules using mypyc. Here is an
|
||||
example ``setup.py`` file::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
from mypyc.build import mypycify
|
||||
|
||||
setup(
|
||||
name='mylib',
|
||||
packages=['mylib'],
|
||||
ext_modules=mypycify([
|
||||
'mylib/__init__.py',
|
||||
'mylib/mod.py',
|
||||
]),
|
||||
)
|
||||
|
||||
We used ``mypycify(...)`` to specify which files to compile using
|
||||
mypyc. Your ``setup.py`` can include additional Python files outside
|
||||
``mypycify(...)`` that won't be compiled.
|
||||
|
||||
Now you can build a wheel (.whl) file for the package::
|
||||
|
||||
python3 setup.py bdist_wheel
|
||||
|
||||
The wheel is created under ``dist/``.
|
||||
|
||||
You can also compile the C extensions in-place, in the current directory (similar
|
||||
to using ``mypyc`` to compile modules)::
|
||||
|
||||
python3 setup.py build_ext --inplace
|
||||
|
||||
You can include most `mypy command line options
|
||||
<https://mypy.readthedocs.io/en/stable/command_line.html>`_ in the
|
||||
list of arguments passed to ``mypycify()``. For example, here we use
|
||||
the ``--disallow-untyped-defs`` flag to require that all functions
|
||||
have type annotations::
|
||||
|
||||
...
|
||||
setup(
|
||||
name='frobnicate',
|
||||
packages=['frobnicate'],
|
||||
ext_modules=mypycify([
|
||||
'--disallow-untyped-defs', # Pass a mypy flag
|
||||
'frobnicate.py',
|
||||
]),
|
||||
)
|
||||
|
||||
.. note:
|
||||
|
||||
You may be tempted to use `--check-untyped-defs
|
||||
<https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-check-untyped-defs>`_
|
||||
to type check functions without type annotations. Note that this
|
||||
may reduce performance, due to many transitions between type-checked and unchecked
|
||||
code.
|
||||
|
||||
Recommended workflow
|
||||
--------------------
|
||||
|
||||
A simple way to use mypyc is to always compile your code after any
|
||||
code changes, but this can get tedious, especially if you have a lot
|
||||
of code. Instead, you can do most development in interpreted mode.
|
||||
This development workflow has worked smoothly for developing mypy and
|
||||
mypyc (often we forget that we aren't working on a vanilla Python
|
||||
project):
|
||||
|
||||
1. During development, use interpreted mode. This gives you a fast
|
||||
edit-run cycle.
|
||||
|
||||
2. Use type annotations liberally and use mypy to type check your code
|
||||
during development. Mypy and tests can find most errors that would
|
||||
break your compiled code, if you have good type annotation
|
||||
coverage. (Running mypy is pretty quick.)
|
||||
|
||||
3. After you've implemented a feature or a fix, compile your project
|
||||
and run tests again, now in compiled mode. Usually nothing will
|
||||
break here, assuming your type annotation coverage is good. This
|
||||
can happen locally or in a Continuous Integration (CI) job. If you
|
||||
have CI, compiling locally may be rarely needed.
|
||||
|
||||
4. Release or deploy a compiled version. Optionally, include a
|
||||
fallback interpreted version for platforms that mypyc doesn't
|
||||
support.
|
||||
|
||||
This mypyc workflow only involves minor tweaks to a typical Python
|
||||
workflow. Most of development, testing and debugging happens in
|
||||
interpreted mode. Incremental mypy runs, especially when using the
|
||||
mypy daemon, are very quick (often a few hundred milliseconds).
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
You can sometimes get good results by just annotating your code and
|
||||
compiling it. If this isn't providing meaningful performance gains, if
|
||||
you have trouble getting your code to work under mypyc, or if you want
|
||||
to optimize your code for maximum performance, you should read the
|
||||
rest of the documentation in some detail.
|
||||
|
||||
Here are some specific recommendations, or you can just read the
|
||||
documentation in order:
|
||||
|
||||
* :ref:`using-type-annotations`
|
||||
* :ref:`native-classes`
|
||||
* :ref:`differences-from-python`
|
||||
* :ref:`performance-tips`
|
||||
61
venv/lib/python3.12/site-packages/mypyc/doc/index.rst
Normal file
61
venv/lib/python3.12/site-packages/mypyc/doc/index.rst
Normal file
@@ -0,0 +1,61 @@
|
||||
.. mypyc documentation master file, created by
|
||||
sphinx-quickstart on Sun Apr 5 14:01:55 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to mypyc documentation!
|
||||
===============================
|
||||
|
||||
Mypyc compiles Python modules to C extensions. It uses standard Python
|
||||
`type hints
|
||||
<https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html>`_ to
|
||||
generate fast code.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: First steps
|
||||
|
||||
introduction
|
||||
getting_started
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Using mypyc
|
||||
|
||||
using_type_annotations
|
||||
native_classes
|
||||
differences_from_python
|
||||
compilation_units
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Native operations reference
|
||||
|
||||
native_operations
|
||||
int_operations
|
||||
bool_operations
|
||||
float_operations
|
||||
str_operations
|
||||
list_operations
|
||||
dict_operations
|
||||
set_operations
|
||||
tuple_operations
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Advanced topics
|
||||
|
||||
performance_tips_and_tricks
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Project Links
|
||||
|
||||
GitHub <https://github.com/python/mypy>
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
162
venv/lib/python3.12/site-packages/mypyc/doc/int_operations.rst
Normal file
162
venv/lib/python3.12/site-packages/mypyc/doc/int_operations.rst
Normal file
@@ -0,0 +1,162 @@
|
||||
.. _int-ops:
|
||||
|
||||
Native integer operations
|
||||
=========================
|
||||
|
||||
Mypyc supports these integer types:
|
||||
|
||||
* ``int`` (arbitrary-precision integer)
|
||||
* ``i64`` (64-bit signed integer)
|
||||
* ``i32`` (32-bit signed integer)
|
||||
* ``i16`` (16-bit signed integer)
|
||||
* ``u8`` (8-bit unsigned integer)
|
||||
|
||||
``i64``, ``i32``, ``i16`` and ``u8`` are *native integer types* and
|
||||
are available in the ``mypy_extensions`` module. ``int`` corresponds
|
||||
to the Python ``int`` type, but uses a more efficient runtime
|
||||
representation (tagged pointer). Native integer types are value types.
|
||||
|
||||
All integer types have optimized primitive operations, but the native
|
||||
integer types are more efficient than ``int``, since they don't
|
||||
require range or bounds checks.
|
||||
|
||||
Operations on integers that are listed here have fast, optimized
|
||||
implementations. Other integer operations use generic implementations
|
||||
that are generally slower. Some operations involving integers and other
|
||||
types, such as list indexing, are documented elsewhere.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
``int`` type:
|
||||
|
||||
* Integer literal
|
||||
* ``int(x: float)``
|
||||
* ``int(x: i64)``
|
||||
* ``int(x: i32)``
|
||||
* ``int(x: i16)``
|
||||
* ``int(x: u8)``
|
||||
* ``int(x: str)``
|
||||
* ``int(x: str, base: int)``
|
||||
* ``int(x: int)`` (no-op)
|
||||
|
||||
``i64`` type:
|
||||
|
||||
* ``i64(x: int)``
|
||||
* ``i64(x: float)``
|
||||
* ``i64(x: i64)`` (no-op)
|
||||
* ``i64(x: i32)``
|
||||
* ``i64(x: i16)``
|
||||
* ``i64(x: u8)``
|
||||
* ``i64(x: str)``
|
||||
* ``i64(x: str, base: int)``
|
||||
|
||||
``i32`` type:
|
||||
|
||||
* ``i32(x: int)``
|
||||
* ``i32(x: float)``
|
||||
* ``i32(x: i64)`` (truncate)
|
||||
* ``i32(x: i32)`` (no-op)
|
||||
* ``i32(x: i16)``
|
||||
* ``i32(x: u8)``
|
||||
* ``i32(x: str)``
|
||||
* ``i32(x: str, base: int)``
|
||||
|
||||
``i16`` type:
|
||||
|
||||
* ``i16(x: int)``
|
||||
* ``i16(x: float)``
|
||||
* ``i16(x: i64)`` (truncate)
|
||||
* ``i16(x: i32)`` (truncate)
|
||||
* ``i16(x: i16)`` (no-op)
|
||||
* ``i16(x: u8)``
|
||||
* ``i16(x: str)``
|
||||
* ``i16(x: str, base: int)``
|
||||
|
||||
Conversions from ``int`` to a native integer type raise
|
||||
``OverflowError`` if the value is too large or small. Conversions from
|
||||
a wider native integer type to a narrower one truncate the value and never
|
||||
fail. More generally, operations between native integer types don't
|
||||
check for overflow.
|
||||
|
||||
Implicit conversions
|
||||
--------------------
|
||||
|
||||
``int`` values can be implicitly converted to a native integer type,
|
||||
for convenience. This means that these are equivalent::
|
||||
|
||||
from mypy_extensions import i64
|
||||
|
||||
def implicit() -> None:
|
||||
# Implicit conversion of 0 (int) to i64
|
||||
x: i64 = 0
|
||||
|
||||
def explicit() -> None:
|
||||
# Explicit conversion of 0 (int) to i64
|
||||
x = i64(0)
|
||||
|
||||
Similarly, a native integer value can be implicitly converted to an
|
||||
arbitrary-precision integer. These two functions are equivalent::
|
||||
|
||||
def implicit(x: i64) -> int:
|
||||
# Implicit conversion from i64 to int
|
||||
return x
|
||||
|
||||
def explicit(x: i64) -> int:
|
||||
# Explicit conversion from i64 to int
|
||||
return int(x)
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* Arithmetic (``+``, ``-``, ``*``, ``//``, ``/``, ``%``)
|
||||
* Bitwise operations (``&``, ``|``, ``^``, ``<<``, ``>>``, ``~``)
|
||||
* Comparisons (``==``, ``!=``, ``<``, etc.)
|
||||
* Augmented assignment (``x += y``, etc.)
|
||||
|
||||
If one of the above native integer operations overflows or underflows
|
||||
with signed operands, the behavior is undefined. Signed native integer
|
||||
types should only be used if all possible values are small enough for
|
||||
the type. For this reason, the arbitrary-precision ``int`` type is
|
||||
recommended for signed values unless the performance of integer
|
||||
operations is critical.
|
||||
|
||||
Operations on unsigned integers (``u8``) wrap around on overflow.
|
||||
|
||||
It's a compile-time error to mix different native integer types in a
|
||||
binary operation such as addition. An explicit conversion is required::
|
||||
|
||||
from mypy_extensions import i64, i32
|
||||
|
||||
def add(x: i64, y: i32) -> None:
|
||||
a = x + y # Error (i64 + i32)
|
||||
b = x + i64(y) # OK
|
||||
|
||||
You can freely mix a native integer value and an arbitrary-precision
|
||||
``int`` value in an operation. The native integer type is "sticky"
|
||||
and the ``int`` operand is coerced to the native integer type::
|
||||
|
||||
def example(x: i64, y: int) -> None:
|
||||
a = x * y
|
||||
# Type of "a" is "i64"
|
||||
...
|
||||
b = 1 - x
|
||||
# Similarly, type of "b" is "i64"
|
||||
|
||||
Statements
|
||||
----------
|
||||
|
||||
For loop over a range is compiled efficiently, if the ``range(...)`` object
|
||||
is constructed in the for statement (after ``in``):
|
||||
|
||||
* ``for x in range(end)``
|
||||
* ``for x in range(start, end)``
|
||||
* ``for x in range(start, end, step)``
|
||||
|
||||
If one of the arguments to ``range`` in a for loop is a native integer
|
||||
type, the type of the loop variable is inferred to have this native
|
||||
integer type, instead of ``int``::
|
||||
|
||||
for x in range(i64(n)):
|
||||
# Type of "x" is "i64"
|
||||
...
|
||||
150
venv/lib/python3.12/site-packages/mypyc/doc/introduction.rst
Normal file
150
venv/lib/python3.12/site-packages/mypyc/doc/introduction.rst
Normal file
@@ -0,0 +1,150 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Mypyc compiles Python modules to C extensions. It uses standard Python
|
||||
`type hints
|
||||
<https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html>`_ to
|
||||
generate fast code.
|
||||
|
||||
The compiled language is a strict, *gradually typed* Python variant. It
|
||||
restricts the use of some dynamic Python features to gain performance,
|
||||
but it's mostly compatible with standard Python.
|
||||
|
||||
Mypyc uses `mypy <https://www.mypy-lang.org/>`_ to perform type
|
||||
checking and type inference. Most type system features in the stdlib
|
||||
`typing <https://docs.python.org/3/library/typing.html>`_ module are
|
||||
supported.
|
||||
|
||||
Compiled modules can import arbitrary Python modules and third-party
|
||||
libraries. You can compile anything from a single performance-critical
|
||||
module to your entire codebase. You can run the modules you compile
|
||||
also as normal, interpreted Python modules.
|
||||
|
||||
Existing code with type annotations is often **1.5x to 5x** faster
|
||||
when compiled. Code tuned for mypyc can be **5x to 10x** faster.
|
||||
|
||||
Mypyc currently aims to speed up non-numeric code, such as server
|
||||
applications. Mypyc is also used to compile itself (and mypy).
|
||||
|
||||
Why mypyc?
|
||||
----------
|
||||
|
||||
**Easy to get started.** Compiled code has the look and feel of
|
||||
regular Python code. Mypyc supports familiar Python syntax and idioms.
|
||||
|
||||
**Expressive types.** Mypyc fully supports standard Python type hints.
|
||||
Mypyc has local type inference, generics, optional types, tuple types,
|
||||
union types, and more. Type hints act as machine-checked
|
||||
documentation, making code not only faster but also easier to
|
||||
understand and modify.
|
||||
|
||||
**Python ecosystem.** Mypyc runs on top of CPython, the
|
||||
standard Python implementation. You can use any third-party libraries,
|
||||
including C extensions, installed with pip. Mypyc uses only valid Python
|
||||
syntax, so all Python editors and IDEs work perfectly.
|
||||
|
||||
**Fast program startup.** Mypyc uses ahead-of-time compilation, so
|
||||
compilation does not slow down program startup. Slow program startup
|
||||
is a common issue with JIT compilers.
|
||||
|
||||
**Migration path for existing code.** Existing Python code often
|
||||
requires only minor changes to compile using mypyc.
|
||||
|
||||
**Waiting for compilation is optional.** Compiled code also runs as
|
||||
normal Python code. You can use interpreted Python during development,
|
||||
with familiar and fast workflows.
|
||||
|
||||
**Runtime type safety.** Mypyc protects you from segfaults and memory
|
||||
corruption. Any unexpected runtime type safety violation is a bug in
|
||||
mypyc. Runtime values are checked against type annotations. (Without
|
||||
mypyc, type annotations are ignored at runtime.)
|
||||
|
||||
**Find errors statically.** Mypyc uses mypy for static type checking
|
||||
that helps catch many bugs.
|
||||
|
||||
Use cases
|
||||
---------
|
||||
|
||||
**Fix only performance bottlenecks.** Often most time is spent in a few
|
||||
Python modules or functions. Add type annotations and compile these
|
||||
modules for easy performance gains.
|
||||
|
||||
**Compile it all.** During development you can use interpreted mode,
|
||||
for a quick edit-run cycle. In releases all non-test code is compiled.
|
||||
This is how mypy achieved a 4x performance improvement over interpreted
|
||||
Python.
|
||||
|
||||
**Take advantage of existing type hints.** If you already use type
|
||||
annotations in your code, adopting mypyc will be easier. You've already
|
||||
done most of the work needed to use mypyc.
|
||||
|
||||
**Alternative to a lower-level language.** Instead of writing
|
||||
performance-critical code in C, C++, Cython or Rust, you may get good
|
||||
performance while staying in the comfort of Python.
|
||||
|
||||
**Migrate C extensions.** Maintaining C extensions is not always fun
|
||||
for a Python developer. With mypyc you may get performance similar to
|
||||
the original C, with the convenience of Python.
|
||||
|
||||
Differences from Cython
|
||||
-----------------------
|
||||
|
||||
Mypyc targets many similar use cases as Cython. Mypyc does many things
|
||||
differently, however:
|
||||
|
||||
* No need to use non-standard syntax, such as ``cpdef``, or extra
|
||||
decorators to get good performance. Clean, normal-looking
|
||||
type-annotated Python code can be fast without language extensions.
|
||||
This makes it practical to compile entire codebases without a
|
||||
developer productivity hit.
|
||||
|
||||
* Mypyc has first-class support for features in the ``typing`` module,
|
||||
such as tuple types, union types and generics.
|
||||
|
||||
* Mypyc has powerful type inference, provided by mypy. Variable type
|
||||
annotations are not needed for optimal performance.
|
||||
|
||||
* Mypyc fully integrates with mypy for robust and seamless static type
|
||||
checking.
|
||||
|
||||
* Mypyc performs strict enforcement of type annotations at runtime,
|
||||
resulting in better runtime type safety and easier debugging.
|
||||
|
||||
Unlike Cython, mypyc doesn't directly support interfacing with C libraries
|
||||
or speeding up numeric code.
|
||||
|
||||
How does it work
|
||||
----------------
|
||||
|
||||
Mypyc uses several techniques to produce fast code:
|
||||
|
||||
* Mypyc uses *ahead-of-time compilation* to native code. This removes
|
||||
CPython interpreter overhead.
|
||||
|
||||
* Mypyc enforces type annotations (and type comments) at runtime,
|
||||
raising ``TypeError`` if runtime values don't match annotations.
|
||||
Value types only need to be checked in the boundaries between
|
||||
dynamic and static typing.
|
||||
|
||||
* Compiled code uses optimized, type-specific primitives.
|
||||
|
||||
* Mypyc uses *early binding* to resolve called functions and name
|
||||
references at compile time. Mypyc avoids many dynamic namespace
|
||||
lookups.
|
||||
|
||||
* Classes are compiled to *C extension classes*. They use `vtables
|
||||
<https://en.wikipedia.org/wiki/Virtual_method_table>`_ for fast
|
||||
method calls and attribute access.
|
||||
|
||||
* Mypyc treats compiled functions, classes, and attributes declared
|
||||
``Final`` as immutable.
|
||||
|
||||
* Mypyc has memory-efficient, unboxed representations for integers and
|
||||
booleans.
|
||||
|
||||
Development status
|
||||
------------------
|
||||
|
||||
Mypyc is currently alpha software. It's only recommended for
|
||||
production use cases with careful testing, and if you are willing to
|
||||
contribute fixes or to work around issues you will encounter.
|
||||
@@ -0,0 +1,65 @@
|
||||
.. _list-ops:
|
||||
|
||||
Native list operations
|
||||
======================
|
||||
|
||||
These ``list`` operations have fast, optimized implementations. Other
|
||||
list operations use generic implementations that are often slower.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
Construct list with specific items:
|
||||
|
||||
* ``[item0, ..., itemN]``
|
||||
|
||||
Construct empty list:
|
||||
|
||||
* ``[]``
|
||||
* ``list()``
|
||||
|
||||
Construct list from iterable:
|
||||
|
||||
* ``list(x: Iterable)``
|
||||
|
||||
List comprehensions:
|
||||
|
||||
* ``[... for ... in ...]``
|
||||
* ``[... for ... in ... if ...]``
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* ``lst[n]`` (get item by integer index)
|
||||
* ``lst[n:m]``, ``lst[n:]``, ``lst[:m]``, ``lst[:]`` (slicing)
|
||||
* ``lst * n``, ``n * lst``
|
||||
* ``obj in lst``
|
||||
|
||||
Statements
|
||||
----------
|
||||
|
||||
Set item by integer index:
|
||||
|
||||
* ``lst[n] = x``
|
||||
|
||||
For loop over a list:
|
||||
|
||||
* ``for item in lst:``
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
* ``lst.append(obj)``
|
||||
* ``lst.extend(x: Iterable)``
|
||||
* ``lst.insert(index, obj)``
|
||||
* ``lst.pop(index=-1)``
|
||||
* ``lst.remove(obj)``
|
||||
* ``lst.count(obj)``
|
||||
* ``lst.index(obj)``
|
||||
* ``lst.reverse()``
|
||||
* ``lst.sort()``
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``len(lst: list)``
|
||||
35
venv/lib/python3.12/site-packages/mypyc/doc/make.bat
Normal file
35
venv/lib/python3.12/site-packages/mypyc/doc/make.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
206
venv/lib/python3.12/site-packages/mypyc/doc/native_classes.rst
Normal file
206
venv/lib/python3.12/site-packages/mypyc/doc/native_classes.rst
Normal file
@@ -0,0 +1,206 @@
|
||||
.. _native-classes:
|
||||
|
||||
Native classes
|
||||
==============
|
||||
|
||||
Classes in compiled modules are *native classes* by default (some
|
||||
exceptions are discussed below). Native classes are compiled to C
|
||||
extension classes, which have some important differences from normal
|
||||
Python classes. Native classes are similar in many ways to built-in
|
||||
types, such as ``int``, ``str``, and ``list``.
|
||||
|
||||
Immutable namespaces
|
||||
--------------------
|
||||
|
||||
The type object namespace of native classes is mostly immutable (but
|
||||
class variables can be assigned to)::
|
||||
|
||||
class Cls:
|
||||
def method1(self) -> None:
|
||||
print("method1")
|
||||
|
||||
def method2(self) -> None:
|
||||
print("method2")
|
||||
|
||||
Cls.method1 = Cls.method2 # Error
|
||||
Cls.new_method = Cls.method2 # Error
|
||||
|
||||
Only attributes defined within a class definition (or in a base class)
|
||||
can be assigned to (similar to using ``__slots__``)::
|
||||
|
||||
class Cls:
|
||||
x: int
|
||||
|
||||
def __init__(self, y: int) -> None:
|
||||
self.x = 0
|
||||
self.y = y
|
||||
|
||||
def method(self) -> None:
|
||||
self.z = "x"
|
||||
|
||||
o = Cls(0)
|
||||
print(o.x, o.y) # OK
|
||||
o.z = "y" # OK
|
||||
o.extra = 3 # Error: no attribute "extra"
|
||||
|
||||
.. _inheritance:
|
||||
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
Only single inheritance is supported (except for :ref:`traits
|
||||
<trait-types>`). Most non-native classes can't be used as base
|
||||
classes.
|
||||
|
||||
These non-native classes can be used as base classes of native
|
||||
classes:
|
||||
|
||||
* ``object``
|
||||
* ``dict`` (and ``Dict[k, v]``)
|
||||
* ``BaseException``
|
||||
* ``Exception``
|
||||
* ``ValueError``
|
||||
* ``IndexError``
|
||||
* ``LookupError``
|
||||
* ``UserWarning``
|
||||
* ``typing.NamedTuple``
|
||||
* ``enum.Enum``
|
||||
|
||||
By default, a non-native class can't inherit a native class, and you
|
||||
can't inherit from a native class outside the compilation unit that
|
||||
defines the class. You can enable these through
|
||||
``mypy_extensions.mypyc_attr``::
|
||||
|
||||
from mypy_extensions import mypyc_attr
|
||||
|
||||
@mypyc_attr(allow_interpreted_subclasses=True)
|
||||
class Cls:
|
||||
...
|
||||
|
||||
Allowing interpreted subclasses has only minor impact on performance
|
||||
of instances of the native class. Accessing methods and attributes of
|
||||
a *non-native* subclass (or a subclass defined in another compilation
|
||||
unit) will be slower, since it needs to use the normal Python
|
||||
attribute access mechanism.
|
||||
|
||||
You need to install ``mypy-extensions`` to use ``@mypyc_attr``:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
pip install --upgrade mypy-extensions
|
||||
|
||||
Class variables
|
||||
---------------
|
||||
|
||||
Class variables must be explicitly declared using ``attr: ClassVar``
|
||||
or ``attr: ClassVar[<type>]``. You can't assign to a class variable
|
||||
through an instance. Example::
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
class Cls:
|
||||
cv: ClassVar = 0
|
||||
|
||||
Cls.cv = 2 # OK
|
||||
o = Cls()
|
||||
print(o.cv) # OK (2)
|
||||
o.cv = 3 # Error!
|
||||
|
||||
.. tip::
|
||||
|
||||
Constant class variables can be declared using ``typing.Final`` or
|
||||
``typing.Final[<type>]``.
|
||||
|
||||
Generic native classes
|
||||
----------------------
|
||||
|
||||
Native classes can be generic. Type variables are *erased* at runtime,
|
||||
and instances don't keep track of type variable values.
|
||||
|
||||
Compiled code thus can't check the values of type variables when
|
||||
performing runtime type checks. These checks are delayed to when
|
||||
reading a value with a type variable type::
|
||||
|
||||
from typing import TypeVar, Generic, cast
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class Box(Generic[T]):
|
||||
def __init__(self, item: T) -> None:
|
||||
self.item = item
|
||||
|
||||
x = Box(1) # Box[int]
|
||||
y = cast(Box[str], x) # OK (type variable value not checked)
|
||||
y.item # Runtime error: item is "int", but "str" expected
|
||||
|
||||
Metaclasses
|
||||
-----------
|
||||
|
||||
Most metaclasses aren't supported with native classes, since their
|
||||
behavior is too dynamic. You can use these metaclasses, however:
|
||||
|
||||
* ``abc.ABCMeta``
|
||||
* ``typing.GenericMeta`` (used by ``typing.Generic``)
|
||||
|
||||
.. note::
|
||||
|
||||
If a class definition uses an unsupported metaclass, *mypyc
|
||||
compiles the class into a regular Python class*.
|
||||
|
||||
Class decorators
|
||||
----------------
|
||||
|
||||
Similar to metaclasses, most class decorators aren't supported with
|
||||
native classes, as they are usually too dynamic. These class
|
||||
decorators can be used with native classes, however:
|
||||
|
||||
* ``mypy_extensions.trait`` (for defining :ref:`trait types <trait-types>`)
|
||||
* ``mypy_extensions.mypyc_attr`` (see :ref:`above <inheritance>`)
|
||||
* ``dataclasses.dataclass``
|
||||
* ``@attr.s(auto_attribs=True)``
|
||||
|
||||
Dataclasses and attrs classes have partial native support, and they aren't as
|
||||
efficient as pure native classes.
|
||||
|
||||
.. note::
|
||||
|
||||
If a class definition uses an unsupported class decorator, *mypyc
|
||||
compiles the class into a regular Python class*.
|
||||
|
||||
Deleting attributes
|
||||
-------------------
|
||||
|
||||
By default, attributes defined in native classes can't be deleted. You
|
||||
can explicitly allow certain attributes to be deleted by using
|
||||
``__deletable__``::
|
||||
|
||||
class Cls:
|
||||
x: int = 0
|
||||
y: int = 0
|
||||
other: int = 0
|
||||
|
||||
__deletable__ = ['x', 'y'] # 'x' and 'y' can be deleted
|
||||
|
||||
o = Cls()
|
||||
del o.x # OK
|
||||
del o.y # OK
|
||||
del o.other # Error
|
||||
|
||||
You must initialize the ``__deletable__`` attribute in the class body,
|
||||
using a list or a tuple expression with only string literal items that
|
||||
refer to attributes. These are not valid::
|
||||
|
||||
a = ['x', 'y']
|
||||
|
||||
class Cls:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
__deletable__ = a # Error: cannot use variable 'a'
|
||||
|
||||
__deletable__ = ('a',) # Error: not in a class body
|
||||
|
||||
Other properties
|
||||
----------------
|
||||
|
||||
Instances of native classes don't usually have a ``__dict__`` attribute.
|
||||
@@ -0,0 +1,55 @@
|
||||
Miscellaneous native operations
|
||||
===============================
|
||||
|
||||
This is a list of various non-type-specific operations that have
|
||||
custom native implementations. If an operation has no native
|
||||
implementation, mypyc will use fallback generic implementations that
|
||||
are often not as fast.
|
||||
|
||||
.. note::
|
||||
|
||||
Operations specific to various primitive types are described
|
||||
in the following sections.
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* ``x is y`` (this is very fast for all types)
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``isinstance(obj, type: type)``
|
||||
* ``isinstance(obj, type: tuple)``
|
||||
* ``cast(<type>, obj)``
|
||||
* ``type(obj)``
|
||||
* ``len(obj)``
|
||||
* ``abs(obj)``
|
||||
* ``id(obj)``
|
||||
* ``iter(obj)``
|
||||
* ``next(iter: Iterator)``
|
||||
* ``hash(obj)``
|
||||
* ``getattr(obj, attr)``
|
||||
* ``getattr(obj, attr, default)``
|
||||
* ``setattr(obj, attr, value)``
|
||||
* ``hasattr(obj, attr)``
|
||||
* ``delattr(obj, name)``
|
||||
* ``slice(start, stop, step)``
|
||||
* ``globals()``
|
||||
|
||||
Method decorators
|
||||
-----------------
|
||||
|
||||
* ``@property``
|
||||
* ``@staticmethod``
|
||||
* ``@classmethod``
|
||||
* ``@abc.abstractmethod``
|
||||
|
||||
Statements
|
||||
----------
|
||||
|
||||
These variants of statements have custom implementations:
|
||||
|
||||
* ``for ... in seq:`` (for loop over a sequence)
|
||||
* ``for ... in enumerate(...):``
|
||||
* ``for ... in zip(...):``
|
||||
@@ -0,0 +1,244 @@
|
||||
.. _performance-tips:
|
||||
|
||||
Performance tips and tricks
|
||||
===========================
|
||||
|
||||
Performance optimization is part art, part science. Just using mypyc
|
||||
in a simple manner will likely make your code faster, but squeezing
|
||||
the most performance out of your code requires the use of some
|
||||
techniques we'll summarize below.
|
||||
|
||||
Profiling
|
||||
---------
|
||||
|
||||
If you are speeding up existing code, understanding where time is
|
||||
spent is important. Mypyc speeds up code that you compile. If most of
|
||||
the time is spent elsewhere, you may come back disappointed. For
|
||||
example, if you spend 40% of time outside compiled code, even if
|
||||
compiled code would go 100x faster, overall performance will only be
|
||||
2.5x faster.
|
||||
|
||||
A simple (but often effective) approach is to record the time in
|
||||
various points of program execution using ``time.time()``, and to
|
||||
print out elapsed time (or to write it to a log file).
|
||||
|
||||
The stdlib modules ``profile`` and ``cProfile`` can provide much more
|
||||
detailed data. (But these only work well with non-compiled code.)
|
||||
|
||||
Avoiding slow libraries
|
||||
-----------------------
|
||||
|
||||
If profiling indicates that a lot of time is spent in the stdlib or
|
||||
third-party libraries, you still have several options.
|
||||
|
||||
First, if most time is spent in a few library features, you can
|
||||
perhaps easily reimplement them in type-annotated Python, or extract
|
||||
the relevant code and annotate it. Now it may be easy to compile this
|
||||
code to speed it up.
|
||||
|
||||
Second, you may be able to avoid the library altogether, or use an
|
||||
alternative, more efficient library to achieve the same purpose.
|
||||
|
||||
Type annotations
|
||||
----------------
|
||||
|
||||
As discussed earlier, type annotations are key to major performance
|
||||
gains. You should at least consider adding annotations to any
|
||||
performance-critical functions and classes. It may also be helpful to
|
||||
annotate code called by this code, even if it's not compiled, since
|
||||
this may help mypy infer better types in the compile code. If you use
|
||||
libraries, ensure they have stub files with decent type annotation
|
||||
coverage. Writing a stub file is often easy, and you only need to
|
||||
annotate features you use a lot.
|
||||
|
||||
If annotating external code or writing stubs feel too burdensome, a
|
||||
simple workaround is to annotate variables explicitly. For example,
|
||||
here we call ``acme.get_items()``, but it has no type annotation. We
|
||||
can use an explicit type annotation for the variable to which we
|
||||
assign the result::
|
||||
|
||||
from typing import List, Tuple
|
||||
import acme
|
||||
|
||||
def work() -> None:
|
||||
# Annotate "items" to help mypyc
|
||||
items: List[Tuple[int, str]] = acme.get_items()
|
||||
for item in items:
|
||||
... # Do some work here
|
||||
|
||||
Without the annotation on ``items``, the type would be ``Any`` (since
|
||||
``acme`` has no type annotation), resulting in slower, generic
|
||||
operations being used later in the function.
|
||||
|
||||
Avoiding slow Python features
|
||||
-----------------------------
|
||||
|
||||
Mypyc can optimize some features more effectively than others. Here
|
||||
the difference is sometimes big -- some things only get marginally
|
||||
faster at best, while others can get 10x faster, or more. Avoiding
|
||||
these slow features in performance-critical parts of your code can
|
||||
help a lot.
|
||||
|
||||
These are some of the most important things to avoid:
|
||||
|
||||
* Using class decorators or metaclasses in compiled code (that aren't
|
||||
properly supported by mypyc)
|
||||
|
||||
* Heavy reliance on interpreted Python libraries (C extensions are
|
||||
usually fine)
|
||||
|
||||
These things also tend to be relatively slow:
|
||||
|
||||
* Using Python classes and instances of Python classes (native classes
|
||||
are much faster)
|
||||
|
||||
* Calling decorated functions (``@property``, ``@staticmethod``, and
|
||||
``@classmethod`` are special cased and thus fast)
|
||||
|
||||
* Calling nested functions
|
||||
|
||||
* Calling functions or methods defined in other compilation units
|
||||
|
||||
* Using ``*args`` or ``**kwargs``
|
||||
|
||||
* Using generator functions
|
||||
|
||||
* Using callable values (i.e. not leveraging early binding to call
|
||||
functions or methods)
|
||||
|
||||
Nested functions can often be replaced with module-level functions or
|
||||
methods of native classes.
|
||||
|
||||
Callable values and nested functions can sometimes be replaced with an
|
||||
instance of a native class with a single method only, such as
|
||||
``call(...)``. You can derive the class from an ABC, if there are
|
||||
multiple possible functions.
|
||||
|
||||
.. note::
|
||||
|
||||
Some slow features will likely get efficient implementations in the
|
||||
future. You should check this section every once in a while to see
|
||||
if some additional operations are fast.
|
||||
|
||||
Using fast native features
|
||||
--------------------------
|
||||
|
||||
Some native operations are particularly quick relative to the
|
||||
corresponding interpreted operations. Using them as much as possible
|
||||
may allow you to see 10x or more in performance gains.
|
||||
|
||||
Some things are not much (or any) faster in compiled code, such as set
|
||||
math operations. In contrast, calling a method of a native class is
|
||||
much faster in compiled code.
|
||||
|
||||
If you are used to optimizing for CPython, you might have replaced
|
||||
some class instances with dictionaries, as they can be
|
||||
faster. However, in compiled code, this "optimization" would likely
|
||||
slow down your code.
|
||||
|
||||
Similarly, caching a frequently called method in a local variable can
|
||||
help in CPython, but it can slow things down in compiled code, since
|
||||
the code won't use :ref:`early binding <early-binding>`::
|
||||
|
||||
def squares(n: int) -> List[int]:
|
||||
a = []
|
||||
append = a.append # Not a good idea in compiled code!
|
||||
for i in range(n):
|
||||
append(i * i)
|
||||
return a
|
||||
|
||||
Here are examples of features that are fast, in no particular order
|
||||
(this list is *not* exhaustive):
|
||||
|
||||
* Calling compiled functions directly defined in the same compilation
|
||||
unit (with positional and/or keyword arguments)
|
||||
|
||||
* Calling methods of native classes defined in the same compilation
|
||||
unit (with positional and/or keyword arguments)
|
||||
|
||||
* Many integer operations
|
||||
|
||||
* Many ``float`` operations
|
||||
|
||||
* Booleans
|
||||
|
||||
* :ref:`Native list operations <list-ops>`, such as indexing,
|
||||
``append``, and list comprehensions
|
||||
|
||||
* While loops
|
||||
|
||||
* For loops over ranges and lists, and with ``enumerate`` or ``zip``
|
||||
|
||||
* Reading dictionary items
|
||||
|
||||
* ``isinstance()`` checks against native classes and instances of
|
||||
primitive types (and unions of them)
|
||||
|
||||
* Accessing local variables
|
||||
|
||||
* Accessing attributes of native classes
|
||||
|
||||
* Accessing final module-level attributes
|
||||
|
||||
* Comparing strings for equality
|
||||
|
||||
These features are also fast, but somewhat less so (relative to other
|
||||
related operations):
|
||||
|
||||
* Constructing instances of native classes
|
||||
|
||||
* Constructing dictionaries
|
||||
|
||||
* Setting dictionary items
|
||||
|
||||
* Native :ref:`dict <dict-ops>` and :ref:`set <set-ops>` operations
|
||||
|
||||
* Accessing module-level variables
|
||||
|
||||
Generally anything documented as a native operation is fast, even if
|
||||
it's not explicitly mentioned here
|
||||
|
||||
Adjusting garbage collection
|
||||
----------------------------
|
||||
|
||||
Compilation does not speed up cyclic garbage collection. If everything
|
||||
else gets much faster, it's possible that garbage collection will take
|
||||
a big fraction of time. You can use ``gc.set_threshold()`` to adjust
|
||||
the garbage collector to run less often::
|
||||
|
||||
import gc
|
||||
|
||||
# Spend less time in gc; do this before significant computation
|
||||
gc.set_threshold(150000)
|
||||
|
||||
... # Actual work happens here
|
||||
|
||||
Fast interpreter shutdown
|
||||
-------------------------
|
||||
|
||||
If you allocate many objects, it's possible that your program spends a
|
||||
lot of time cleaning up when the Python runtime shuts down. Mypyc
|
||||
won't speed up the shutdown of a Python process much.
|
||||
|
||||
You can call ``os._exit(code)`` to immediately terminate the Python
|
||||
process, skipping normal cleanup. This can give a nice boost to a
|
||||
batch process or a command-line tool.
|
||||
|
||||
.. note::
|
||||
|
||||
This can be dangerous and can lose data. You need to ensure
|
||||
that all streams are flushed and everything is otherwise cleaned up
|
||||
properly.
|
||||
|
||||
Work smarter
|
||||
------------
|
||||
|
||||
Usually there are many things you can do to improve performance, even
|
||||
if most tweaks will yield only minor gains. The key to being effective
|
||||
is to focus on things that give a large gain with a small effort.
|
||||
|
||||
For example, low-level optimizations, such as avoiding a nested
|
||||
function, can be pointless, if you could instead avoid a metaclass --
|
||||
to allow a key class to be compiled as a native class. The latter
|
||||
optimization could speed up numerous method calls and attribute
|
||||
accesses, just like that.
|
||||
@@ -0,0 +1,47 @@
|
||||
.. _set-ops:
|
||||
|
||||
Native set operations
|
||||
======================
|
||||
|
||||
These ``set`` operations have fast, optimized implementations. Other
|
||||
set operations use generic implementations that are often slower.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
Construct set with specific items:
|
||||
|
||||
* ``{item0, ..., itemN}``
|
||||
|
||||
Construct empty set:
|
||||
|
||||
* ``set()``
|
||||
|
||||
Construct set from iterable:
|
||||
|
||||
* ``set(x: Iterable)``
|
||||
|
||||
Set comprehensions:
|
||||
|
||||
* ``{... for ... in ...}``
|
||||
* ``{... for ... in ... if ...}``
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* ``item in s``
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
* ``s.add(item)``
|
||||
* ``s.remove(item)``
|
||||
* ``s.discard(item)``
|
||||
* ``s.update(x: Iterable)``
|
||||
* ``s.clear()``
|
||||
* ``s.pop()``
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``len(s: set)``
|
||||
@@ -0,0 +1,35 @@
|
||||
.. _str-ops:
|
||||
|
||||
Native string operations
|
||||
========================
|
||||
|
||||
These ``str`` operations have fast, optimized implementations. Other
|
||||
string operations use generic implementations that are often slower.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
* String literal
|
||||
* ``str(x: int)``
|
||||
* ``str(x: object)``
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* Concatenation (``s1 + s2``)
|
||||
* Indexing (``s[n]``)
|
||||
* Slicing (``s[n:m]``, ``s[n:]``, ``s[:m]``)
|
||||
* Comparisons (``==``, ``!=``)
|
||||
* Augmented assignment (``s1 += s2``)
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
* ``s1.endswith(s2: str)``
|
||||
* ``s.join(x: Iterable)``
|
||||
* ``s.replace(old: str, new: str)``
|
||||
* ``s.replace(old: str, new: str, count: int)``
|
||||
* ``s.split()``
|
||||
* ``s.split(sep: str)``
|
||||
* ``s.split(sep: str, maxsplit: int)``
|
||||
* ``s1.startswith(s2: str)``
|
||||
@@ -0,0 +1,33 @@
|
||||
.. _tuple-ops:
|
||||
|
||||
Native tuple operations
|
||||
=======================
|
||||
|
||||
These ``tuple`` operations have fast, optimized implementations. Other
|
||||
tuple operations use generic implementations that are often slower.
|
||||
|
||||
Unless mentioned otherwise, these operations apply to both fixed-length
|
||||
tuples and variable-length tuples.
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
* ``item0, ..., itemN`` (construct a tuple)
|
||||
* ``tuple(lst: list)`` (construct a variable-length tuple)
|
||||
* ``tuple(lst: Iterable)`` (construct a variable-length tuple)
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
* ``tup[n]`` (integer index)
|
||||
* ``tup[n:m]``, ``tup[n:]``, ``tup[:m]`` (slicing)
|
||||
|
||||
Statements
|
||||
----------
|
||||
|
||||
* ``item0, ..., itemN = tup`` (for fixed-length tuples)
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* ``len(tup: tuple)``
|
||||
@@ -0,0 +1,398 @@
|
||||
.. _using-type-annotations:
|
||||
|
||||
Using type annotations
|
||||
======================
|
||||
|
||||
You will get the most out of mypyc if you compile code with precise
|
||||
type annotations. Not all type annotations will help performance
|
||||
equally, however. Using types such as :ref:`primitive types
|
||||
<primitive-types>`, :ref:`native classes <native-class-intro>`,
|
||||
:ref:`union types <union-types>`, :ref:`trait types <trait-types>`,
|
||||
and :ref:`tuple types <tuple-types>` as much as possible is a key to
|
||||
major performance gains over CPython.
|
||||
|
||||
In contrast, some other types, including ``Any``, are treated as
|
||||
:ref:`erased types <erased-types>`. Operations on erased types use
|
||||
generic operations that work with arbitrary objects, similar to how
|
||||
the CPython interpreter works. If you only use erased types, the only
|
||||
notable benefits over CPython will be the removal of interpreter
|
||||
overhead (from compilation) and a bit of :ref:`early binding
|
||||
<early-binding>`, which will usually only give minor performance
|
||||
gains.
|
||||
|
||||
.. _primitive-types:
|
||||
|
||||
Primitive types
|
||||
---------------
|
||||
|
||||
The following built-in types are treated as *primitive types* by
|
||||
mypyc, and many operations on these types have efficient
|
||||
implementations:
|
||||
|
||||
* ``int`` (:ref:`native operations <int-ops>`)
|
||||
* ``i64`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
|
||||
* ``i32`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
|
||||
* ``i16`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
|
||||
* ``u8`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
|
||||
* ``float`` (:ref:`native operations <float-ops>`)
|
||||
* ``bool`` (:ref:`native operations <bool-ops>`)
|
||||
* ``str`` (:ref:`native operations <str-ops>`)
|
||||
* ``List[T]`` (:ref:`native operations <list-ops>`)
|
||||
* ``Dict[K, V]`` (:ref:`native operations <dict-ops>`)
|
||||
* ``Set[T]`` (:ref:`native operations <set-ops>`)
|
||||
* ``Tuple[T, ...]`` (variable-length tuple; :ref:`native operations <tuple-ops>`)
|
||||
* ``None``
|
||||
|
||||
The link after each type lists all supported native, optimized
|
||||
operations for the type. You can use all operations supported by
|
||||
Python, but *native operations* will have custom, optimized
|
||||
implementations.
|
||||
|
||||
Primitive containers
|
||||
--------------------
|
||||
|
||||
Primitive container objects such as ``list`` and ``dict`` don't
|
||||
maintain knowledge of the item types at runtime -- the item type is
|
||||
*erased*.
|
||||
|
||||
This means that item types are checked when items are accessed, not
|
||||
when a container is passed as an argument or assigned to another
|
||||
variable. For example, here we have a runtime type error on the final
|
||||
line of ``example`` (the ``Any`` type means an arbitrary, unchecked
|
||||
value)::
|
||||
|
||||
from typing import List, Any
|
||||
|
||||
def example(a: List[Any]) -> None:
|
||||
b: List[int] = a # No error -- items are not checked
|
||||
print(b[0]) # Error here -- got str, but expected int
|
||||
|
||||
example(["x"])
|
||||
|
||||
.. _native-class-intro:
|
||||
|
||||
Native classes
|
||||
--------------
|
||||
|
||||
Classes that get compiled to C extensions are called native
|
||||
classes. Most common operations on instances of these classes are
|
||||
optimized, including construction, attribute access and method calls.
|
||||
|
||||
Native class definitions look exactly like normal Python class
|
||||
definitions. A class is usually native if it's in a compiled module
|
||||
(though there are some exceptions).
|
||||
|
||||
Consider this example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
class Point:
|
||||
def __init__(self, x: int, y: int) -> None:
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def shift(p: Point) -> Point:
|
||||
return Point(p.x + 1, p.y + 1)
|
||||
|
||||
All operations in the above example use native operations, if the file
|
||||
is compiled.
|
||||
|
||||
Native classes have some notable different from Python classes:
|
||||
|
||||
* Only attributes and methods defined in the class body or methods are
|
||||
supported. If you try to assign to an undefined attribute outside
|
||||
the class definition, ``AttributeError`` will be raised. This enables
|
||||
an efficient memory layout and fast method calls for native classes.
|
||||
|
||||
* Native classes usually don't define the ``__dict__`` attribute (they
|
||||
don't have an attribute dictionary). This follows from only having
|
||||
a specific set of attributes.
|
||||
|
||||
* Native classes can't have an arbitrary metaclass or use most class
|
||||
decorators.
|
||||
|
||||
Native classes only support single inheritance. A limited form of
|
||||
multiple inheritance is supported through *trait types*. You generally
|
||||
must inherit from another native class (or ``object``). By default,
|
||||
you can't inherit a Python class from a native class (but there's
|
||||
an :ref:`override <inheritance>` to allow that).
|
||||
|
||||
See :ref:`native-classes` for more details.
|
||||
|
||||
.. _tuple-types:
|
||||
|
||||
Tuple types
|
||||
-----------
|
||||
|
||||
Fixed-length
|
||||
`tuple types <https://mypy.readthedocs.io/en/stable/kinds_of_types.html#tuple-types>`_
|
||||
such as ``Tuple[int, str]`` are represented
|
||||
as :ref:`value types <value-and-heap-types>` when stored in variables,
|
||||
passed as arguments, or returned from functions. Value types are
|
||||
allocated in the low-level machine stack or in CPU registers, as
|
||||
opposed to *heap types*, which are allocated dynamically from the
|
||||
heap.
|
||||
|
||||
Like all value types, tuples will be *boxed*, i.e. converted to
|
||||
corresponding heap types, when stored in Python containers, or passed
|
||||
to non-native code. A boxed tuple value will be a regular Python tuple
|
||||
object.
|
||||
|
||||
.. _union-types:
|
||||
|
||||
Union types
|
||||
-----------
|
||||
|
||||
`Union types <https://mypy.readthedocs.io/en/stable/kinds_of_types.html#union-types>`_
|
||||
and
|
||||
`optional types <https://mypy.readthedocs.io/en/stable/kinds_of_types.html#optional-types-and-the-none-type>`_
|
||||
that contain primitive types, native class types and
|
||||
trait types are also efficient. If a union type has
|
||||
:ref:`erased <erased-types>` items, accessing items with
|
||||
non-erased types is often still quite efficient.
|
||||
|
||||
A value with a union types is always :ref:`boxed <value-and-heap-types>`,
|
||||
even if it contains a value that also has an unboxed representation, such
|
||||
as an integer or a boolean.
|
||||
|
||||
For example, using ``Optional[int]`` is quite efficient, but the value
|
||||
will always be boxed. A plain ``int`` value will usually be faster, since
|
||||
it has an unboxed representation.
|
||||
|
||||
.. _trait-types:
|
||||
|
||||
Trait types
|
||||
-----------
|
||||
|
||||
Trait types enable a form of multiple inheritance for native classes.
|
||||
A native class can inherit any number of traits. Trait types are
|
||||
defined as classes using the ``mypy_extensions.trait`` decorator::
|
||||
|
||||
from mypy_extensions import trait
|
||||
|
||||
@trait
|
||||
class MyTrait:
|
||||
def method(self) -> None:
|
||||
...
|
||||
|
||||
Traits can define methods, properties and attributes. They often
|
||||
define abstract methods. Traits can be generic.
|
||||
|
||||
If a class subclasses both a non-trait class and traits, the traits
|
||||
must be placed at the end of the base class list::
|
||||
|
||||
class Base: ...
|
||||
|
||||
class Derived(Base, MyTrait, FooTrait): # OK
|
||||
...
|
||||
|
||||
class Derived2(MyTrait, FooTrait, Base):
|
||||
# Error: traits should come last
|
||||
...
|
||||
|
||||
Traits have some special properties:
|
||||
|
||||
* You shouldn't create instances of traits (though mypyc does not
|
||||
prevent it yet).
|
||||
|
||||
* Traits can subclass other traits or native classes, but the MRO must be
|
||||
linear (just like with native classes).
|
||||
|
||||
* Accessing methods or attributes through a trait type is somewhat
|
||||
less efficient than through a native class type, but this is much
|
||||
faster than through Python class types or other
|
||||
:ref:`erased types <erased-types>`.
|
||||
|
||||
You need to install ``mypy-extensions`` to use ``@trait``:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
pip install --upgrade mypy-extensions
|
||||
|
||||
.. _erased-types:
|
||||
|
||||
Erased types
|
||||
------------
|
||||
|
||||
Mypyc supports many other kinds of types as well, beyond those
|
||||
described above. However, these types don't have customized
|
||||
operations, and they are implemented using *type erasure*. Type
|
||||
erasure means that all other types are equivalent to untyped values at
|
||||
runtime, i.e. they are the equivalent of the type ``Any``. Erased
|
||||
types include these:
|
||||
|
||||
* Python classes (including ABCs)
|
||||
|
||||
* Non-mypyc extension types and primitive types (including built-in
|
||||
types that are not primitives)
|
||||
|
||||
* `Callable types <https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas>`_
|
||||
|
||||
* `Type variable types <https://mypy.readthedocs.io/en/stable/generics.html>`_
|
||||
|
||||
* Type `Any <https://mypy.readthedocs.io/en/stable/dynamic_typing.html>`_
|
||||
|
||||
* Protocol types
|
||||
|
||||
Using erased types can still improve performance, since they can
|
||||
enable better types to be inferred for expressions that use these
|
||||
types. For example, a value with type ``Callable[[], int]`` will not
|
||||
allow native calls. However, the return type is a primitive type, and
|
||||
we can use fast operations on the return value::
|
||||
|
||||
from typing import Callable
|
||||
|
||||
def call_and_inc(f: Callable[[], int]) -> int:
|
||||
# Slow call, since f has an erased type
|
||||
n = f()
|
||||
# Fast increment; inferred type of n is int (primitive type)
|
||||
n += 1
|
||||
return n
|
||||
|
||||
If the type of the argument ``f`` was ``Any``, the type of ``n`` would
|
||||
also be ``Any``, resulting in a generic, slower increment operation
|
||||
being used.
|
||||
|
||||
Strict runtime type checking
|
||||
----------------------------
|
||||
|
||||
Compiled code ensures that any variable or expression with a
|
||||
non-erased type only has compatible values at runtime. This is in
|
||||
contrast with using *optional static typing*, such as by using mypy,
|
||||
when type annotations are not enforced at runtime. Mypyc ensures
|
||||
type safety both statically and at runtime.
|
||||
|
||||
``Any`` types and erased types in general can compromise type safety,
|
||||
and this is by design. Inserting strict runtime type checks for all
|
||||
possible values would be too expensive and against the goal of
|
||||
high performance.
|
||||
|
||||
.. _value-and-heap-types:
|
||||
|
||||
Value and heap types
|
||||
--------------------
|
||||
|
||||
In CPython, memory for all objects is dynamically allocated on the
|
||||
heap. All Python types are thus *heap types*. In compiled code, some
|
||||
types are *value types* -- no object is (necessarily) allocated on the
|
||||
heap. ``bool``, ``float``, ``None``, :ref:`native integer types <native-ints>`
|
||||
and fixed-length tuples are value types.
|
||||
|
||||
``int`` is a hybrid. For typical integer values, it is a value
|
||||
type. Large enough integer values, those that require more than 63
|
||||
bits (or 31 bits on 32-bit platforms) to represent, use a heap-based
|
||||
representation (same as CPython).
|
||||
|
||||
Value types have a few differences from heap types:
|
||||
|
||||
* When an instance of a value type is used in a context that expects a
|
||||
heap value, for example as a list item, it will transparently switch
|
||||
to a heap-based representation (boxing) as needed.
|
||||
|
||||
* Similarly, mypyc transparently changes from a heap-based
|
||||
representation to a value representation (unboxing).
|
||||
|
||||
* Object identity of integers, floating point values and tuples is not
|
||||
preserved. You should use ``==`` instead of ``is`` if you are comparing
|
||||
two integers, floats or fixed-length tuples.
|
||||
|
||||
* When an instance of a subclass of a value type is converted to the
|
||||
base type, it is implicitly converted to an instance of the target
|
||||
type. For example, a ``bool`` value assigned to a variable with an
|
||||
``int`` type will be converted to the corresponding integer.
|
||||
|
||||
The latter conversion is the only implicit type conversion that
|
||||
happens in mypyc programs.
|
||||
|
||||
Example::
|
||||
|
||||
def example() -> None:
|
||||
# A small integer uses the value (unboxed) representation
|
||||
x = 5
|
||||
# A large integer uses the heap (boxed) representation
|
||||
x = 2**500
|
||||
# Lists always contain boxed integers
|
||||
a = [55]
|
||||
# When reading from a list, the object is automatically unboxed
|
||||
x = a[0]
|
||||
# True is converted to 1 on assignment
|
||||
x = True
|
||||
|
||||
Since integers and floating point values have a different runtime
|
||||
representations and neither can represent all the values of the other
|
||||
type, type narrowing of floating point values through assignment is
|
||||
disallowed in compiled code. For consistency, mypyc rejects assigning
|
||||
an integer value to a float variable even in variable initialization.
|
||||
An explicit conversion is required.
|
||||
|
||||
Examples::
|
||||
|
||||
def narrowing(n: int) -> None:
|
||||
# Error: Incompatible value representations in assignment
|
||||
# (expression has type "int", variable has type "float")
|
||||
x: float = 0
|
||||
|
||||
y: float = 0.0 # Ok
|
||||
|
||||
if f():
|
||||
y = n # Error
|
||||
if f():
|
||||
y = float(n) # Ok
|
||||
|
||||
.. _native-ints:
|
||||
|
||||
Native integer types
|
||||
--------------------
|
||||
|
||||
You can use the native integer types ``i64`` (64-bit signed integer),
|
||||
``i32`` (32-bit signed integer), ``i16`` (16-bit signed integer), and
|
||||
``u8`` (8-bit unsigned integer) if you know that integer values will
|
||||
always fit within fixed bounds. These types are faster than the
|
||||
arbitrary-precision ``int`` type, since they don't require overflow
|
||||
checks on operations. They may also use less memory than ``int``
|
||||
values. The types are imported from the ``mypy_extensions`` module
|
||||
(installed via ``pip install mypy_extensions``).
|
||||
|
||||
Example::
|
||||
|
||||
from mypy_extensions import i64
|
||||
|
||||
def sum_list(l: list[i64]) -> i64:
|
||||
s: i64 = 0
|
||||
for n in l:
|
||||
s += n
|
||||
return s
|
||||
|
||||
# Implicit conversions from int to i64
|
||||
print(sum_list([1, 3, 5]))
|
||||
|
||||
.. note::
|
||||
|
||||
Since there are no overflow checks when performing native integer
|
||||
arithmetic, the above function could result in an overflow or other
|
||||
undefined behavior if the sum might not fit within 64 bits.
|
||||
|
||||
The behavior when running as interpreted Python program will be
|
||||
different if there are overflows. Declaring native integer types
|
||||
have no effect unless code is compiled. Native integer types are
|
||||
effectively equivalent to ``int`` when interpreted.
|
||||
|
||||
Native integer types have these additional properties:
|
||||
|
||||
* Values can be implicitly converted between ``int`` and a native
|
||||
integer type (both ways).
|
||||
|
||||
* Conversions between different native integer types must be explicit.
|
||||
A conversion to a narrower native integer type truncates the value
|
||||
without a runtime overflow check.
|
||||
|
||||
* If a binary operation (such as ``+``) or an augmented assignment
|
||||
(such as ``+=``) mixes native integer and ``int`` values, the
|
||||
``int`` operand is implicitly coerced to the native integer type
|
||||
(native integer types are "sticky").
|
||||
|
||||
* You can't mix different native integer types in binary
|
||||
operations. Instead, convert between types explicitly.
|
||||
|
||||
For more information about native integer types, refer to
|
||||
:ref:`native integer operations <int-ops>`.
|
||||
BIN
venv/lib/python3.12/site-packages/mypyc/errors.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/mypyc/errors.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
29
venv/lib/python3.12/site-packages/mypyc/errors.py
Normal file
29
venv/lib/python3.12/site-packages/mypyc/errors.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import mypy.errors
|
||||
from mypy.options import Options
|
||||
|
||||
|
||||
class Errors:
|
||||
def __init__(self, options: Options) -> None:
|
||||
self.num_errors = 0
|
||||
self.num_warnings = 0
|
||||
self._errors = mypy.errors.Errors(options, hide_error_codes=True)
|
||||
|
||||
def error(self, msg: str, path: str, line: int) -> None:
|
||||
self._errors.report(line, None, msg, severity="error", file=path)
|
||||
self.num_errors += 1
|
||||
|
||||
def note(self, msg: str, path: str, line: int) -> None:
|
||||
self._errors.report(line, None, msg, severity="note", file=path)
|
||||
|
||||
def warning(self, msg: str, path: str, line: int) -> None:
|
||||
self._errors.report(line, None, msg, severity="warning", file=path)
|
||||
self.num_warnings += 1
|
||||
|
||||
def new_messages(self) -> list[str]:
|
||||
return self._errors.new_messages()
|
||||
|
||||
def flush_errors(self) -> None:
|
||||
for error in self.new_messages():
|
||||
print(error)
|
||||
28
venv/lib/python3.12/site-packages/mypyc/external/googletest/LICENSE
vendored
Normal file
28
venv/lib/python3.12/site-packages/mypyc/external/googletest/LICENSE
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
Copyright 2008, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
280
venv/lib/python3.12/site-packages/mypyc/external/googletest/README.md
vendored
Normal file
280
venv/lib/python3.12/site-packages/mypyc/external/googletest/README.md
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
|
||||
### Generic Build Instructions ###
|
||||
|
||||
#### Setup ####
|
||||
|
||||
To build Google Test and your tests that use it, you need to tell your
|
||||
build system where to find its headers and source files. The exact
|
||||
way to do it depends on which build system you use, and is usually
|
||||
straightforward.
|
||||
|
||||
#### Build ####
|
||||
|
||||
Suppose you put Google Test in directory `${GTEST_DIR}`. To build it,
|
||||
create a library build target (or a project as called by Visual Studio
|
||||
and Xcode) to compile
|
||||
|
||||
${GTEST_DIR}/src/gtest-all.cc
|
||||
|
||||
with `${GTEST_DIR}/include` in the system header search path and `${GTEST_DIR}`
|
||||
in the normal header search path. Assuming a Linux-like system and gcc,
|
||||
something like the following will do:
|
||||
|
||||
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
|
||||
-pthread -c ${GTEST_DIR}/src/gtest-all.cc
|
||||
ar -rv libgtest.a gtest-all.o
|
||||
|
||||
(We need `-pthread` as Google Test uses threads.)
|
||||
|
||||
Next, you should compile your test source file with
|
||||
`${GTEST_DIR}/include` in the system header search path, and link it
|
||||
with gtest and any other necessary libraries:
|
||||
|
||||
g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \
|
||||
-o your_test
|
||||
|
||||
As an example, the make/ directory contains a Makefile that you can
|
||||
use to build Google Test on systems where GNU make is available
|
||||
(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
|
||||
Test's own tests. Instead, it just builds the Google Test library and
|
||||
a sample test. You can use it as a starting point for your own build
|
||||
script.
|
||||
|
||||
If the default settings are correct for your environment, the
|
||||
following commands should succeed:
|
||||
|
||||
cd ${GTEST_DIR}/make
|
||||
make
|
||||
./sample1_unittest
|
||||
|
||||
If you see errors, try to tweak the contents of `make/Makefile` to make
|
||||
them go away. There are instructions in `make/Makefile` on how to do
|
||||
it.
|
||||
|
||||
### Using CMake ###
|
||||
|
||||
Google Test comes with a CMake build script (
|
||||
[CMakeLists.txt](CMakeLists.txt)) that can be used on a wide range of platforms ("C" stands for
|
||||
cross-platform.). If you don't have CMake installed already, you can
|
||||
download it for free from <http://www.cmake.org/>.
|
||||
|
||||
CMake works by generating native makefiles or build projects that can
|
||||
be used in the compiler environment of your choice. The typical
|
||||
workflow starts with:
|
||||
|
||||
mkdir mybuild # Create a directory to hold the build output.
|
||||
cd mybuild
|
||||
cmake ${GTEST_DIR} # Generate native build scripts.
|
||||
|
||||
If you want to build Google Test's samples, you should replace the
|
||||
last command with
|
||||
|
||||
cmake -Dgtest_build_samples=ON ${GTEST_DIR}
|
||||
|
||||
If you are on a \*nix system, you should now see a Makefile in the
|
||||
current directory. Just type 'make' to build gtest.
|
||||
|
||||
If you use Windows and have Visual Studio installed, a `gtest.sln` file
|
||||
and several `.vcproj` files will be created. You can then build them
|
||||
using Visual Studio.
|
||||
|
||||
On Mac OS X with Xcode installed, a `.xcodeproj` file will be generated.
|
||||
|
||||
### Legacy Build Scripts ###
|
||||
|
||||
Before settling on CMake, we have been providing hand-maintained build
|
||||
projects/scripts for Visual Studio, Xcode, and Autotools. While we
|
||||
continue to provide them for convenience, they are not actively
|
||||
maintained any more. We highly recommend that you follow the
|
||||
instructions in the previous two sections to integrate Google Test
|
||||
with your existing build system.
|
||||
|
||||
If you still need to use the legacy build scripts, here's how:
|
||||
|
||||
The msvc\ folder contains two solutions with Visual C++ projects.
|
||||
Open the `gtest.sln` or `gtest-md.sln` file using Visual Studio, and you
|
||||
are ready to build Google Test the same way you build any Visual
|
||||
Studio project. Files that have names ending with -md use DLL
|
||||
versions of Microsoft runtime libraries (the /MD or the /MDd compiler
|
||||
option). Files without that suffix use static versions of the runtime
|
||||
libraries (the /MT or the /MTd option). Please note that one must use
|
||||
the same option to compile both gtest and the test code. If you use
|
||||
Visual Studio 2005 or above, we recommend the -md version as /MD is
|
||||
the default for new projects in these versions of Visual Studio.
|
||||
|
||||
On Mac OS X, open the `gtest.xcodeproj` in the `xcode/` folder using
|
||||
Xcode. Build the "gtest" target. The universal binary framework will
|
||||
end up in your selected build directory (selected in the Xcode
|
||||
"Preferences..." -> "Building" pane and defaults to xcode/build).
|
||||
Alternatively, at the command line, enter:
|
||||
|
||||
xcodebuild
|
||||
|
||||
This will build the "Release" configuration of gtest.framework in your
|
||||
default build location. See the "xcodebuild" man page for more
|
||||
information about building different configurations and building in
|
||||
different locations.
|
||||
|
||||
If you wish to use the Google Test Xcode project with Xcode 4.x and
|
||||
above, you need to either:
|
||||
|
||||
* update the SDK configuration options in xcode/Config/General.xconfig.
|
||||
Comment options `SDKROOT`, `MACOS_DEPLOYMENT_TARGET`, and `GCC_VERSION`. If
|
||||
you choose this route you lose the ability to target earlier versions
|
||||
of MacOS X.
|
||||
* Install an SDK for an earlier version. This doesn't appear to be
|
||||
supported by Apple, but has been reported to work
|
||||
(http://stackoverflow.com/questions/5378518).
|
||||
|
||||
### Tweaking Google Test ###
|
||||
|
||||
Google Test can be used in diverse environments. The default
|
||||
configuration may not work (or may not work well) out of the box in
|
||||
some environments. However, you can easily tweak Google Test by
|
||||
defining control macros on the compiler command line. Generally,
|
||||
these macros are named like `GTEST_XYZ` and you define them to either 1
|
||||
or 0 to enable or disable a certain feature.
|
||||
|
||||
We list the most frequently used macros below. For a complete list,
|
||||
see file [include/gtest/internal/gtest-port.h](include/gtest/internal/gtest-port.h).
|
||||
|
||||
### Choosing a TR1 Tuple Library ###
|
||||
|
||||
Some Google Test features require the C++ Technical Report 1 (TR1)
|
||||
tuple library, which is not yet available with all compilers. The
|
||||
good news is that Google Test implements a subset of TR1 tuple that's
|
||||
enough for its own need, and will automatically use this when the
|
||||
compiler doesn't provide TR1 tuple.
|
||||
|
||||
Usually you don't need to care about which tuple library Google Test
|
||||
uses. However, if your project already uses TR1 tuple, you need to
|
||||
tell Google Test to use the same TR1 tuple library the rest of your
|
||||
project uses, or the two tuple implementations will clash. To do
|
||||
that, add
|
||||
|
||||
-DGTEST_USE_OWN_TR1_TUPLE=0
|
||||
|
||||
to the compiler flags while compiling Google Test and your tests. If
|
||||
you want to force Google Test to use its own tuple library, just add
|
||||
|
||||
-DGTEST_USE_OWN_TR1_TUPLE=1
|
||||
|
||||
to the compiler flags instead.
|
||||
|
||||
If you don't want Google Test to use tuple at all, add
|
||||
|
||||
-DGTEST_HAS_TR1_TUPLE=0
|
||||
|
||||
and all features using tuple will be disabled.
|
||||
|
||||
### Multi-threaded Tests ###
|
||||
|
||||
Google Test is thread-safe where the pthread library is available.
|
||||
After `#include "gtest/gtest.h"`, you can check the `GTEST_IS_THREADSAFE`
|
||||
macro to see whether this is the case (yes if the macro is `#defined` to
|
||||
1, no if it's undefined.).
|
||||
|
||||
If Google Test doesn't correctly detect whether pthread is available
|
||||
in your environment, you can force it with
|
||||
|
||||
-DGTEST_HAS_PTHREAD=1
|
||||
|
||||
or
|
||||
|
||||
-DGTEST_HAS_PTHREAD=0
|
||||
|
||||
When Google Test uses pthread, you may need to add flags to your
|
||||
compiler and/or linker to select the pthread library, or you'll get
|
||||
link errors. If you use the CMake script or the deprecated Autotools
|
||||
script, this is taken care of for you. If you use your own build
|
||||
script, you'll need to read your compiler and linker's manual to
|
||||
figure out what flags to add.
|
||||
|
||||
### As a Shared Library (DLL) ###
|
||||
|
||||
Google Test is compact, so most users can build and link it as a
|
||||
static library for the simplicity. You can choose to use Google Test
|
||||
as a shared library (known as a DLL on Windows) if you prefer.
|
||||
|
||||
To compile *gtest* as a shared library, add
|
||||
|
||||
-DGTEST_CREATE_SHARED_LIBRARY=1
|
||||
|
||||
to the compiler flags. You'll also need to tell the linker to produce
|
||||
a shared library instead - consult your linker's manual for how to do
|
||||
it.
|
||||
|
||||
To compile your *tests* that use the gtest shared library, add
|
||||
|
||||
-DGTEST_LINKED_AS_SHARED_LIBRARY=1
|
||||
|
||||
to the compiler flags.
|
||||
|
||||
Note: while the above steps aren't technically necessary today when
|
||||
using some compilers (e.g. GCC), they may become necessary in the
|
||||
future, if we decide to improve the speed of loading the library (see
|
||||
<http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are
|
||||
recommended to always add the above flags when using Google Test as a
|
||||
shared library. Otherwise a future release of Google Test may break
|
||||
your build script.
|
||||
|
||||
### Avoiding Macro Name Clashes ###
|
||||
|
||||
In C++, macros don't obey namespaces. Therefore two libraries that
|
||||
both define a macro of the same name will clash if you `#include` both
|
||||
definitions. In case a Google Test macro clashes with another
|
||||
library, you can force Google Test to rename its macro to avoid the
|
||||
conflict.
|
||||
|
||||
Specifically, if both Google Test and some other code define macro
|
||||
FOO, you can add
|
||||
|
||||
-DGTEST_DONT_DEFINE_FOO=1
|
||||
|
||||
to the compiler flags to tell Google Test to change the macro's name
|
||||
from `FOO` to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`,
|
||||
or `TEST`. For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll
|
||||
need to write
|
||||
|
||||
GTEST_TEST(SomeTest, DoesThis) { ... }
|
||||
|
||||
instead of
|
||||
|
||||
TEST(SomeTest, DoesThis) { ... }
|
||||
|
||||
in order to define a test.
|
||||
|
||||
## Developing Google Test ##
|
||||
|
||||
This section discusses how to make your own changes to Google Test.
|
||||
|
||||
### Testing Google Test Itself ###
|
||||
|
||||
To make sure your changes work as intended and don't break existing
|
||||
functionality, you'll want to compile and run Google Test's own tests.
|
||||
For that you can use CMake:
|
||||
|
||||
mkdir mybuild
|
||||
cd mybuild
|
||||
cmake -Dgtest_build_tests=ON ${GTEST_DIR}
|
||||
|
||||
Make sure you have Python installed, as some of Google Test's tests
|
||||
are written in Python. If the cmake command complains about not being
|
||||
able to find Python (`Could NOT find PythonInterp (missing:
|
||||
PYTHON_EXECUTABLE)`), try telling it explicitly where your Python
|
||||
executable can be found:
|
||||
|
||||
cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}
|
||||
|
||||
Next, you can build Google Test and all of its own tests. On \*nix,
|
||||
this is usually done by 'make'. To run the tests, do
|
||||
|
||||
make test
|
||||
|
||||
All tests should pass.
|
||||
|
||||
Normally you don't need to worry about regenerating the source files,
|
||||
unless you need to modify them. In that case, you should modify the
|
||||
corresponding .pump files instead and run the pump.py Python script to
|
||||
regenerate them. You can find pump.py in the [scripts/](scripts/) directory.
|
||||
Read the [Pump manual](docs/PumpManual.md) for how to use it.
|
||||
294
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-death-test.h
vendored
Normal file
294
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-death-test.h
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
// Copyright 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
//
|
||||
// The Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// This header file defines the public API for death tests. It is
|
||||
// #included by gtest.h so a user doesn't need to include this
|
||||
// directly.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
|
||||
|
||||
#include "gtest/internal/gtest-death-test-internal.h"
|
||||
|
||||
namespace testing {
|
||||
|
||||
// This flag controls the style of death tests. Valid values are "threadsafe",
|
||||
// meaning that the death test child process will re-execute the test binary
|
||||
// from the start, running only a single death test, or "fast",
|
||||
// meaning that the child process will execute the test logic immediately
|
||||
// after forking.
|
||||
GTEST_DECLARE_string_(death_test_style);
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Returns a Boolean value indicating whether the caller is currently
|
||||
// executing in the context of the death test child process. Tools such as
|
||||
// Valgrind heap checkers may need this to modify their behavior in death
|
||||
// tests. IMPORTANT: This is an internal utility. Using it may break the
|
||||
// implementation of death tests. User code MUST NOT use it.
|
||||
GTEST_API_ bool InDeathTestChild();
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// The following macros are useful for writing death tests.
|
||||
|
||||
// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
|
||||
// executed:
|
||||
//
|
||||
// 1. It generates a warning if there is more than one active
|
||||
// thread. This is because it's safe to fork() or clone() only
|
||||
// when there is a single thread.
|
||||
//
|
||||
// 2. The parent process clone()s a sub-process and runs the death
|
||||
// test in it; the sub-process exits with code 0 at the end of the
|
||||
// death test, if it hasn't exited already.
|
||||
//
|
||||
// 3. The parent process waits for the sub-process to terminate.
|
||||
//
|
||||
// 4. The parent process checks the exit code and error message of
|
||||
// the sub-process.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
|
||||
// for (int i = 0; i < 5; i++) {
|
||||
// EXPECT_DEATH(server.ProcessRequest(i),
|
||||
// "Invalid request .* in ProcessRequest()")
|
||||
// << "Failed to die on request " << i;
|
||||
// }
|
||||
//
|
||||
// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
|
||||
//
|
||||
// bool KilledBySIGHUP(int exit_code) {
|
||||
// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
|
||||
// }
|
||||
//
|
||||
// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
|
||||
//
|
||||
// On the regular expressions used in death tests:
|
||||
//
|
||||
// On POSIX-compliant systems (*nix), we use the <regex.h> library,
|
||||
// which uses the POSIX extended regex syntax.
|
||||
//
|
||||
// On other platforms (e.g. Windows), we only support a simple regex
|
||||
// syntax implemented as part of Google Test. This limited
|
||||
// implementation should be enough most of the time when writing
|
||||
// death tests; though it lacks many features you can find in PCRE
|
||||
// or POSIX extended regex syntax. For example, we don't support
|
||||
// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
|
||||
// repetition count ("x{5,7}"), among others.
|
||||
//
|
||||
// Below is the syntax that we do support. We chose it to be a
|
||||
// subset of both PCRE and POSIX extended regex, so it's easy to
|
||||
// learn wherever you come from. In the following: 'A' denotes a
|
||||
// literal character, period (.), or a single \\ escape sequence;
|
||||
// 'x' and 'y' denote regular expressions; 'm' and 'n' are for
|
||||
// natural numbers.
|
||||
//
|
||||
// c matches any literal character c
|
||||
// \\d matches any decimal digit
|
||||
// \\D matches any character that's not a decimal digit
|
||||
// \\f matches \f
|
||||
// \\n matches \n
|
||||
// \\r matches \r
|
||||
// \\s matches any ASCII whitespace, including \n
|
||||
// \\S matches any character that's not a whitespace
|
||||
// \\t matches \t
|
||||
// \\v matches \v
|
||||
// \\w matches any letter, _, or decimal digit
|
||||
// \\W matches any character that \\w doesn't match
|
||||
// \\c matches any literal character c, which must be a punctuation
|
||||
// . matches any single character except \n
|
||||
// A? matches 0 or 1 occurrences of A
|
||||
// A* matches 0 or many occurrences of A
|
||||
// A+ matches 1 or many occurrences of A
|
||||
// ^ matches the beginning of a string (not that of each line)
|
||||
// $ matches the end of a string (not that of each line)
|
||||
// xy matches x followed by y
|
||||
//
|
||||
// If you accidentally use PCRE or POSIX extended regex features
|
||||
// not implemented by us, you will get a run-time failure. In that
|
||||
// case, please try to rewrite your regular expression within the
|
||||
// above syntax.
|
||||
//
|
||||
// This implementation is *not* meant to be as highly tuned or robust
|
||||
// as a compiled regex library, but should perform well enough for a
|
||||
// death test, which already incurs significant overhead by launching
|
||||
// a child process.
|
||||
//
|
||||
// Known caveats:
|
||||
//
|
||||
// A "threadsafe" style death test obtains the path to the test
|
||||
// program from argv[0] and re-executes it in the sub-process. For
|
||||
// simplicity, the current implementation doesn't search the PATH
|
||||
// when launching the sub-process. This means that the user must
|
||||
// invoke the test program via a path that contains at least one
|
||||
// path separator (e.g. path/to/foo_test and
|
||||
// /absolute/path/to/bar_test are fine, but foo_test is not). This
|
||||
// is rarely a problem as people usually don't put the test binary
|
||||
// directory in PATH.
|
||||
//
|
||||
// TODO(wan@google.com): make thread-safe death tests search the PATH.
|
||||
|
||||
// Asserts that a given statement causes the program to exit, with an
|
||||
// integer exit status that satisfies predicate, and emitting error output
|
||||
// that matches regex.
|
||||
# define ASSERT_EXIT(statement, predicate, regex) \
|
||||
GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
|
||||
|
||||
// Like ASSERT_EXIT, but continues on to successive tests in the
|
||||
// test case, if any:
|
||||
# define EXPECT_EXIT(statement, predicate, regex) \
|
||||
GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
|
||||
|
||||
// Asserts that a given statement causes the program to exit, either by
|
||||
// explicitly exiting with a nonzero exit code or being killed by a
|
||||
// signal, and emitting error output that matches regex.
|
||||
# define ASSERT_DEATH(statement, regex) \
|
||||
ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
|
||||
|
||||
// Like ASSERT_DEATH, but continues on to successive tests in the
|
||||
// test case, if any:
|
||||
# define EXPECT_DEATH(statement, regex) \
|
||||
EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
|
||||
|
||||
// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
|
||||
|
||||
// Tests that an exit code describes a normal exit with a given exit code.
|
||||
class GTEST_API_ ExitedWithCode {
|
||||
public:
|
||||
explicit ExitedWithCode(int exit_code);
|
||||
bool operator()(int exit_status) const;
|
||||
private:
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const ExitedWithCode& other);
|
||||
|
||||
const int exit_code_;
|
||||
};
|
||||
|
||||
# if !GTEST_OS_WINDOWS
|
||||
// Tests that an exit code describes an exit due to termination by a
|
||||
// given signal.
|
||||
class GTEST_API_ KilledBySignal {
|
||||
public:
|
||||
explicit KilledBySignal(int signum);
|
||||
bool operator()(int exit_status) const;
|
||||
private:
|
||||
const int signum_;
|
||||
};
|
||||
# endif // !GTEST_OS_WINDOWS
|
||||
|
||||
// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
|
||||
// The death testing framework causes this to have interesting semantics,
|
||||
// since the sideeffects of the call are only visible in opt mode, and not
|
||||
// in debug mode.
|
||||
//
|
||||
// In practice, this can be used to test functions that utilize the
|
||||
// LOG(DFATAL) macro using the following style:
|
||||
//
|
||||
// int DieInDebugOr12(int* sideeffect) {
|
||||
// if (sideeffect) {
|
||||
// *sideeffect = 12;
|
||||
// }
|
||||
// LOG(DFATAL) << "death";
|
||||
// return 12;
|
||||
// }
|
||||
//
|
||||
// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
|
||||
// int sideeffect = 0;
|
||||
// // Only asserts in dbg.
|
||||
// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
|
||||
//
|
||||
// #ifdef NDEBUG
|
||||
// // opt-mode has sideeffect visible.
|
||||
// EXPECT_EQ(12, sideeffect);
|
||||
// #else
|
||||
// // dbg-mode no visible sideeffect.
|
||||
// EXPECT_EQ(0, sideeffect);
|
||||
// #endif
|
||||
// }
|
||||
//
|
||||
// This will assert that DieInDebugReturn12InOpt() crashes in debug
|
||||
// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
|
||||
// appropriate fallback value (12 in this case) in opt mode. If you
|
||||
// need to test that a function has appropriate side-effects in opt
|
||||
// mode, include assertions against the side-effects. A general
|
||||
// pattern for this is:
|
||||
//
|
||||
// EXPECT_DEBUG_DEATH({
|
||||
// // Side-effects here will have an effect after this statement in
|
||||
// // opt mode, but none in debug mode.
|
||||
// EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
|
||||
// }, "death");
|
||||
//
|
||||
# ifdef NDEBUG
|
||||
|
||||
# define EXPECT_DEBUG_DEATH(statement, regex) \
|
||||
GTEST_EXECUTE_STATEMENT_(statement, regex)
|
||||
|
||||
# define ASSERT_DEBUG_DEATH(statement, regex) \
|
||||
GTEST_EXECUTE_STATEMENT_(statement, regex)
|
||||
|
||||
# else
|
||||
|
||||
# define EXPECT_DEBUG_DEATH(statement, regex) \
|
||||
EXPECT_DEATH(statement, regex)
|
||||
|
||||
# define ASSERT_DEBUG_DEATH(statement, regex) \
|
||||
ASSERT_DEATH(statement, regex)
|
||||
|
||||
# endif // NDEBUG for EXPECT_DEBUG_DEATH
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
|
||||
// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
|
||||
// death tests are supported; otherwise they just issue a warning. This is
|
||||
// useful when you are combining death test assertions with normal test
|
||||
// assertions in one test.
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
EXPECT_DEATH(statement, regex)
|
||||
# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
ASSERT_DEATH(statement, regex)
|
||||
#else
|
||||
# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
|
||||
# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
|
||||
#endif
|
||||
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
|
||||
250
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-message.h
vendored
Normal file
250
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-message.h
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
//
|
||||
// The Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// This header file defines the Message class.
|
||||
//
|
||||
// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
|
||||
// leave some internal implementation details in this header file.
|
||||
// They are clearly marked by comments like this:
|
||||
//
|
||||
// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||
//
|
||||
// Such code is NOT meant to be used by a user directly, and is subject
|
||||
// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
|
||||
// program!
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
// Ensures that there is at least one operator<< in the global namespace.
|
||||
// See Message& operator<<(...) below for why.
|
||||
void operator<<(const testing::internal::Secret&, int);
|
||||
|
||||
namespace testing {
|
||||
|
||||
// The Message class works like an ostream repeater.
|
||||
//
|
||||
// Typical usage:
|
||||
//
|
||||
// 1. You stream a bunch of values to a Message object.
|
||||
// It will remember the text in a stringstream.
|
||||
// 2. Then you stream the Message object to an ostream.
|
||||
// This causes the text in the Message to be streamed
|
||||
// to the ostream.
|
||||
//
|
||||
// For example;
|
||||
//
|
||||
// testing::Message foo;
|
||||
// foo << 1 << " != " << 2;
|
||||
// std::cout << foo;
|
||||
//
|
||||
// will print "1 != 2".
|
||||
//
|
||||
// Message is not intended to be inherited from. In particular, its
|
||||
// destructor is not virtual.
|
||||
//
|
||||
// Note that stringstream behaves differently in gcc and in MSVC. You
|
||||
// can stream a NULL char pointer to it in the former, but not in the
|
||||
// latter (it causes an access violation if you do). The Message
|
||||
// class hides this difference by treating a NULL char pointer as
|
||||
// "(null)".
|
||||
class GTEST_API_ Message {
|
||||
private:
|
||||
// The type of basic IO manipulators (endl, ends, and flush) for
|
||||
// narrow streams.
|
||||
typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
|
||||
|
||||
public:
|
||||
// Constructs an empty Message.
|
||||
Message();
|
||||
|
||||
// Copy constructor.
|
||||
Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT
|
||||
*ss_ << msg.GetString();
|
||||
}
|
||||
|
||||
// Constructs a Message from a C-string.
|
||||
explicit Message(const char* str) : ss_(new ::std::stringstream) {
|
||||
*ss_ << str;
|
||||
}
|
||||
|
||||
#if GTEST_OS_SYMBIAN
|
||||
// Streams a value (either a pointer or not) to this object.
|
||||
template <typename T>
|
||||
inline Message& operator <<(const T& value) {
|
||||
StreamHelper(typename internal::is_pointer<T>::type(), value);
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
// Streams a non-pointer value to this object.
|
||||
template <typename T>
|
||||
inline Message& operator <<(const T& val) {
|
||||
// Some libraries overload << for STL containers. These
|
||||
// overloads are defined in the global namespace instead of ::std.
|
||||
//
|
||||
// C++'s symbol lookup rule (i.e. Koenig lookup) says that these
|
||||
// overloads are visible in either the std namespace or the global
|
||||
// namespace, but not other namespaces, including the testing
|
||||
// namespace which Google Test's Message class is in.
|
||||
//
|
||||
// To allow STL containers (and other types that has a << operator
|
||||
// defined in the global namespace) to be used in Google Test
|
||||
// assertions, testing::Message must access the custom << operator
|
||||
// from the global namespace. With this using declaration,
|
||||
// overloads of << defined in the global namespace and those
|
||||
// visible via Koenig lookup are both exposed in this function.
|
||||
using ::operator <<;
|
||||
*ss_ << val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Streams a pointer value to this object.
|
||||
//
|
||||
// This function is an overload of the previous one. When you
|
||||
// stream a pointer to a Message, this definition will be used as it
|
||||
// is more specialized. (The C++ Standard, section
|
||||
// [temp.func.order].) If you stream a non-pointer, then the
|
||||
// previous definition will be used.
|
||||
//
|
||||
// The reason for this overload is that streaming a NULL pointer to
|
||||
// ostream is undefined behavior. Depending on the compiler, you
|
||||
// may get "0", "(nil)", "(null)", or an access violation. To
|
||||
// ensure consistent result across compilers, we always treat NULL
|
||||
// as "(null)".
|
||||
template <typename T>
|
||||
inline Message& operator <<(T* const& pointer) { // NOLINT
|
||||
if (pointer == NULL) {
|
||||
*ss_ << "(null)";
|
||||
} else {
|
||||
*ss_ << pointer;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif // GTEST_OS_SYMBIAN
|
||||
|
||||
// Since the basic IO manipulators are overloaded for both narrow
|
||||
// and wide streams, we have to provide this specialized definition
|
||||
// of operator <<, even though its body is the same as the
|
||||
// templatized version above. Without this definition, streaming
|
||||
// endl or other basic IO manipulators to Message will confuse the
|
||||
// compiler.
|
||||
Message& operator <<(BasicNarrowIoManip val) {
|
||||
*ss_ << val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Instead of 1/0, we want to see true/false for bool values.
|
||||
Message& operator <<(bool b) {
|
||||
return *this << (b ? "true" : "false");
|
||||
}
|
||||
|
||||
// These two overloads allow streaming a wide C string to a Message
|
||||
// using the UTF-8 encoding.
|
||||
Message& operator <<(const wchar_t* wide_c_str);
|
||||
Message& operator <<(wchar_t* wide_c_str);
|
||||
|
||||
#if GTEST_HAS_STD_WSTRING
|
||||
// Converts the given wide string to a narrow string using the UTF-8
|
||||
// encoding, and streams the result to this Message object.
|
||||
Message& operator <<(const ::std::wstring& wstr);
|
||||
#endif // GTEST_HAS_STD_WSTRING
|
||||
|
||||
#if GTEST_HAS_GLOBAL_WSTRING
|
||||
// Converts the given wide string to a narrow string using the UTF-8
|
||||
// encoding, and streams the result to this Message object.
|
||||
Message& operator <<(const ::wstring& wstr);
|
||||
#endif // GTEST_HAS_GLOBAL_WSTRING
|
||||
|
||||
// Gets the text streamed to this object so far as an std::string.
|
||||
// Each '\0' character in the buffer is replaced with "\\0".
|
||||
//
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||
std::string GetString() const;
|
||||
|
||||
private:
|
||||
|
||||
#if GTEST_OS_SYMBIAN
|
||||
// These are needed as the Nokia Symbian Compiler cannot decide between
|
||||
// const T& and const T* in a function template. The Nokia compiler _can_
|
||||
// decide between class template specializations for T and T*, so a
|
||||
// tr1::type_traits-like is_pointer works, and we can overload on that.
|
||||
template <typename T>
|
||||
inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) {
|
||||
if (pointer == NULL) {
|
||||
*ss_ << "(null)";
|
||||
} else {
|
||||
*ss_ << pointer;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
inline void StreamHelper(internal::false_type /*is_pointer*/,
|
||||
const T& value) {
|
||||
// See the comments in Message& operator <<(const T&) above for why
|
||||
// we need this using statement.
|
||||
using ::operator <<;
|
||||
*ss_ << value;
|
||||
}
|
||||
#endif // GTEST_OS_SYMBIAN
|
||||
|
||||
// We'll hold the text streamed to this object here.
|
||||
const internal::scoped_ptr< ::std::stringstream> ss_;
|
||||
|
||||
// We declare (but don't implement) this to prevent the compiler
|
||||
// from implementing the assignment operator.
|
||||
void operator=(const Message&);
|
||||
};
|
||||
|
||||
// Streams a Message to an ostream.
|
||||
inline std::ostream& operator <<(std::ostream& os, const Message& sb) {
|
||||
return os << sb.GetString();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Converts a streamable value to an std::string. A NULL pointer is
|
||||
// converted to "(null)". When the input value is a ::string,
|
||||
// ::std::string, ::wstring, or ::std::wstring object, each NUL
|
||||
// character in it is replaced with "\\0".
|
||||
template <typename T>
|
||||
std::string StreamableToString(const T& streamable) {
|
||||
return (Message() << streamable).GetString();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
|
||||
1444
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-param-test.h
vendored
Normal file
1444
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-param-test.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
510
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-param-test.h.pump
vendored
Normal file
510
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-param-test.h.pump
vendored
Normal file
@@ -0,0 +1,510 @@
|
||||
$$ -*- mode: c++; -*-
|
||||
$var n = 50 $$ Maximum length of Values arguments we want to support.
|
||||
$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support.
|
||||
// Copyright 2008, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Authors: vladl@google.com (Vlad Losev)
|
||||
//
|
||||
// Macros and functions for implementing parameterized tests
|
||||
// in Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
|
||||
//
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
|
||||
|
||||
|
||||
// Value-parameterized tests allow you to test your code with different
|
||||
// parameters without writing multiple copies of the same test.
|
||||
//
|
||||
// Here is how you use value-parameterized tests:
|
||||
|
||||
#if 0
|
||||
|
||||
// To write value-parameterized tests, first you should define a fixture
|
||||
// class. It is usually derived from testing::TestWithParam<T> (see below for
|
||||
// another inheritance scheme that's sometimes useful in more complicated
|
||||
// class hierarchies), where the type of your parameter values.
|
||||
// TestWithParam<T> is itself derived from testing::Test. T can be any
|
||||
// copyable type. If it's a raw pointer, you are responsible for managing the
|
||||
// lifespan of the pointed values.
|
||||
|
||||
class FooTest : public ::testing::TestWithParam<const char*> {
|
||||
// You can implement all the usual class fixture members here.
|
||||
};
|
||||
|
||||
// Then, use the TEST_P macro to define as many parameterized tests
|
||||
// for this fixture as you want. The _P suffix is for "parameterized"
|
||||
// or "pattern", whichever you prefer to think.
|
||||
|
||||
TEST_P(FooTest, DoesBlah) {
|
||||
// Inside a test, access the test parameter with the GetParam() method
|
||||
// of the TestWithParam<T> class:
|
||||
EXPECT_TRUE(foo.Blah(GetParam()));
|
||||
...
|
||||
}
|
||||
|
||||
TEST_P(FooTest, HasBlahBlah) {
|
||||
...
|
||||
}
|
||||
|
||||
// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
|
||||
// case with any set of parameters you want. Google Test defines a number
|
||||
// of functions for generating test parameters. They return what we call
|
||||
// (surprise!) parameter generators. Here is a summary of them, which
|
||||
// are all in the testing namespace:
|
||||
//
|
||||
//
|
||||
// Range(begin, end [, step]) - Yields values {begin, begin+step,
|
||||
// begin+step+step, ...}. The values do not
|
||||
// include end. step defaults to 1.
|
||||
// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}.
|
||||
// ValuesIn(container) - Yields values from a C-style array, an STL
|
||||
// ValuesIn(begin,end) container, or an iterator range [begin, end).
|
||||
// Bool() - Yields sequence {false, true}.
|
||||
// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product
|
||||
// for the math savvy) of the values generated
|
||||
// by the N generators.
|
||||
//
|
||||
// For more details, see comments at the definitions of these functions below
|
||||
// in this file.
|
||||
//
|
||||
// The following statement will instantiate tests from the FooTest test case
|
||||
// each with parameter values "meeny", "miny", and "moe".
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InstantiationName,
|
||||
FooTest,
|
||||
Values("meeny", "miny", "moe"));
|
||||
|
||||
// To distinguish different instances of the pattern, (yes, you
|
||||
// can instantiate it more then once) the first argument to the
|
||||
// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
|
||||
// actual test case name. Remember to pick unique prefixes for different
|
||||
// instantiations. The tests from the instantiation above will have
|
||||
// these names:
|
||||
//
|
||||
// * InstantiationName/FooTest.DoesBlah/0 for "meeny"
|
||||
// * InstantiationName/FooTest.DoesBlah/1 for "miny"
|
||||
// * InstantiationName/FooTest.DoesBlah/2 for "moe"
|
||||
// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
|
||||
// * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
|
||||
// * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
|
||||
//
|
||||
// You can use these names in --gtest_filter.
|
||||
//
|
||||
// This statement will instantiate all tests from FooTest again, each
|
||||
// with parameter values "cat" and "dog":
|
||||
|
||||
const char* pets[] = {"cat", "dog"};
|
||||
INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
|
||||
|
||||
// The tests from the instantiation above will have these names:
|
||||
//
|
||||
// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
|
||||
// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
|
||||
// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
|
||||
// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
|
||||
//
|
||||
// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
|
||||
// in the given test case, whether their definitions come before or
|
||||
// AFTER the INSTANTIATE_TEST_CASE_P statement.
|
||||
//
|
||||
// Please also note that generator expressions (including parameters to the
|
||||
// generators) are evaluated in InitGoogleTest(), after main() has started.
|
||||
// This allows the user on one hand, to adjust generator parameters in order
|
||||
// to dynamically determine a set of tests to run and on the other hand,
|
||||
// give the user a chance to inspect the generated tests with Google Test
|
||||
// reflection API before RUN_ALL_TESTS() is executed.
|
||||
//
|
||||
// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
|
||||
// for more examples.
|
||||
//
|
||||
// In the future, we plan to publish the API for defining new parameter
|
||||
// generators. But for now this interface remains part of the internal
|
||||
// implementation and is subject to change.
|
||||
//
|
||||
//
|
||||
// A parameterized test fixture must be derived from testing::Test and from
|
||||
// testing::WithParamInterface<T>, where T is the type of the parameter
|
||||
// values. Inheriting from TestWithParam<T> satisfies that requirement because
|
||||
// TestWithParam<T> inherits from both Test and WithParamInterface. In more
|
||||
// complicated hierarchies, however, it is occasionally useful to inherit
|
||||
// separately from Test and WithParamInterface. For example:
|
||||
|
||||
class BaseTest : public ::testing::Test {
|
||||
// You can inherit all the usual members for a non-parameterized test
|
||||
// fixture here.
|
||||
};
|
||||
|
||||
class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
|
||||
// The usual test fixture members go here too.
|
||||
};
|
||||
|
||||
TEST_F(BaseTest, HasFoo) {
|
||||
// This is an ordinary non-parameterized test.
|
||||
}
|
||||
|
||||
TEST_P(DerivedTest, DoesBlah) {
|
||||
// GetParam works just the same here as if you inherit from TestWithParam.
|
||||
EXPECT_TRUE(foo.Blah(GetParam()));
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
#if !GTEST_OS_SYMBIAN
|
||||
# include <utility>
|
||||
#endif
|
||||
|
||||
// scripts/fuse_gtest.py depends on gtest's own header being #included
|
||||
// *unconditionally*. Therefore these #includes cannot be moved
|
||||
// inside #if GTEST_HAS_PARAM_TEST.
|
||||
#include "gtest/internal/gtest-internal.h"
|
||||
#include "gtest/internal/gtest-param-util.h"
|
||||
#include "gtest/internal/gtest-param-util-generated.h"
|
||||
|
||||
#if GTEST_HAS_PARAM_TEST
|
||||
|
||||
namespace testing {
|
||||
|
||||
// Functions producing parameter generators.
|
||||
//
|
||||
// Google Test uses these generators to produce parameters for value-
|
||||
// parameterized tests. When a parameterized test case is instantiated
|
||||
// with a particular generator, Google Test creates and runs tests
|
||||
// for each element in the sequence produced by the generator.
|
||||
//
|
||||
// In the following sample, tests from test case FooTest are instantiated
|
||||
// each three times with parameter values 3, 5, and 8:
|
||||
//
|
||||
// class FooTest : public TestWithParam<int> { ... };
|
||||
//
|
||||
// TEST_P(FooTest, TestThis) {
|
||||
// }
|
||||
// TEST_P(FooTest, TestThat) {
|
||||
// }
|
||||
// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
|
||||
//
|
||||
|
||||
// Range() returns generators providing sequences of values in a range.
|
||||
//
|
||||
// Synopsis:
|
||||
// Range(start, end)
|
||||
// - returns a generator producing a sequence of values {start, start+1,
|
||||
// start+2, ..., }.
|
||||
// Range(start, end, step)
|
||||
// - returns a generator producing a sequence of values {start, start+step,
|
||||
// start+step+step, ..., }.
|
||||
// Notes:
|
||||
// * The generated sequences never include end. For example, Range(1, 5)
|
||||
// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
|
||||
// returns a generator producing {1, 3, 5, 7}.
|
||||
// * start and end must have the same type. That type may be any integral or
|
||||
// floating-point type or a user defined type satisfying these conditions:
|
||||
// * It must be assignable (have operator=() defined).
|
||||
// * It must have operator+() (operator+(int-compatible type) for
|
||||
// two-operand version).
|
||||
// * It must have operator<() defined.
|
||||
// Elements in the resulting sequences will also have that type.
|
||||
// * Condition start < end must be satisfied in order for resulting sequences
|
||||
// to contain any elements.
|
||||
//
|
||||
template <typename T, typename IncrementT>
|
||||
internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
|
||||
return internal::ParamGenerator<T>(
|
||||
new internal::RangeGenerator<T, IncrementT>(start, end, step));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
internal::ParamGenerator<T> Range(T start, T end) {
|
||||
return Range(start, end, 1);
|
||||
}
|
||||
|
||||
// ValuesIn() function allows generation of tests with parameters coming from
|
||||
// a container.
|
||||
//
|
||||
// Synopsis:
|
||||
// ValuesIn(const T (&array)[N])
|
||||
// - returns a generator producing sequences with elements from
|
||||
// a C-style array.
|
||||
// ValuesIn(const Container& container)
|
||||
// - returns a generator producing sequences with elements from
|
||||
// an STL-style container.
|
||||
// ValuesIn(Iterator begin, Iterator end)
|
||||
// - returns a generator producing sequences with elements from
|
||||
// a range [begin, end) defined by a pair of STL-style iterators. These
|
||||
// iterators can also be plain C pointers.
|
||||
//
|
||||
// Please note that ValuesIn copies the values from the containers
|
||||
// passed in and keeps them to generate tests in RUN_ALL_TESTS().
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// This instantiates tests from test case StringTest
|
||||
// each with C-string values of "foo", "bar", and "baz":
|
||||
//
|
||||
// const char* strings[] = {"foo", "bar", "baz"};
|
||||
// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
|
||||
//
|
||||
// This instantiates tests from test case StlStringTest
|
||||
// each with STL strings with values "a" and "b":
|
||||
//
|
||||
// ::std::vector< ::std::string> GetParameterStrings() {
|
||||
// ::std::vector< ::std::string> v;
|
||||
// v.push_back("a");
|
||||
// v.push_back("b");
|
||||
// return v;
|
||||
// }
|
||||
//
|
||||
// INSTANTIATE_TEST_CASE_P(CharSequence,
|
||||
// StlStringTest,
|
||||
// ValuesIn(GetParameterStrings()));
|
||||
//
|
||||
//
|
||||
// This will also instantiate tests from CharTest
|
||||
// each with parameter values 'a' and 'b':
|
||||
//
|
||||
// ::std::list<char> GetParameterChars() {
|
||||
// ::std::list<char> list;
|
||||
// list.push_back('a');
|
||||
// list.push_back('b');
|
||||
// return list;
|
||||
// }
|
||||
// ::std::list<char> l = GetParameterChars();
|
||||
// INSTANTIATE_TEST_CASE_P(CharSequence2,
|
||||
// CharTest,
|
||||
// ValuesIn(l.begin(), l.end()));
|
||||
//
|
||||
template <typename ForwardIterator>
|
||||
internal::ParamGenerator<
|
||||
typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
|
||||
ValuesIn(ForwardIterator begin, ForwardIterator end) {
|
||||
typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
|
||||
::value_type ParamType;
|
||||
return internal::ParamGenerator<ParamType>(
|
||||
new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
|
||||
return ValuesIn(array, array + N);
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
internal::ParamGenerator<typename Container::value_type> ValuesIn(
|
||||
const Container& container) {
|
||||
return ValuesIn(container.begin(), container.end());
|
||||
}
|
||||
|
||||
// Values() allows generating tests from explicitly specified list of
|
||||
// parameters.
|
||||
//
|
||||
// Synopsis:
|
||||
// Values(T v1, T v2, ..., T vN)
|
||||
// - returns a generator producing sequences with elements v1, v2, ..., vN.
|
||||
//
|
||||
// For example, this instantiates tests from test case BarTest each
|
||||
// with values "one", "two", and "three":
|
||||
//
|
||||
// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
|
||||
//
|
||||
// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
|
||||
// The exact type of values will depend on the type of parameter in BazTest.
|
||||
//
|
||||
// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
|
||||
//
|
||||
// Currently, Values() supports from 1 to $n parameters.
|
||||
//
|
||||
$range i 1..n
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
|
||||
template <$for j, [[typename T$j]]>
|
||||
internal::ValueArray$i<$for j, [[T$j]]> Values($for j, [[T$j v$j]]) {
|
||||
return internal::ValueArray$i<$for j, [[T$j]]>($for j, [[v$j]]);
|
||||
}
|
||||
|
||||
]]
|
||||
|
||||
// Bool() allows generating tests with parameters in a set of (false, true).
|
||||
//
|
||||
// Synopsis:
|
||||
// Bool()
|
||||
// - returns a generator producing sequences with elements {false, true}.
|
||||
//
|
||||
// It is useful when testing code that depends on Boolean flags. Combinations
|
||||
// of multiple flags can be tested when several Bool()'s are combined using
|
||||
// Combine() function.
|
||||
//
|
||||
// In the following example all tests in the test case FlagDependentTest
|
||||
// will be instantiated twice with parameters false and true.
|
||||
//
|
||||
// class FlagDependentTest : public testing::TestWithParam<bool> {
|
||||
// virtual void SetUp() {
|
||||
// external_flag = GetParam();
|
||||
// }
|
||||
// }
|
||||
// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
|
||||
//
|
||||
inline internal::ParamGenerator<bool> Bool() {
|
||||
return Values(false, true);
|
||||
}
|
||||
|
||||
# if GTEST_HAS_COMBINE
|
||||
// Combine() allows the user to combine two or more sequences to produce
|
||||
// values of a Cartesian product of those sequences' elements.
|
||||
//
|
||||
// Synopsis:
|
||||
// Combine(gen1, gen2, ..., genN)
|
||||
// - returns a generator producing sequences with elements coming from
|
||||
// the Cartesian product of elements from the sequences generated by
|
||||
// gen1, gen2, ..., genN. The sequence elements will have a type of
|
||||
// tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
|
||||
// of elements from sequences produces by gen1, gen2, ..., genN.
|
||||
//
|
||||
// Combine can have up to $maxtuple arguments. This number is currently limited
|
||||
// by the maximum number of elements in the tuple implementation used by Google
|
||||
// Test.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// This will instantiate tests in test case AnimalTest each one with
|
||||
// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
|
||||
// tuple("dog", BLACK), and tuple("dog", WHITE):
|
||||
//
|
||||
// enum Color { BLACK, GRAY, WHITE };
|
||||
// class AnimalTest
|
||||
// : public testing::TestWithParam<tuple<const char*, Color> > {...};
|
||||
//
|
||||
// TEST_P(AnimalTest, AnimalLooksNice) {...}
|
||||
//
|
||||
// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
|
||||
// Combine(Values("cat", "dog"),
|
||||
// Values(BLACK, WHITE)));
|
||||
//
|
||||
// This will instantiate tests in FlagDependentTest with all variations of two
|
||||
// Boolean flags:
|
||||
//
|
||||
// class FlagDependentTest
|
||||
// : public testing::TestWithParam<tuple<bool, bool> > {
|
||||
// virtual void SetUp() {
|
||||
// // Assigns external_flag_1 and external_flag_2 values from the tuple.
|
||||
// tie(external_flag_1, external_flag_2) = GetParam();
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// TEST_P(FlagDependentTest, TestFeature1) {
|
||||
// // Test your code using external_flag_1 and external_flag_2 here.
|
||||
// }
|
||||
// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
|
||||
// Combine(Bool(), Bool()));
|
||||
//
|
||||
$range i 2..maxtuple
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
|
||||
template <$for j, [[typename Generator$j]]>
|
||||
internal::CartesianProductHolder$i<$for j, [[Generator$j]]> Combine(
|
||||
$for j, [[const Generator$j& g$j]]) {
|
||||
return internal::CartesianProductHolder$i<$for j, [[Generator$j]]>(
|
||||
$for j, [[g$j]]);
|
||||
}
|
||||
|
||||
]]
|
||||
# endif // GTEST_HAS_COMBINE
|
||||
|
||||
|
||||
|
||||
# define TEST_P(test_case_name, test_name) \
|
||||
class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
|
||||
: public test_case_name { \
|
||||
public: \
|
||||
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
|
||||
virtual void TestBody(); \
|
||||
private: \
|
||||
static int AddToRegistry() { \
|
||||
::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
|
||||
GetTestCasePatternHolder<test_case_name>(\
|
||||
#test_case_name, \
|
||||
::testing::internal::CodeLocation(\
|
||||
__FILE__, __LINE__))->AddTestPattern(\
|
||||
#test_case_name, \
|
||||
#test_name, \
|
||||
new ::testing::internal::TestMetaFactory< \
|
||||
GTEST_TEST_CLASS_NAME_(\
|
||||
test_case_name, test_name)>()); \
|
||||
return 0; \
|
||||
} \
|
||||
static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(\
|
||||
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
|
||||
}; \
|
||||
int GTEST_TEST_CLASS_NAME_(test_case_name, \
|
||||
test_name)::gtest_registering_dummy_ = \
|
||||
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
|
||||
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
|
||||
|
||||
// The optional last argument to INSTANTIATE_TEST_CASE_P allows the user
|
||||
// to specify a function or functor that generates custom test name suffixes
|
||||
// based on the test parameters. The function should accept one argument of
|
||||
// type testing::TestParamInfo<class ParamType>, and return std::string.
|
||||
//
|
||||
// testing::PrintToStringParamName is a builtin test suffix generator that
|
||||
// returns the value of testing::PrintToString(GetParam()).
|
||||
//
|
||||
// Note: test names must be non-empty, unique, and may only contain ASCII
|
||||
// alphanumeric characters or underscore. Because PrintToString adds quotes
|
||||
// to std::string and C strings, it won't work for these types.
|
||||
|
||||
# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \
|
||||
::testing::internal::ParamGenerator<test_case_name::ParamType> \
|
||||
gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
|
||||
::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \
|
||||
const ::testing::TestParamInfo<test_case_name::ParamType>& info) { \
|
||||
return ::testing::internal::GetParamNameGen<test_case_name::ParamType> \
|
||||
(__VA_ARGS__)(info); \
|
||||
} \
|
||||
int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \
|
||||
::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
|
||||
GetTestCasePatternHolder<test_case_name>(\
|
||||
#test_case_name, \
|
||||
::testing::internal::CodeLocation(\
|
||||
__FILE__, __LINE__))->AddTestCaseInstantiation(\
|
||||
#prefix, \
|
||||
>est_##prefix##test_case_name##_EvalGenerator_, \
|
||||
>est_##prefix##test_case_name##_EvalGenerateName_, \
|
||||
__FILE__, __LINE__)
|
||||
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_HAS_PARAM_TEST
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
|
||||
993
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-printers.h
vendored
Normal file
993
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-printers.h
vendored
Normal file
@@ -0,0 +1,993 @@
|
||||
// Copyright 2007, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
|
||||
// Google Test - The Google C++ Testing Framework
|
||||
//
|
||||
// This file implements a universal value printer that can print a
|
||||
// value of any type T:
|
||||
//
|
||||
// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
|
||||
//
|
||||
// A user can teach this function how to print a class type T by
|
||||
// defining either operator<<() or PrintTo() in the namespace that
|
||||
// defines T. More specifically, the FIRST defined function in the
|
||||
// following list will be used (assuming T is defined in namespace
|
||||
// foo):
|
||||
//
|
||||
// 1. foo::PrintTo(const T&, ostream*)
|
||||
// 2. operator<<(ostream&, const T&) defined in either foo or the
|
||||
// global namespace.
|
||||
//
|
||||
// If none of the above is defined, it will print the debug string of
|
||||
// the value if it is a protocol buffer, or print the raw bytes in the
|
||||
// value otherwise.
|
||||
//
|
||||
// To aid debugging: when T is a reference type, the address of the
|
||||
// value is also printed; when T is a (const) char pointer, both the
|
||||
// pointer value and the NUL-terminated string it points to are
|
||||
// printed.
|
||||
//
|
||||
// We also provide some convenient wrappers:
|
||||
//
|
||||
// // Prints a value to a string. For a (const or not) char
|
||||
// // pointer, the NUL-terminated string (but not the pointer) is
|
||||
// // printed.
|
||||
// std::string ::testing::PrintToString(const T& value);
|
||||
//
|
||||
// // Prints a value tersely: for a reference type, the referenced
|
||||
// // value (but not the address) is printed; for a (const or not) char
|
||||
// // pointer, the NUL-terminated string (but not the pointer) is
|
||||
// // printed.
|
||||
// void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
|
||||
//
|
||||
// // Prints value using the type inferred by the compiler. The difference
|
||||
// // from UniversalTersePrint() is that this function prints both the
|
||||
// // pointer and the NUL-terminated string for a (const or not) char pointer.
|
||||
// void ::testing::internal::UniversalPrint(const T& value, ostream*);
|
||||
//
|
||||
// // Prints the fields of a tuple tersely to a string vector, one
|
||||
// // element for each field. Tuple support must be enabled in
|
||||
// // gtest-port.h.
|
||||
// std::vector<string> UniversalTersePrintTupleFieldsToStrings(
|
||||
// const Tuple& value);
|
||||
//
|
||||
// Known limitation:
|
||||
//
|
||||
// The print primitives print the elements of an STL-style container
|
||||
// using the compiler-inferred type of *iter where iter is a
|
||||
// const_iterator of the container. When const_iterator is an input
|
||||
// iterator but not a forward iterator, this inferred type may not
|
||||
// match value_type, and the print output may be incorrect. In
|
||||
// practice, this is rarely a problem as for most containers
|
||||
// const_iterator is a forward iterator. We'll fix this if there's an
|
||||
// actual need for it. Note that this fix cannot rely on value_type
|
||||
// being defined as many user-defined container types don't have
|
||||
// value_type.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
|
||||
|
||||
#include <ostream> // NOLINT
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
#include "gtest/internal/gtest-internal.h"
|
||||
|
||||
#if GTEST_HAS_STD_TUPLE_
|
||||
# include <tuple>
|
||||
#endif
|
||||
|
||||
namespace testing {
|
||||
|
||||
// Definitions in the 'internal' and 'internal2' name spaces are
|
||||
// subject to change without notice. DO NOT USE THEM IN USER CODE!
|
||||
namespace internal2 {
|
||||
|
||||
// Prints the given number of bytes in the given object to the given
|
||||
// ostream.
|
||||
GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
|
||||
size_t count,
|
||||
::std::ostream* os);
|
||||
|
||||
// For selecting which printer to use when a given type has neither <<
|
||||
// nor PrintTo().
|
||||
enum TypeKind {
|
||||
kProtobuf, // a protobuf type
|
||||
kConvertibleToInteger, // a type implicitly convertible to BiggestInt
|
||||
// (e.g. a named or unnamed enum type)
|
||||
kOtherType // anything else
|
||||
};
|
||||
|
||||
// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
|
||||
// by the universal printer to print a value of type T when neither
|
||||
// operator<< nor PrintTo() is defined for T, where kTypeKind is the
|
||||
// "kind" of T as defined by enum TypeKind.
|
||||
template <typename T, TypeKind kTypeKind>
|
||||
class TypeWithoutFormatter {
|
||||
public:
|
||||
// This default version is called when kTypeKind is kOtherType.
|
||||
static void PrintValue(const T& value, ::std::ostream* os) {
|
||||
PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
|
||||
sizeof(value), os);
|
||||
}
|
||||
};
|
||||
|
||||
// We print a protobuf using its ShortDebugString() when the string
|
||||
// doesn't exceed this many characters; otherwise we print it using
|
||||
// DebugString() for better readability.
|
||||
const size_t kProtobufOneLinerMaxLength = 50;
|
||||
|
||||
template <typename T>
|
||||
class TypeWithoutFormatter<T, kProtobuf> {
|
||||
public:
|
||||
static void PrintValue(const T& value, ::std::ostream* os) {
|
||||
const ::testing::internal::string short_str = value.ShortDebugString();
|
||||
const ::testing::internal::string pretty_str =
|
||||
short_str.length() <= kProtobufOneLinerMaxLength ?
|
||||
short_str : ("\n" + value.DebugString());
|
||||
*os << ("<" + pretty_str + ">");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TypeWithoutFormatter<T, kConvertibleToInteger> {
|
||||
public:
|
||||
// Since T has no << operator or PrintTo() but can be implicitly
|
||||
// converted to BiggestInt, we print it as a BiggestInt.
|
||||
//
|
||||
// Most likely T is an enum type (either named or unnamed), in which
|
||||
// case printing it as an integer is the desired behavior. In case
|
||||
// T is not an enum, printing it as an integer is the best we can do
|
||||
// given that it has no user-defined printer.
|
||||
static void PrintValue(const T& value, ::std::ostream* os) {
|
||||
const internal::BiggestInt kBigInt = value;
|
||||
*os << kBigInt;
|
||||
}
|
||||
};
|
||||
|
||||
// Prints the given value to the given ostream. If the value is a
|
||||
// protocol message, its debug string is printed; if it's an enum or
|
||||
// of a type implicitly convertible to BiggestInt, it's printed as an
|
||||
// integer; otherwise the bytes in the value are printed. This is
|
||||
// what UniversalPrinter<T>::Print() does when it knows nothing about
|
||||
// type T and T has neither << operator nor PrintTo().
|
||||
//
|
||||
// A user can override this behavior for a class type Foo by defining
|
||||
// a << operator in the namespace where Foo is defined.
|
||||
//
|
||||
// We put this operator in namespace 'internal2' instead of 'internal'
|
||||
// to simplify the implementation, as much code in 'internal' needs to
|
||||
// use << in STL, which would conflict with our own << were it defined
|
||||
// in 'internal'.
|
||||
//
|
||||
// Note that this operator<< takes a generic std::basic_ostream<Char,
|
||||
// CharTraits> type instead of the more restricted std::ostream. If
|
||||
// we define it to take an std::ostream instead, we'll get an
|
||||
// "ambiguous overloads" compiler error when trying to print a type
|
||||
// Foo that supports streaming to std::basic_ostream<Char,
|
||||
// CharTraits>, as the compiler cannot tell whether
|
||||
// operator<<(std::ostream&, const T&) or
|
||||
// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
|
||||
// specific.
|
||||
template <typename Char, typename CharTraits, typename T>
|
||||
::std::basic_ostream<Char, CharTraits>& operator<<(
|
||||
::std::basic_ostream<Char, CharTraits>& os, const T& x) {
|
||||
TypeWithoutFormatter<T,
|
||||
(internal::IsAProtocolMessage<T>::value ? kProtobuf :
|
||||
internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
|
||||
kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace internal2
|
||||
} // namespace testing
|
||||
|
||||
// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
|
||||
// magic needed for implementing UniversalPrinter won't work.
|
||||
namespace testing_internal {
|
||||
|
||||
// Used to print a value that is not an STL-style container when the
|
||||
// user doesn't define PrintTo() for it.
|
||||
template <typename T>
|
||||
void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
|
||||
// With the following statement, during unqualified name lookup,
|
||||
// testing::internal2::operator<< appears as if it was declared in
|
||||
// the nearest enclosing namespace that contains both
|
||||
// ::testing_internal and ::testing::internal2, i.e. the global
|
||||
// namespace. For more details, refer to the C++ Standard section
|
||||
// 7.3.4-1 [namespace.udir]. This allows us to fall back onto
|
||||
// testing::internal2::operator<< in case T doesn't come with a <<
|
||||
// operator.
|
||||
//
|
||||
// We cannot write 'using ::testing::internal2::operator<<;', which
|
||||
// gcc 3.3 fails to compile due to a compiler bug.
|
||||
using namespace ::testing::internal2; // NOLINT
|
||||
|
||||
// Assuming T is defined in namespace foo, in the next statement,
|
||||
// the compiler will consider all of:
|
||||
//
|
||||
// 1. foo::operator<< (thanks to Koenig look-up),
|
||||
// 2. ::operator<< (as the current namespace is enclosed in ::),
|
||||
// 3. testing::internal2::operator<< (thanks to the using statement above).
|
||||
//
|
||||
// The operator<< whose type matches T best will be picked.
|
||||
//
|
||||
// We deliberately allow #2 to be a candidate, as sometimes it's
|
||||
// impossible to define #1 (e.g. when foo is ::std, defining
|
||||
// anything in it is undefined behavior unless you are a compiler
|
||||
// vendor.).
|
||||
*os << value;
|
||||
}
|
||||
|
||||
} // namespace testing_internal
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
|
||||
// value of type ToPrint that is an operand of a comparison assertion
|
||||
// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in
|
||||
// the comparison, and is used to help determine the best way to
|
||||
// format the value. In particular, when the value is a C string
|
||||
// (char pointer) and the other operand is an STL string object, we
|
||||
// want to format the C string as a string, since we know it is
|
||||
// compared by value with the string object. If the value is a char
|
||||
// pointer but the other operand is not an STL string object, we don't
|
||||
// know whether the pointer is supposed to point to a NUL-terminated
|
||||
// string, and thus want to print it as a pointer to be safe.
|
||||
//
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||
|
||||
// The default case.
|
||||
template <typename ToPrint, typename OtherOperand>
|
||||
class FormatForComparison {
|
||||
public:
|
||||
static ::std::string Format(const ToPrint& value) {
|
||||
return ::testing::PrintToString(value);
|
||||
}
|
||||
};
|
||||
|
||||
// Array.
|
||||
template <typename ToPrint, size_t N, typename OtherOperand>
|
||||
class FormatForComparison<ToPrint[N], OtherOperand> {
|
||||
public:
|
||||
static ::std::string Format(const ToPrint* value) {
|
||||
return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
|
||||
}
|
||||
};
|
||||
|
||||
// By default, print C string as pointers to be safe, as we don't know
|
||||
// whether they actually point to a NUL-terminated string.
|
||||
|
||||
#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \
|
||||
template <typename OtherOperand> \
|
||||
class FormatForComparison<CharType*, OtherOperand> { \
|
||||
public: \
|
||||
static ::std::string Format(CharType* value) { \
|
||||
return ::testing::PrintToString(static_cast<const void*>(value)); \
|
||||
} \
|
||||
}
|
||||
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
|
||||
|
||||
#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
|
||||
|
||||
// If a C string is compared with an STL string object, we know it's meant
|
||||
// to point to a NUL-terminated string, and thus can print it as a string.
|
||||
|
||||
#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
|
||||
template <> \
|
||||
class FormatForComparison<CharType*, OtherStringType> { \
|
||||
public: \
|
||||
static ::std::string Format(CharType* value) { \
|
||||
return ::testing::PrintToString(value); \
|
||||
} \
|
||||
}
|
||||
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
|
||||
|
||||
#if GTEST_HAS_GLOBAL_STRING
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
|
||||
#endif
|
||||
|
||||
#if GTEST_HAS_GLOBAL_WSTRING
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
|
||||
#endif
|
||||
|
||||
#if GTEST_HAS_STD_WSTRING
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
|
||||
GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
|
||||
#endif
|
||||
|
||||
#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
|
||||
|
||||
// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
|
||||
// operand to be used in a failure message. The type (but not value)
|
||||
// of the other operand may affect the format. This allows us to
|
||||
// print a char* as a raw pointer when it is compared against another
|
||||
// char* or void*, and print it as a C string when it is compared
|
||||
// against an std::string object, for example.
|
||||
//
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||
template <typename T1, typename T2>
|
||||
std::string FormatForComparisonFailureMessage(
|
||||
const T1& value, const T2& /* other_operand */) {
|
||||
return FormatForComparison<T1, T2>::Format(value);
|
||||
}
|
||||
|
||||
// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
|
||||
// value to the given ostream. The caller must ensure that
|
||||
// 'ostream_ptr' is not NULL, or the behavior is undefined.
|
||||
//
|
||||
// We define UniversalPrinter as a class template (as opposed to a
|
||||
// function template), as we need to partially specialize it for
|
||||
// reference types, which cannot be done with function templates.
|
||||
template <typename T>
|
||||
class UniversalPrinter;
|
||||
|
||||
template <typename T>
|
||||
void UniversalPrint(const T& value, ::std::ostream* os);
|
||||
|
||||
// Used to print an STL-style container when the user doesn't define
|
||||
// a PrintTo() for it.
|
||||
template <typename C>
|
||||
void DefaultPrintTo(IsContainer /* dummy */,
|
||||
false_type /* is not a pointer */,
|
||||
const C& container, ::std::ostream* os) {
|
||||
const size_t kMaxCount = 32; // The maximum number of elements to print.
|
||||
*os << '{';
|
||||
size_t count = 0;
|
||||
for (typename C::const_iterator it = container.begin();
|
||||
it != container.end(); ++it, ++count) {
|
||||
if (count > 0) {
|
||||
*os << ',';
|
||||
if (count == kMaxCount) { // Enough has been printed.
|
||||
*os << " ...";
|
||||
break;
|
||||
}
|
||||
}
|
||||
*os << ' ';
|
||||
// We cannot call PrintTo(*it, os) here as PrintTo() doesn't
|
||||
// handle *it being a native array.
|
||||
internal::UniversalPrint(*it, os);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
*os << ' ';
|
||||
}
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
// Used to print a pointer that is neither a char pointer nor a member
|
||||
// pointer, when the user doesn't define PrintTo() for it. (A member
|
||||
// variable pointer or member function pointer doesn't really point to
|
||||
// a location in the address space. Their representation is
|
||||
// implementation-defined. Therefore they will be printed as raw
|
||||
// bytes.)
|
||||
template <typename T>
|
||||
void DefaultPrintTo(IsNotContainer /* dummy */,
|
||||
true_type /* is a pointer */,
|
||||
T* p, ::std::ostream* os) {
|
||||
if (p == NULL) {
|
||||
*os << "NULL";
|
||||
} else {
|
||||
// C++ doesn't allow casting from a function pointer to any object
|
||||
// pointer.
|
||||
//
|
||||
// IsTrue() silences warnings: "Condition is always true",
|
||||
// "unreachable code".
|
||||
if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
|
||||
// T is not a function type. We just call << to print p,
|
||||
// relying on ADL to pick up user-defined << for their pointer
|
||||
// types, if any.
|
||||
*os << p;
|
||||
} else {
|
||||
// T is a function type, so '*os << p' doesn't do what we want
|
||||
// (it just prints p as bool). We want to print p as a const
|
||||
// void*. However, we cannot cast it to const void* directly,
|
||||
// even using reinterpret_cast, as earlier versions of gcc
|
||||
// (e.g. 3.4.5) cannot compile the cast when p is a function
|
||||
// pointer. Casting to UInt64 first solves the problem.
|
||||
*os << reinterpret_cast<const void*>(
|
||||
reinterpret_cast<internal::UInt64>(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to print a non-container, non-pointer value when the user
|
||||
// doesn't define PrintTo() for it.
|
||||
template <typename T>
|
||||
void DefaultPrintTo(IsNotContainer /* dummy */,
|
||||
false_type /* is not a pointer */,
|
||||
const T& value, ::std::ostream* os) {
|
||||
::testing_internal::DefaultPrintNonContainerTo(value, os);
|
||||
}
|
||||
|
||||
// Prints the given value using the << operator if it has one;
|
||||
// otherwise prints the bytes in it. This is what
|
||||
// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
|
||||
// or overloaded for type T.
|
||||
//
|
||||
// A user can override this behavior for a class type Foo by defining
|
||||
// an overload of PrintTo() in the namespace where Foo is defined. We
|
||||
// give the user this option as sometimes defining a << operator for
|
||||
// Foo is not desirable (e.g. the coding style may prevent doing it,
|
||||
// or there is already a << operator but it doesn't do what the user
|
||||
// wants).
|
||||
template <typename T>
|
||||
void PrintTo(const T& value, ::std::ostream* os) {
|
||||
// DefaultPrintTo() is overloaded. The type of its first two
|
||||
// arguments determine which version will be picked. If T is an
|
||||
// STL-style container, the version for container will be called; if
|
||||
// T is a pointer, the pointer version will be called; otherwise the
|
||||
// generic version will be called.
|
||||
//
|
||||
// Note that we check for container types here, prior to we check
|
||||
// for protocol message types in our operator<<. The rationale is:
|
||||
//
|
||||
// For protocol messages, we want to give people a chance to
|
||||
// override Google Mock's format by defining a PrintTo() or
|
||||
// operator<<. For STL containers, other formats can be
|
||||
// incompatible with Google Mock's format for the container
|
||||
// elements; therefore we check for container types here to ensure
|
||||
// that our format is used.
|
||||
//
|
||||
// The second argument of DefaultPrintTo() is needed to bypass a bug
|
||||
// in Symbian's C++ compiler that prevents it from picking the right
|
||||
// overload between:
|
||||
//
|
||||
// PrintTo(const T& x, ...);
|
||||
// PrintTo(T* x, ...);
|
||||
DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
|
||||
}
|
||||
|
||||
// The following list of PrintTo() overloads tells
|
||||
// UniversalPrinter<T>::Print() how to print standard types (built-in
|
||||
// types, strings, plain arrays, and pointers).
|
||||
|
||||
// Overloads for various char types.
|
||||
GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os);
|
||||
GTEST_API_ void PrintTo(signed char c, ::std::ostream* os);
|
||||
inline void PrintTo(char c, ::std::ostream* os) {
|
||||
// When printing a plain char, we always treat it as unsigned. This
|
||||
// way, the output won't be affected by whether the compiler thinks
|
||||
// char is signed or not.
|
||||
PrintTo(static_cast<unsigned char>(c), os);
|
||||
}
|
||||
|
||||
// Overloads for other simple built-in types.
|
||||
inline void PrintTo(bool x, ::std::ostream* os) {
|
||||
*os << (x ? "true" : "false");
|
||||
}
|
||||
|
||||
// Overload for wchar_t type.
|
||||
// Prints a wchar_t as a symbol if it is printable or as its internal
|
||||
// code otherwise and also as its decimal code (except for L'\0').
|
||||
// The L'\0' char is printed as "L'\\0'". The decimal code is printed
|
||||
// as signed integer when wchar_t is implemented by the compiler
|
||||
// as a signed type and is printed as an unsigned integer when wchar_t
|
||||
// is implemented as an unsigned type.
|
||||
GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
|
||||
|
||||
// Overloads for C strings.
|
||||
GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
|
||||
inline void PrintTo(char* s, ::std::ostream* os) {
|
||||
PrintTo(ImplicitCast_<const char*>(s), os);
|
||||
}
|
||||
|
||||
// signed/unsigned char is often used for representing binary data, so
|
||||
// we print pointers to it as void* to be safe.
|
||||
inline void PrintTo(const signed char* s, ::std::ostream* os) {
|
||||
PrintTo(ImplicitCast_<const void*>(s), os);
|
||||
}
|
||||
inline void PrintTo(signed char* s, ::std::ostream* os) {
|
||||
PrintTo(ImplicitCast_<const void*>(s), os);
|
||||
}
|
||||
inline void PrintTo(const unsigned char* s, ::std::ostream* os) {
|
||||
PrintTo(ImplicitCast_<const void*>(s), os);
|
||||
}
|
||||
inline void PrintTo(unsigned char* s, ::std::ostream* os) {
|
||||
PrintTo(ImplicitCast_<const void*>(s), os);
|
||||
}
|
||||
|
||||
// MSVC can be configured to define wchar_t as a typedef of unsigned
|
||||
// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
|
||||
// type. When wchar_t is a typedef, defining an overload for const
|
||||
// wchar_t* would cause unsigned short* be printed as a wide string,
|
||||
// possibly causing invalid memory accesses.
|
||||
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
|
||||
// Overloads for wide C strings
|
||||
GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os);
|
||||
inline void PrintTo(wchar_t* s, ::std::ostream* os) {
|
||||
PrintTo(ImplicitCast_<const wchar_t*>(s), os);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Overload for C arrays. Multi-dimensional arrays are printed
|
||||
// properly.
|
||||
|
||||
// Prints the given number of elements in an array, without printing
|
||||
// the curly braces.
|
||||
template <typename T>
|
||||
void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
|
||||
UniversalPrint(a[0], os);
|
||||
for (size_t i = 1; i != count; i++) {
|
||||
*os << ", ";
|
||||
UniversalPrint(a[i], os);
|
||||
}
|
||||
}
|
||||
|
||||
// Overloads for ::string and ::std::string.
|
||||
#if GTEST_HAS_GLOBAL_STRING
|
||||
GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os);
|
||||
inline void PrintTo(const ::string& s, ::std::ostream* os) {
|
||||
PrintStringTo(s, os);
|
||||
}
|
||||
#endif // GTEST_HAS_GLOBAL_STRING
|
||||
|
||||
GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
|
||||
inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
|
||||
PrintStringTo(s, os);
|
||||
}
|
||||
|
||||
// Overloads for ::wstring and ::std::wstring.
|
||||
#if GTEST_HAS_GLOBAL_WSTRING
|
||||
GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
|
||||
inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
|
||||
PrintWideStringTo(s, os);
|
||||
}
|
||||
#endif // GTEST_HAS_GLOBAL_WSTRING
|
||||
|
||||
#if GTEST_HAS_STD_WSTRING
|
||||
GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
|
||||
inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
|
||||
PrintWideStringTo(s, os);
|
||||
}
|
||||
#endif // GTEST_HAS_STD_WSTRING
|
||||
|
||||
#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
|
||||
// Helper function for printing a tuple. T must be instantiated with
|
||||
// a tuple type.
|
||||
template <typename T>
|
||||
void PrintTupleTo(const T& t, ::std::ostream* os);
|
||||
#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
|
||||
|
||||
#if GTEST_HAS_TR1_TUPLE
|
||||
// Overload for ::std::tr1::tuple. Needed for printing function arguments,
|
||||
// which are packed as tuples.
|
||||
|
||||
// Overloaded PrintTo() for tuples of various arities. We support
|
||||
// tuples of up-to 10 fields. The following implementation works
|
||||
// regardless of whether tr1::tuple is implemented using the
|
||||
// non-standard variadic template feature or not.
|
||||
|
||||
inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
|
||||
::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
|
||||
::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
|
||||
::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
|
||||
::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9>
|
||||
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
|
||||
::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10>
|
||||
void PrintTo(
|
||||
const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
|
||||
::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
#endif // GTEST_HAS_TR1_TUPLE
|
||||
|
||||
#if GTEST_HAS_STD_TUPLE_
|
||||
template <typename... Types>
|
||||
void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) {
|
||||
PrintTupleTo(t, os);
|
||||
}
|
||||
#endif // GTEST_HAS_STD_TUPLE_
|
||||
|
||||
// Overload for std::pair.
|
||||
template <typename T1, typename T2>
|
||||
void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
|
||||
*os << '(';
|
||||
// We cannot use UniversalPrint(value.first, os) here, as T1 may be
|
||||
// a reference type. The same for printing value.second.
|
||||
UniversalPrinter<T1>::Print(value.first, os);
|
||||
*os << ", ";
|
||||
UniversalPrinter<T2>::Print(value.second, os);
|
||||
*os << ')';
|
||||
}
|
||||
|
||||
// Implements printing a non-reference type T by letting the compiler
|
||||
// pick the right overload of PrintTo() for T.
|
||||
template <typename T>
|
||||
class UniversalPrinter {
|
||||
public:
|
||||
// MSVC warns about adding const to a function type, so we want to
|
||||
// disable the warning.
|
||||
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
|
||||
|
||||
// Note: we deliberately don't call this PrintTo(), as that name
|
||||
// conflicts with ::testing::internal::PrintTo in the body of the
|
||||
// function.
|
||||
static void Print(const T& value, ::std::ostream* os) {
|
||||
// By default, ::testing::internal::PrintTo() is used for printing
|
||||
// the value.
|
||||
//
|
||||
// Thanks to Koenig look-up, if T is a class and has its own
|
||||
// PrintTo() function defined in its namespace, that function will
|
||||
// be visible here. Since it is more specific than the generic ones
|
||||
// in ::testing::internal, it will be picked by the compiler in the
|
||||
// following statement - exactly what we want.
|
||||
PrintTo(value, os);
|
||||
}
|
||||
|
||||
GTEST_DISABLE_MSC_WARNINGS_POP_()
|
||||
};
|
||||
|
||||
// UniversalPrintArray(begin, len, os) prints an array of 'len'
|
||||
// elements, starting at address 'begin'.
|
||||
template <typename T>
|
||||
void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
|
||||
if (len == 0) {
|
||||
*os << "{}";
|
||||
} else {
|
||||
*os << "{ ";
|
||||
const size_t kThreshold = 18;
|
||||
const size_t kChunkSize = 8;
|
||||
// If the array has more than kThreshold elements, we'll have to
|
||||
// omit some details by printing only the first and the last
|
||||
// kChunkSize elements.
|
||||
// TODO(wan@google.com): let the user control the threshold using a flag.
|
||||
if (len <= kThreshold) {
|
||||
PrintRawArrayTo(begin, len, os);
|
||||
} else {
|
||||
PrintRawArrayTo(begin, kChunkSize, os);
|
||||
*os << ", ..., ";
|
||||
PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
|
||||
}
|
||||
*os << " }";
|
||||
}
|
||||
}
|
||||
// This overload prints a (const) char array compactly.
|
||||
GTEST_API_ void UniversalPrintArray(
|
||||
const char* begin, size_t len, ::std::ostream* os);
|
||||
|
||||
// This overload prints a (const) wchar_t array compactly.
|
||||
GTEST_API_ void UniversalPrintArray(
|
||||
const wchar_t* begin, size_t len, ::std::ostream* os);
|
||||
|
||||
// Implements printing an array type T[N].
|
||||
template <typename T, size_t N>
|
||||
class UniversalPrinter<T[N]> {
|
||||
public:
|
||||
// Prints the given array, omitting some elements when there are too
|
||||
// many.
|
||||
static void Print(const T (&a)[N], ::std::ostream* os) {
|
||||
UniversalPrintArray(a, N, os);
|
||||
}
|
||||
};
|
||||
|
||||
// Implements printing a reference type T&.
|
||||
template <typename T>
|
||||
class UniversalPrinter<T&> {
|
||||
public:
|
||||
// MSVC warns about adding const to a function type, so we want to
|
||||
// disable the warning.
|
||||
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
|
||||
|
||||
static void Print(const T& value, ::std::ostream* os) {
|
||||
// Prints the address of the value. We use reinterpret_cast here
|
||||
// as static_cast doesn't compile when T is a function type.
|
||||
*os << "@" << reinterpret_cast<const void*>(&value) << " ";
|
||||
|
||||
// Then prints the value itself.
|
||||
UniversalPrint(value, os);
|
||||
}
|
||||
|
||||
GTEST_DISABLE_MSC_WARNINGS_POP_()
|
||||
};
|
||||
|
||||
// Prints a value tersely: for a reference type, the referenced value
|
||||
// (but not the address) is printed; for a (const) char pointer, the
|
||||
// NUL-terminated string (but not the pointer) is printed.
|
||||
|
||||
template <typename T>
|
||||
class UniversalTersePrinter {
|
||||
public:
|
||||
static void Print(const T& value, ::std::ostream* os) {
|
||||
UniversalPrint(value, os);
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
class UniversalTersePrinter<T&> {
|
||||
public:
|
||||
static void Print(const T& value, ::std::ostream* os) {
|
||||
UniversalPrint(value, os);
|
||||
}
|
||||
};
|
||||
template <typename T, size_t N>
|
||||
class UniversalTersePrinter<T[N]> {
|
||||
public:
|
||||
static void Print(const T (&value)[N], ::std::ostream* os) {
|
||||
UniversalPrinter<T[N]>::Print(value, os);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
class UniversalTersePrinter<const char*> {
|
||||
public:
|
||||
static void Print(const char* str, ::std::ostream* os) {
|
||||
if (str == NULL) {
|
||||
*os << "NULL";
|
||||
} else {
|
||||
UniversalPrint(string(str), os);
|
||||
}
|
||||
}
|
||||
};
|
||||
template <>
|
||||
class UniversalTersePrinter<char*> {
|
||||
public:
|
||||
static void Print(char* str, ::std::ostream* os) {
|
||||
UniversalTersePrinter<const char*>::Print(str, os);
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_HAS_STD_WSTRING
|
||||
template <>
|
||||
class UniversalTersePrinter<const wchar_t*> {
|
||||
public:
|
||||
static void Print(const wchar_t* str, ::std::ostream* os) {
|
||||
if (str == NULL) {
|
||||
*os << "NULL";
|
||||
} else {
|
||||
UniversalPrint(::std::wstring(str), os);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <>
|
||||
class UniversalTersePrinter<wchar_t*> {
|
||||
public:
|
||||
static void Print(wchar_t* str, ::std::ostream* os) {
|
||||
UniversalTersePrinter<const wchar_t*>::Print(str, os);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void UniversalTersePrint(const T& value, ::std::ostream* os) {
|
||||
UniversalTersePrinter<T>::Print(value, os);
|
||||
}
|
||||
|
||||
// Prints a value using the type inferred by the compiler. The
|
||||
// difference between this and UniversalTersePrint() is that for a
|
||||
// (const) char pointer, this prints both the pointer and the
|
||||
// NUL-terminated string.
|
||||
template <typename T>
|
||||
void UniversalPrint(const T& value, ::std::ostream* os) {
|
||||
// A workarond for the bug in VC++ 7.1 that prevents us from instantiating
|
||||
// UniversalPrinter with T directly.
|
||||
typedef T T1;
|
||||
UniversalPrinter<T1>::Print(value, os);
|
||||
}
|
||||
|
||||
typedef ::std::vector<string> Strings;
|
||||
|
||||
// TuplePolicy<TupleT> must provide:
|
||||
// - tuple_size
|
||||
// size of tuple TupleT.
|
||||
// - get<size_t I>(const TupleT& t)
|
||||
// static function extracting element I of tuple TupleT.
|
||||
// - tuple_element<size_t I>::type
|
||||
// type of element I of tuple TupleT.
|
||||
template <typename TupleT>
|
||||
struct TuplePolicy;
|
||||
|
||||
#if GTEST_HAS_TR1_TUPLE
|
||||
template <typename TupleT>
|
||||
struct TuplePolicy {
|
||||
typedef TupleT Tuple;
|
||||
static const size_t tuple_size = ::std::tr1::tuple_size<Tuple>::value;
|
||||
|
||||
template <size_t I>
|
||||
struct tuple_element : ::std::tr1::tuple_element<I, Tuple> {};
|
||||
|
||||
template <size_t I>
|
||||
static typename AddReference<
|
||||
const typename ::std::tr1::tuple_element<I, Tuple>::type>::type get(
|
||||
const Tuple& tuple) {
|
||||
return ::std::tr1::get<I>(tuple);
|
||||
}
|
||||
};
|
||||
template <typename TupleT>
|
||||
const size_t TuplePolicy<TupleT>::tuple_size;
|
||||
#endif // GTEST_HAS_TR1_TUPLE
|
||||
|
||||
#if GTEST_HAS_STD_TUPLE_
|
||||
template <typename... Types>
|
||||
struct TuplePolicy< ::std::tuple<Types...> > {
|
||||
typedef ::std::tuple<Types...> Tuple;
|
||||
static const size_t tuple_size = ::std::tuple_size<Tuple>::value;
|
||||
|
||||
template <size_t I>
|
||||
struct tuple_element : ::std::tuple_element<I, Tuple> {};
|
||||
|
||||
template <size_t I>
|
||||
static const typename ::std::tuple_element<I, Tuple>::type& get(
|
||||
const Tuple& tuple) {
|
||||
return ::std::get<I>(tuple);
|
||||
}
|
||||
};
|
||||
template <typename... Types>
|
||||
const size_t TuplePolicy< ::std::tuple<Types...> >::tuple_size;
|
||||
#endif // GTEST_HAS_STD_TUPLE_
|
||||
|
||||
#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
|
||||
// This helper template allows PrintTo() for tuples and
|
||||
// UniversalTersePrintTupleFieldsToStrings() to be defined by
|
||||
// induction on the number of tuple fields. The idea is that
|
||||
// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
|
||||
// fields in tuple t, and can be defined in terms of
|
||||
// TuplePrefixPrinter<N - 1>.
|
||||
//
|
||||
// The inductive case.
|
||||
template <size_t N>
|
||||
struct TuplePrefixPrinter {
|
||||
// Prints the first N fields of a tuple.
|
||||
template <typename Tuple>
|
||||
static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
|
||||
TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
|
||||
GTEST_INTENTIONAL_CONST_COND_PUSH_()
|
||||
if (N > 1) {
|
||||
GTEST_INTENTIONAL_CONST_COND_POP_()
|
||||
*os << ", ";
|
||||
}
|
||||
UniversalPrinter<
|
||||
typename TuplePolicy<Tuple>::template tuple_element<N - 1>::type>
|
||||
::Print(TuplePolicy<Tuple>::template get<N - 1>(t), os);
|
||||
}
|
||||
|
||||
// Tersely prints the first N fields of a tuple to a string vector,
|
||||
// one element for each field.
|
||||
template <typename Tuple>
|
||||
static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
|
||||
TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
|
||||
::std::stringstream ss;
|
||||
UniversalTersePrint(TuplePolicy<Tuple>::template get<N - 1>(t), &ss);
|
||||
strings->push_back(ss.str());
|
||||
}
|
||||
};
|
||||
|
||||
// Base case.
|
||||
template <>
|
||||
struct TuplePrefixPrinter<0> {
|
||||
template <typename Tuple>
|
||||
static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
|
||||
|
||||
template <typename Tuple>
|
||||
static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
|
||||
};
|
||||
|
||||
// Helper function for printing a tuple.
|
||||
// Tuple must be either std::tr1::tuple or std::tuple type.
|
||||
template <typename Tuple>
|
||||
void PrintTupleTo(const Tuple& t, ::std::ostream* os) {
|
||||
*os << "(";
|
||||
TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::PrintPrefixTo(t, os);
|
||||
*os << ")";
|
||||
}
|
||||
|
||||
// Prints the fields of a tuple tersely to a string vector, one
|
||||
// element for each field. See the comment before
|
||||
// UniversalTersePrint() for how we define "tersely".
|
||||
template <typename Tuple>
|
||||
Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
|
||||
Strings result;
|
||||
TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::
|
||||
TersePrintPrefixToStrings(value, &result);
|
||||
return result;
|
||||
}
|
||||
#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
::std::string PrintToString(const T& value) {
|
||||
::std::stringstream ss;
|
||||
internal::UniversalTersePrinter<T>::Print(value, &ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
|
||||
// Include any custom printer added by the local installation.
|
||||
// We must include this header at the end to make sure it can use the
|
||||
// declarations from this file.
|
||||
#include "gtest/internal/custom/gtest-printers.h"
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
|
||||
232
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-spi.h
vendored
Normal file
232
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-spi.h
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright 2007, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
//
|
||||
// Utilities for testing Google Test itself and code that uses Google Test
|
||||
// (e.g. frameworks built on top of Google Test).
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace testing {
|
||||
|
||||
// This helper class can be used to mock out Google Test failure reporting
|
||||
// so that we can test Google Test or code that builds on Google Test.
|
||||
//
|
||||
// An object of this class appends a TestPartResult object to the
|
||||
// TestPartResultArray object given in the constructor whenever a Google Test
|
||||
// failure is reported. It can either intercept only failures that are
|
||||
// generated in the same thread that created this object or it can intercept
|
||||
// all generated failures. The scope of this mock object can be controlled with
|
||||
// the second argument to the two arguments constructor.
|
||||
class GTEST_API_ ScopedFakeTestPartResultReporter
|
||||
: public TestPartResultReporterInterface {
|
||||
public:
|
||||
// The two possible mocking modes of this object.
|
||||
enum InterceptMode {
|
||||
INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
|
||||
INTERCEPT_ALL_THREADS // Intercepts all failures.
|
||||
};
|
||||
|
||||
// The c'tor sets this object as the test part result reporter used
|
||||
// by Google Test. The 'result' parameter specifies where to report the
|
||||
// results. This reporter will only catch failures generated in the current
|
||||
// thread. DEPRECATED
|
||||
explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
|
||||
|
||||
// Same as above, but you can choose the interception scope of this object.
|
||||
ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
|
||||
TestPartResultArray* result);
|
||||
|
||||
// The d'tor restores the previous test part result reporter.
|
||||
virtual ~ScopedFakeTestPartResultReporter();
|
||||
|
||||
// Appends the TestPartResult object to the TestPartResultArray
|
||||
// received in the constructor.
|
||||
//
|
||||
// This method is from the TestPartResultReporterInterface
|
||||
// interface.
|
||||
virtual void ReportTestPartResult(const TestPartResult& result);
|
||||
private:
|
||||
void Init();
|
||||
|
||||
const InterceptMode intercept_mode_;
|
||||
TestPartResultReporterInterface* old_reporter_;
|
||||
TestPartResultArray* const result_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// A helper class for implementing EXPECT_FATAL_FAILURE() and
|
||||
// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
|
||||
// TestPartResultArray contains exactly one failure that has the given
|
||||
// type and contains the given substring. If that's not the case, a
|
||||
// non-fatal failure will be generated.
|
||||
class GTEST_API_ SingleFailureChecker {
|
||||
public:
|
||||
// The constructor remembers the arguments.
|
||||
SingleFailureChecker(const TestPartResultArray* results,
|
||||
TestPartResult::Type type,
|
||||
const string& substr);
|
||||
~SingleFailureChecker();
|
||||
private:
|
||||
const TestPartResultArray* const results_;
|
||||
const TestPartResult::Type type_;
|
||||
const string substr_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace testing
|
||||
|
||||
// A set of macros for testing Google Test assertions or code that's expected
|
||||
// to generate Google Test fatal failures. It verifies that the given
|
||||
// statement will cause exactly one fatal Google Test failure with 'substr'
|
||||
// being part of the failure message.
|
||||
//
|
||||
// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
|
||||
// affects and considers failures generated in the current thread and
|
||||
// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
|
||||
//
|
||||
// The verification of the assertion is done correctly even when the statement
|
||||
// throws an exception or aborts the current function.
|
||||
//
|
||||
// Known restrictions:
|
||||
// - 'statement' cannot reference local non-static variables or
|
||||
// non-static members of the current object.
|
||||
// - 'statement' cannot return a value.
|
||||
// - You cannot stream a failure message to this macro.
|
||||
//
|
||||
// Note that even though the implementations of the following two
|
||||
// macros are much alike, we cannot refactor them to use a common
|
||||
// helper macro, due to some peculiarity in how the preprocessor
|
||||
// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
|
||||
// gtest_unittest.cc will fail to compile if we do that.
|
||||
#define EXPECT_FATAL_FAILURE(statement, substr) \
|
||||
do { \
|
||||
class GTestExpectFatalFailureHelper {\
|
||||
public:\
|
||||
static void Execute() { statement; }\
|
||||
};\
|
||||
::testing::TestPartResultArray gtest_failures;\
|
||||
::testing::internal::SingleFailureChecker gtest_checker(\
|
||||
>est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
|
||||
{\
|
||||
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
|
||||
::testing::ScopedFakeTestPartResultReporter:: \
|
||||
INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\
|
||||
GTestExpectFatalFailureHelper::Execute();\
|
||||
}\
|
||||
} while (::testing::internal::AlwaysFalse())
|
||||
|
||||
#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
|
||||
do { \
|
||||
class GTestExpectFatalFailureHelper {\
|
||||
public:\
|
||||
static void Execute() { statement; }\
|
||||
};\
|
||||
::testing::TestPartResultArray gtest_failures;\
|
||||
::testing::internal::SingleFailureChecker gtest_checker(\
|
||||
>est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
|
||||
{\
|
||||
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
|
||||
::testing::ScopedFakeTestPartResultReporter:: \
|
||||
INTERCEPT_ALL_THREADS, >est_failures);\
|
||||
GTestExpectFatalFailureHelper::Execute();\
|
||||
}\
|
||||
} while (::testing::internal::AlwaysFalse())
|
||||
|
||||
// A macro for testing Google Test assertions or code that's expected to
|
||||
// generate Google Test non-fatal failures. It asserts that the given
|
||||
// statement will cause exactly one non-fatal Google Test failure with 'substr'
|
||||
// being part of the failure message.
|
||||
//
|
||||
// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
|
||||
// affects and considers failures generated in the current thread and
|
||||
// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
|
||||
//
|
||||
// 'statement' is allowed to reference local variables and members of
|
||||
// the current object.
|
||||
//
|
||||
// The verification of the assertion is done correctly even when the statement
|
||||
// throws an exception or aborts the current function.
|
||||
//
|
||||
// Known restrictions:
|
||||
// - You cannot stream a failure message to this macro.
|
||||
//
|
||||
// Note that even though the implementations of the following two
|
||||
// macros are much alike, we cannot refactor them to use a common
|
||||
// helper macro, due to some peculiarity in how the preprocessor
|
||||
// works. If we do that, the code won't compile when the user gives
|
||||
// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
|
||||
// expands to code containing an unprotected comma. The
|
||||
// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
|
||||
// catches that.
|
||||
//
|
||||
// For the same reason, we have to write
|
||||
// if (::testing::internal::AlwaysTrue()) { statement; }
|
||||
// instead of
|
||||
// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
|
||||
// to avoid an MSVC warning on unreachable code.
|
||||
#define EXPECT_NONFATAL_FAILURE(statement, substr) \
|
||||
do {\
|
||||
::testing::TestPartResultArray gtest_failures;\
|
||||
::testing::internal::SingleFailureChecker gtest_checker(\
|
||||
>est_failures, ::testing::TestPartResult::kNonFatalFailure, \
|
||||
(substr));\
|
||||
{\
|
||||
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
|
||||
::testing::ScopedFakeTestPartResultReporter:: \
|
||||
INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\
|
||||
if (::testing::internal::AlwaysTrue()) { statement; }\
|
||||
}\
|
||||
} while (::testing::internal::AlwaysFalse())
|
||||
|
||||
#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
|
||||
do {\
|
||||
::testing::TestPartResultArray gtest_failures;\
|
||||
::testing::internal::SingleFailureChecker gtest_checker(\
|
||||
>est_failures, ::testing::TestPartResult::kNonFatalFailure, \
|
||||
(substr));\
|
||||
{\
|
||||
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
|
||||
::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
|
||||
>est_failures);\
|
||||
if (::testing::internal::AlwaysTrue()) { statement; }\
|
||||
}\
|
||||
} while (::testing::internal::AlwaysFalse())
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||
179
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-test-part.h
vendored
Normal file
179
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-test-part.h
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2008, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: mheule@google.com (Markus Heule)
|
||||
//
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
#include "gtest/internal/gtest-internal.h"
|
||||
#include "gtest/internal/gtest-string.h"
|
||||
|
||||
namespace testing {
|
||||
|
||||
// A copyable object representing the result of a test part (i.e. an
|
||||
// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
|
||||
//
|
||||
// Don't inherit from TestPartResult as its destructor is not virtual.
|
||||
class GTEST_API_ TestPartResult {
|
||||
public:
|
||||
// The possible outcomes of a test part (i.e. an assertion or an
|
||||
// explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
|
||||
enum Type {
|
||||
kSuccess, // Succeeded.
|
||||
kNonFatalFailure, // Failed but the test can continue.
|
||||
kFatalFailure // Failed and the test should be terminated.
|
||||
};
|
||||
|
||||
// C'tor. TestPartResult does NOT have a default constructor.
|
||||
// Always use this constructor (with parameters) to create a
|
||||
// TestPartResult object.
|
||||
TestPartResult(Type a_type,
|
||||
const char* a_file_name,
|
||||
int a_line_number,
|
||||
const char* a_message)
|
||||
: type_(a_type),
|
||||
file_name_(a_file_name == NULL ? "" : a_file_name),
|
||||
line_number_(a_line_number),
|
||||
summary_(ExtractSummary(a_message)),
|
||||
message_(a_message) {
|
||||
}
|
||||
|
||||
// Gets the outcome of the test part.
|
||||
Type type() const { return type_; }
|
||||
|
||||
// Gets the name of the source file where the test part took place, or
|
||||
// NULL if it's unknown.
|
||||
const char* file_name() const {
|
||||
return file_name_.empty() ? NULL : file_name_.c_str();
|
||||
}
|
||||
|
||||
// Gets the line in the source file where the test part took place,
|
||||
// or -1 if it's unknown.
|
||||
int line_number() const { return line_number_; }
|
||||
|
||||
// Gets the summary of the failure message.
|
||||
const char* summary() const { return summary_.c_str(); }
|
||||
|
||||
// Gets the message associated with the test part.
|
||||
const char* message() const { return message_.c_str(); }
|
||||
|
||||
// Returns true iff the test part passed.
|
||||
bool passed() const { return type_ == kSuccess; }
|
||||
|
||||
// Returns true iff the test part failed.
|
||||
bool failed() const { return type_ != kSuccess; }
|
||||
|
||||
// Returns true iff the test part non-fatally failed.
|
||||
bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
|
||||
|
||||
// Returns true iff the test part fatally failed.
|
||||
bool fatally_failed() const { return type_ == kFatalFailure; }
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
// Gets the summary of the failure message by omitting the stack
|
||||
// trace in it.
|
||||
static std::string ExtractSummary(const char* message);
|
||||
|
||||
// The name of the source file where the test part took place, or
|
||||
// "" if the source file is unknown.
|
||||
std::string file_name_;
|
||||
// The line in the source file where the test part took place, or -1
|
||||
// if the line number is unknown.
|
||||
int line_number_;
|
||||
std::string summary_; // The test failure summary.
|
||||
std::string message_; // The test failure message.
|
||||
};
|
||||
|
||||
// Prints a TestPartResult object.
|
||||
std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
|
||||
|
||||
// An array of TestPartResult objects.
|
||||
//
|
||||
// Don't inherit from TestPartResultArray as its destructor is not
|
||||
// virtual.
|
||||
class GTEST_API_ TestPartResultArray {
|
||||
public:
|
||||
TestPartResultArray() {}
|
||||
|
||||
// Appends the given TestPartResult to the array.
|
||||
void Append(const TestPartResult& result);
|
||||
|
||||
// Returns the TestPartResult at the given index (0-based).
|
||||
const TestPartResult& GetTestPartResult(int index) const;
|
||||
|
||||
// Returns the number of TestPartResult objects in the array.
|
||||
int size() const;
|
||||
|
||||
private:
|
||||
std::vector<TestPartResult> array_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);
|
||||
};
|
||||
|
||||
// This interface knows how to report a test part result.
|
||||
class TestPartResultReporterInterface {
|
||||
public:
|
||||
virtual ~TestPartResultReporterInterface() {}
|
||||
|
||||
virtual void ReportTestPartResult(const TestPartResult& result) = 0;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
|
||||
// statement generates new fatal failures. To do so it registers itself as the
|
||||
// current test part result reporter. Besides checking if fatal failures were
|
||||
// reported, it only delegates the reporting to the former result reporter.
|
||||
// The original result reporter is restored in the destructor.
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||
class GTEST_API_ HasNewFatalFailureHelper
|
||||
: public TestPartResultReporterInterface {
|
||||
public:
|
||||
HasNewFatalFailureHelper();
|
||||
virtual ~HasNewFatalFailureHelper();
|
||||
virtual void ReportTestPartResult(const TestPartResult& result);
|
||||
bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
|
||||
private:
|
||||
bool has_new_fatal_failure_;
|
||||
TestPartResultReporterInterface* original_reporter_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
|
||||
263
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-typed-test.h
vendored
Normal file
263
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest-typed-test.h
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
|
||||
|
||||
// This header implements typed tests and type-parameterized tests.
|
||||
|
||||
// Typed (aka type-driven) tests repeat the same test for types in a
|
||||
// list. You must know which types you want to test with when writing
|
||||
// typed tests. Here's how you do it:
|
||||
|
||||
#if 0
|
||||
|
||||
// First, define a fixture class template. It should be parameterized
|
||||
// by a type. Remember to derive it from testing::Test.
|
||||
template <typename T>
|
||||
class FooTest : public testing::Test {
|
||||
public:
|
||||
...
|
||||
typedef std::list<T> List;
|
||||
static T shared_;
|
||||
T value_;
|
||||
};
|
||||
|
||||
// Next, associate a list of types with the test case, which will be
|
||||
// repeated for each type in the list. The typedef is necessary for
|
||||
// the macro to parse correctly.
|
||||
typedef testing::Types<char, int, unsigned int> MyTypes;
|
||||
TYPED_TEST_CASE(FooTest, MyTypes);
|
||||
|
||||
// If the type list contains only one type, you can write that type
|
||||
// directly without Types<...>:
|
||||
// TYPED_TEST_CASE(FooTest, int);
|
||||
|
||||
// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
|
||||
// tests for this test case as you want.
|
||||
TYPED_TEST(FooTest, DoesBlah) {
|
||||
// Inside a test, refer to TypeParam to get the type parameter.
|
||||
// Since we are inside a derived class template, C++ requires use to
|
||||
// visit the members of FooTest via 'this'.
|
||||
TypeParam n = this->value_;
|
||||
|
||||
// To visit static members of the fixture, add the TestFixture::
|
||||
// prefix.
|
||||
n += TestFixture::shared_;
|
||||
|
||||
// To refer to typedefs in the fixture, add the "typename
|
||||
// TestFixture::" prefix.
|
||||
typename TestFixture::List values;
|
||||
values.push_back(n);
|
||||
...
|
||||
}
|
||||
|
||||
TYPED_TEST(FooTest, HasPropertyA) { ... }
|
||||
|
||||
#endif // 0
|
||||
|
||||
// Type-parameterized tests are abstract test patterns parameterized
|
||||
// by a type. Compared with typed tests, type-parameterized tests
|
||||
// allow you to define the test pattern without knowing what the type
|
||||
// parameters are. The defined pattern can be instantiated with
|
||||
// different types any number of times, in any number of translation
|
||||
// units.
|
||||
//
|
||||
// If you are designing an interface or concept, you can define a
|
||||
// suite of type-parameterized tests to verify properties that any
|
||||
// valid implementation of the interface/concept should have. Then,
|
||||
// each implementation can easily instantiate the test suite to verify
|
||||
// that it conforms to the requirements, without having to write
|
||||
// similar tests repeatedly. Here's an example:
|
||||
|
||||
#if 0
|
||||
|
||||
// First, define a fixture class template. It should be parameterized
|
||||
// by a type. Remember to derive it from testing::Test.
|
||||
template <typename T>
|
||||
class FooTest : public testing::Test {
|
||||
...
|
||||
};
|
||||
|
||||
// Next, declare that you will define a type-parameterized test case
|
||||
// (the _P suffix is for "parameterized" or "pattern", whichever you
|
||||
// prefer):
|
||||
TYPED_TEST_CASE_P(FooTest);
|
||||
|
||||
// Then, use TYPED_TEST_P() to define as many type-parameterized tests
|
||||
// for this type-parameterized test case as you want.
|
||||
TYPED_TEST_P(FooTest, DoesBlah) {
|
||||
// Inside a test, refer to TypeParam to get the type parameter.
|
||||
TypeParam n = 0;
|
||||
...
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FooTest, HasPropertyA) { ... }
|
||||
|
||||
// Now the tricky part: you need to register all test patterns before
|
||||
// you can instantiate them. The first argument of the macro is the
|
||||
// test case name; the rest are the names of the tests in this test
|
||||
// case.
|
||||
REGISTER_TYPED_TEST_CASE_P(FooTest,
|
||||
DoesBlah, HasPropertyA);
|
||||
|
||||
// Finally, you are free to instantiate the pattern with the types you
|
||||
// want. If you put the above code in a header file, you can #include
|
||||
// it in multiple C++ source files and instantiate it multiple times.
|
||||
//
|
||||
// To distinguish different instances of the pattern, the first
|
||||
// argument to the INSTANTIATE_* macro is a prefix that will be added
|
||||
// to the actual test case name. Remember to pick unique prefixes for
|
||||
// different instances.
|
||||
typedef testing::Types<char, int, unsigned int> MyTypes;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
|
||||
|
||||
// If the type list contains only one type, you can write that type
|
||||
// directly without Types<...>:
|
||||
// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
|
||||
|
||||
#endif // 0
|
||||
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
#include "gtest/internal/gtest-type-util.h"
|
||||
|
||||
// Implements typed tests.
|
||||
|
||||
#if GTEST_HAS_TYPED_TEST
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Expands to the name of the typedef for the type parameters of the
|
||||
// given test case.
|
||||
# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
|
||||
|
||||
// The 'Types' template argument below must have spaces around it
|
||||
// since some compilers may choke on '>>' when passing a template
|
||||
// instance (e.g. Types<int>)
|
||||
# define TYPED_TEST_CASE(CaseName, Types) \
|
||||
typedef ::testing::internal::TypeList< Types >::type \
|
||||
GTEST_TYPE_PARAMS_(CaseName)
|
||||
|
||||
# define TYPED_TEST(CaseName, TestName) \
|
||||
template <typename gtest_TypeParam_> \
|
||||
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
|
||||
: public CaseName<gtest_TypeParam_> { \
|
||||
private: \
|
||||
typedef CaseName<gtest_TypeParam_> TestFixture; \
|
||||
typedef gtest_TypeParam_ TypeParam; \
|
||||
virtual void TestBody(); \
|
||||
}; \
|
||||
bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
|
||||
::testing::internal::TypeParameterizedTest< \
|
||||
CaseName, \
|
||||
::testing::internal::TemplateSel< \
|
||||
GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
|
||||
GTEST_TYPE_PARAMS_(CaseName)>::Register(\
|
||||
"", ::testing::internal::CodeLocation(__FILE__, __LINE__), \
|
||||
#CaseName, #TestName, 0); \
|
||||
template <typename gtest_TypeParam_> \
|
||||
void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
|
||||
|
||||
#endif // GTEST_HAS_TYPED_TEST
|
||||
|
||||
// Implements type-parameterized tests.
|
||||
|
||||
#if GTEST_HAS_TYPED_TEST_P
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Expands to the namespace name that the type-parameterized tests for
|
||||
// the given type-parameterized test case are defined in. The exact
|
||||
// name of the namespace is subject to change without notice.
|
||||
# define GTEST_CASE_NAMESPACE_(TestCaseName) \
|
||||
gtest_case_##TestCaseName##_
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Expands to the name of the variable used to remember the names of
|
||||
// the defined tests in the given test case.
|
||||
# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \
|
||||
gtest_typed_test_case_p_state_##TestCaseName##_
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
|
||||
//
|
||||
// Expands to the name of the variable used to remember the names of
|
||||
// the registered tests in the given test case.
|
||||
# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \
|
||||
gtest_registered_test_names_##TestCaseName##_
|
||||
|
||||
// The variables defined in the type-parameterized test macros are
|
||||
// static as typically these macros are used in a .h file that can be
|
||||
// #included in multiple translation units linked together.
|
||||
# define TYPED_TEST_CASE_P(CaseName) \
|
||||
static ::testing::internal::TypedTestCasePState \
|
||||
GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
|
||||
|
||||
# define TYPED_TEST_P(CaseName, TestName) \
|
||||
namespace GTEST_CASE_NAMESPACE_(CaseName) { \
|
||||
template <typename gtest_TypeParam_> \
|
||||
class TestName : public CaseName<gtest_TypeParam_> { \
|
||||
private: \
|
||||
typedef CaseName<gtest_TypeParam_> TestFixture; \
|
||||
typedef gtest_TypeParam_ TypeParam; \
|
||||
virtual void TestBody(); \
|
||||
}; \
|
||||
static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
|
||||
GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\
|
||||
__FILE__, __LINE__, #CaseName, #TestName); \
|
||||
} \
|
||||
template <typename gtest_TypeParam_> \
|
||||
void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
|
||||
|
||||
# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \
|
||||
namespace GTEST_CASE_NAMESPACE_(CaseName) { \
|
||||
typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
|
||||
} \
|
||||
static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
|
||||
GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
|
||||
__FILE__, __LINE__, #__VA_ARGS__)
|
||||
|
||||
// The 'Types' template argument below must have spaces around it
|
||||
// since some compilers may choke on '>>' when passing a template
|
||||
// instance (e.g. Types<int>)
|
||||
# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
|
||||
bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
|
||||
::testing::internal::TypeParameterizedTestCase<CaseName, \
|
||||
GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
|
||||
::testing::internal::TypeList< Types >::type>::Register(\
|
||||
#Prefix, \
|
||||
::testing::internal::CodeLocation(__FILE__, __LINE__), \
|
||||
>EST_TYPED_TEST_CASE_P_STATE_(CaseName), \
|
||||
#CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
|
||||
|
||||
#endif // GTEST_HAS_TYPED_TEST_P
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
|
||||
2236
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest.h
vendored
Normal file
2236
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
358
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest_pred_impl.h
vendored
Normal file
358
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest_pred_impl.h
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
// Copyright 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
|
||||
// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND!
|
||||
//
|
||||
// Implements a family of generic predicate assertion macros.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||
|
||||
// Makes sure this header is not included before gtest.h.
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
|
||||
# error Do not include gtest_pred_impl.h directly. Include gtest.h instead.
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
|
||||
|
||||
// This header implements a family of generic predicate assertion
|
||||
// macros:
|
||||
//
|
||||
// ASSERT_PRED_FORMAT1(pred_format, v1)
|
||||
// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
|
||||
// ...
|
||||
//
|
||||
// where pred_format is a function or functor that takes n (in the
|
||||
// case of ASSERT_PRED_FORMATn) values and their source expression
|
||||
// text, and returns a testing::AssertionResult. See the definition
|
||||
// of ASSERT_EQ in gtest.h for an example.
|
||||
//
|
||||
// If you don't care about formatting, you can use the more
|
||||
// restrictive version:
|
||||
//
|
||||
// ASSERT_PRED1(pred, v1)
|
||||
// ASSERT_PRED2(pred, v1, v2)
|
||||
// ...
|
||||
//
|
||||
// where pred is an n-ary function or functor that returns bool,
|
||||
// and the values v1, v2, ..., must support the << operator for
|
||||
// streaming to std::ostream.
|
||||
//
|
||||
// We also define the EXPECT_* variations.
|
||||
//
|
||||
// For now we only support predicates whose arity is at most 5.
|
||||
// Please email googletestframework@googlegroups.com if you need
|
||||
// support for higher arities.
|
||||
|
||||
// GTEST_ASSERT_ is the basic statement to which all of the assertions
|
||||
// in this file reduce. Don't use this in your code.
|
||||
|
||||
#define GTEST_ASSERT_(expression, on_failure) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (const ::testing::AssertionResult gtest_ar = (expression)) \
|
||||
; \
|
||||
else \
|
||||
on_failure(gtest_ar.failure_message())
|
||||
|
||||
|
||||
// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use
|
||||
// this in your code.
|
||||
template <typename Pred,
|
||||
typename T1>
|
||||
AssertionResult AssertPred1Helper(const char* pred_text,
|
||||
const char* e1,
|
||||
Pred pred,
|
||||
const T1& v1) {
|
||||
if (pred(v1)) return AssertionSuccess();
|
||||
|
||||
return AssertionFailure() << pred_text << "("
|
||||
<< e1 << ") evaluates to false, where"
|
||||
<< "\n" << e1 << " evaluates to " << v1;
|
||||
}
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
|
||||
// Don't use this in your code.
|
||||
#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\
|
||||
GTEST_ASSERT_(pred_format(#v1, v1), \
|
||||
on_failure)
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use
|
||||
// this in your code.
|
||||
#define GTEST_PRED1_(pred, v1, on_failure)\
|
||||
GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \
|
||||
#v1, \
|
||||
pred, \
|
||||
v1), on_failure)
|
||||
|
||||
// Unary predicate assertion macros.
|
||||
#define EXPECT_PRED_FORMAT1(pred_format, v1) \
|
||||
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
|
||||
#define EXPECT_PRED1(pred, v1) \
|
||||
GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
|
||||
#define ASSERT_PRED_FORMAT1(pred_format, v1) \
|
||||
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
|
||||
#define ASSERT_PRED1(pred, v1) \
|
||||
GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
|
||||
|
||||
|
||||
|
||||
// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use
|
||||
// this in your code.
|
||||
template <typename Pred,
|
||||
typename T1,
|
||||
typename T2>
|
||||
AssertionResult AssertPred2Helper(const char* pred_text,
|
||||
const char* e1,
|
||||
const char* e2,
|
||||
Pred pred,
|
||||
const T1& v1,
|
||||
const T2& v2) {
|
||||
if (pred(v1, v2)) return AssertionSuccess();
|
||||
|
||||
return AssertionFailure() << pred_text << "("
|
||||
<< e1 << ", "
|
||||
<< e2 << ") evaluates to false, where"
|
||||
<< "\n" << e1 << " evaluates to " << v1
|
||||
<< "\n" << e2 << " evaluates to " << v2;
|
||||
}
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
|
||||
// Don't use this in your code.
|
||||
#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\
|
||||
GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \
|
||||
on_failure)
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use
|
||||
// this in your code.
|
||||
#define GTEST_PRED2_(pred, v1, v2, on_failure)\
|
||||
GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \
|
||||
#v1, \
|
||||
#v2, \
|
||||
pred, \
|
||||
v1, \
|
||||
v2), on_failure)
|
||||
|
||||
// Binary predicate assertion macros.
|
||||
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
|
||||
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
|
||||
#define EXPECT_PRED2(pred, v1, v2) \
|
||||
GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
|
||||
#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
|
||||
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
|
||||
#define ASSERT_PRED2(pred, v1, v2) \
|
||||
GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
|
||||
|
||||
|
||||
|
||||
// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use
|
||||
// this in your code.
|
||||
template <typename Pred,
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3>
|
||||
AssertionResult AssertPred3Helper(const char* pred_text,
|
||||
const char* e1,
|
||||
const char* e2,
|
||||
const char* e3,
|
||||
Pred pred,
|
||||
const T1& v1,
|
||||
const T2& v2,
|
||||
const T3& v3) {
|
||||
if (pred(v1, v2, v3)) return AssertionSuccess();
|
||||
|
||||
return AssertionFailure() << pred_text << "("
|
||||
<< e1 << ", "
|
||||
<< e2 << ", "
|
||||
<< e3 << ") evaluates to false, where"
|
||||
<< "\n" << e1 << " evaluates to " << v1
|
||||
<< "\n" << e2 << " evaluates to " << v2
|
||||
<< "\n" << e3 << " evaluates to " << v3;
|
||||
}
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
|
||||
// Don't use this in your code.
|
||||
#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\
|
||||
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \
|
||||
on_failure)
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use
|
||||
// this in your code.
|
||||
#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\
|
||||
GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \
|
||||
#v1, \
|
||||
#v2, \
|
||||
#v3, \
|
||||
pred, \
|
||||
v1, \
|
||||
v2, \
|
||||
v3), on_failure)
|
||||
|
||||
// Ternary predicate assertion macros.
|
||||
#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
|
||||
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
|
||||
#define EXPECT_PRED3(pred, v1, v2, v3) \
|
||||
GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
|
||||
#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
|
||||
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
|
||||
#define ASSERT_PRED3(pred, v1, v2, v3) \
|
||||
GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
|
||||
|
||||
|
||||
|
||||
// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use
|
||||
// this in your code.
|
||||
template <typename Pred,
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4>
|
||||
AssertionResult AssertPred4Helper(const char* pred_text,
|
||||
const char* e1,
|
||||
const char* e2,
|
||||
const char* e3,
|
||||
const char* e4,
|
||||
Pred pred,
|
||||
const T1& v1,
|
||||
const T2& v2,
|
||||
const T3& v3,
|
||||
const T4& v4) {
|
||||
if (pred(v1, v2, v3, v4)) return AssertionSuccess();
|
||||
|
||||
return AssertionFailure() << pred_text << "("
|
||||
<< e1 << ", "
|
||||
<< e2 << ", "
|
||||
<< e3 << ", "
|
||||
<< e4 << ") evaluates to false, where"
|
||||
<< "\n" << e1 << " evaluates to " << v1
|
||||
<< "\n" << e2 << " evaluates to " << v2
|
||||
<< "\n" << e3 << " evaluates to " << v3
|
||||
<< "\n" << e4 << " evaluates to " << v4;
|
||||
}
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
|
||||
// Don't use this in your code.
|
||||
#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\
|
||||
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \
|
||||
on_failure)
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use
|
||||
// this in your code.
|
||||
#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\
|
||||
GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \
|
||||
#v1, \
|
||||
#v2, \
|
||||
#v3, \
|
||||
#v4, \
|
||||
pred, \
|
||||
v1, \
|
||||
v2, \
|
||||
v3, \
|
||||
v4), on_failure)
|
||||
|
||||
// 4-ary predicate assertion macros.
|
||||
#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
|
||||
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
|
||||
#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
|
||||
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
|
||||
#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
|
||||
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
|
||||
#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
|
||||
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
|
||||
|
||||
|
||||
|
||||
// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use
|
||||
// this in your code.
|
||||
template <typename Pred,
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5>
|
||||
AssertionResult AssertPred5Helper(const char* pred_text,
|
||||
const char* e1,
|
||||
const char* e2,
|
||||
const char* e3,
|
||||
const char* e4,
|
||||
const char* e5,
|
||||
Pred pred,
|
||||
const T1& v1,
|
||||
const T2& v2,
|
||||
const T3& v3,
|
||||
const T4& v4,
|
||||
const T5& v5) {
|
||||
if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
|
||||
|
||||
return AssertionFailure() << pred_text << "("
|
||||
<< e1 << ", "
|
||||
<< e2 << ", "
|
||||
<< e3 << ", "
|
||||
<< e4 << ", "
|
||||
<< e5 << ") evaluates to false, where"
|
||||
<< "\n" << e1 << " evaluates to " << v1
|
||||
<< "\n" << e2 << " evaluates to " << v2
|
||||
<< "\n" << e3 << " evaluates to " << v3
|
||||
<< "\n" << e4 << " evaluates to " << v4
|
||||
<< "\n" << e5 << " evaluates to " << v5;
|
||||
}
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
|
||||
// Don't use this in your code.
|
||||
#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\
|
||||
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
|
||||
on_failure)
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use
|
||||
// this in your code.
|
||||
#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\
|
||||
GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \
|
||||
#v1, \
|
||||
#v2, \
|
||||
#v3, \
|
||||
#v4, \
|
||||
#v5, \
|
||||
pred, \
|
||||
v1, \
|
||||
v2, \
|
||||
v3, \
|
||||
v4, \
|
||||
v5), on_failure)
|
||||
|
||||
// 5-ary predicate assertion macros.
|
||||
#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
|
||||
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
|
||||
#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
|
||||
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
|
||||
#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
|
||||
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
|
||||
#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
|
||||
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
|
||||
|
||||
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||
58
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest_prod.h
vendored
Normal file
58
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/gtest_prod.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
//
|
||||
// Google C++ Testing Framework definitions useful in production code.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
|
||||
|
||||
// When you need to test the private or protected members of a class,
|
||||
// use the FRIEND_TEST macro to declare your tests as friends of the
|
||||
// class. For example:
|
||||
//
|
||||
// class MyClass {
|
||||
// private:
|
||||
// void MyMethod();
|
||||
// FRIEND_TEST(MyClassTest, MyMethod);
|
||||
// };
|
||||
//
|
||||
// class MyClassTest : public testing::Test {
|
||||
// // ...
|
||||
// };
|
||||
//
|
||||
// TEST_F(MyClassTest, MyMethod) {
|
||||
// // Can call MyClass::MyMethod() here.
|
||||
// }
|
||||
|
||||
#define FRIEND_TEST(test_case_name, test_name)\
|
||||
friend class test_case_name##_##test_name##_Test
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2015, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Injection point for custom user configurations.
|
||||
// The following macros can be defined:
|
||||
//
|
||||
// Flag related macros:
|
||||
// GTEST_FLAG(flag_name)
|
||||
// GTEST_USE_OWN_FLAGFILE_FLAG_ - Define to 0 when the system provides its
|
||||
// own flagfile flag parsing.
|
||||
// GTEST_DECLARE_bool_(name)
|
||||
// GTEST_DECLARE_int32_(name)
|
||||
// GTEST_DECLARE_string_(name)
|
||||
// GTEST_DEFINE_bool_(name, default_val, doc)
|
||||
// GTEST_DEFINE_int32_(name, default_val, doc)
|
||||
// GTEST_DEFINE_string_(name, default_val, doc)
|
||||
//
|
||||
// Test filtering:
|
||||
// GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that
|
||||
// will be used if --GTEST_FLAG(test_filter)
|
||||
// is not provided.
|
||||
//
|
||||
// Logging:
|
||||
// GTEST_LOG_(severity)
|
||||
// GTEST_CHECK_(condition)
|
||||
// Functions LogToStderr() and FlushInfoLog() have to be provided too.
|
||||
//
|
||||
// Threading:
|
||||
// GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided.
|
||||
// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are
|
||||
// already provided.
|
||||
// Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and
|
||||
// GTEST_DEFINE_STATIC_MUTEX_(mutex)
|
||||
//
|
||||
// GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
|
||||
// GTEST_LOCK_EXCLUDED_(locks)
|
||||
//
|
||||
// ** Custom implementation starts here **
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2015, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This file provides an injection point for custom printers in a local
|
||||
// installation of gTest.
|
||||
// It will be included from gtest-printers.h and the overrides in this file
|
||||
// will be visible to everyone.
|
||||
// See documentation at gtest/gtest-printers.h for details on how to define a
|
||||
// custom printer.
|
||||
//
|
||||
// ** Custom implementation starts here **
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2015, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Injection point for custom user configurations.
|
||||
// The following macros can be defined:
|
||||
//
|
||||
// GTEST_OS_STACK_TRACE_GETTER_ - The name of an implementation of
|
||||
// OsStackTraceGetterInterface.
|
||||
//
|
||||
// ** Custom implementation starts here **
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
|
||||
@@ -0,0 +1,319 @@
|
||||
// Copyright 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
|
||||
//
|
||||
// The Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// This header file defines internal utilities needed for implementing
|
||||
// death tests. They are subject to change without notice.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
|
||||
|
||||
#include "gtest/internal/gtest-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
GTEST_DECLARE_string_(internal_run_death_test);
|
||||
|
||||
// Names of the flags (needed for parsing Google Test flags).
|
||||
const char kDeathTestStyleFlag[] = "death_test_style";
|
||||
const char kDeathTestUseFork[] = "death_test_use_fork";
|
||||
const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
|
||||
// DeathTest is a class that hides much of the complexity of the
|
||||
// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
|
||||
// returns a concrete class that depends on the prevailing death test
|
||||
// style, as defined by the --gtest_death_test_style and/or
|
||||
// --gtest_internal_run_death_test flags.
|
||||
|
||||
// In describing the results of death tests, these terms are used with
|
||||
// the corresponding definitions:
|
||||
//
|
||||
// exit status: The integer exit information in the format specified
|
||||
// by wait(2)
|
||||
// exit code: The integer code passed to exit(3), _exit(2), or
|
||||
// returned from main()
|
||||
class GTEST_API_ DeathTest {
|
||||
public:
|
||||
// Create returns false if there was an error determining the
|
||||
// appropriate action to take for the current death test; for example,
|
||||
// if the gtest_death_test_style flag is set to an invalid value.
|
||||
// The LastMessage method will return a more detailed message in that
|
||||
// case. Otherwise, the DeathTest pointer pointed to by the "test"
|
||||
// argument is set. If the death test should be skipped, the pointer
|
||||
// is set to NULL; otherwise, it is set to the address of a new concrete
|
||||
// DeathTest object that controls the execution of the current test.
|
||||
static bool Create(const char* statement, const RE* regex,
|
||||
const char* file, int line, DeathTest** test);
|
||||
DeathTest();
|
||||
virtual ~DeathTest() { }
|
||||
|
||||
// A helper class that aborts a death test when it's deleted.
|
||||
class ReturnSentinel {
|
||||
public:
|
||||
explicit ReturnSentinel(DeathTest* test) : test_(test) { }
|
||||
~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
|
||||
private:
|
||||
DeathTest* const test_;
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel);
|
||||
} GTEST_ATTRIBUTE_UNUSED_;
|
||||
|
||||
// An enumeration of possible roles that may be taken when a death
|
||||
// test is encountered. EXECUTE means that the death test logic should
|
||||
// be executed immediately. OVERSEE means that the program should prepare
|
||||
// the appropriate environment for a child process to execute the death
|
||||
// test, then wait for it to complete.
|
||||
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
|
||||
|
||||
// An enumeration of the three reasons that a test might be aborted.
|
||||
enum AbortReason {
|
||||
TEST_ENCOUNTERED_RETURN_STATEMENT,
|
||||
TEST_THREW_EXCEPTION,
|
||||
TEST_DID_NOT_DIE
|
||||
};
|
||||
|
||||
// Assumes one of the above roles.
|
||||
virtual TestRole AssumeRole() = 0;
|
||||
|
||||
// Waits for the death test to finish and returns its status.
|
||||
virtual int Wait() = 0;
|
||||
|
||||
// Returns true if the death test passed; that is, the test process
|
||||
// exited during the test, its exit status matches a user-supplied
|
||||
// predicate, and its stderr output matches a user-supplied regular
|
||||
// expression.
|
||||
// The user-supplied predicate may be a macro expression rather
|
||||
// than a function pointer or functor, or else Wait and Passed could
|
||||
// be combined.
|
||||
virtual bool Passed(bool exit_status_ok) = 0;
|
||||
|
||||
// Signals that the death test did not die as expected.
|
||||
virtual void Abort(AbortReason reason) = 0;
|
||||
|
||||
// Returns a human-readable outcome message regarding the outcome of
|
||||
// the last death test.
|
||||
static const char* LastMessage();
|
||||
|
||||
static void set_last_death_test_message(const std::string& message);
|
||||
|
||||
private:
|
||||
// A string containing a description of the outcome of the last death test.
|
||||
static std::string last_death_test_message_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
|
||||
};
|
||||
|
||||
// Factory interface for death tests. May be mocked out for testing.
|
||||
class DeathTestFactory {
|
||||
public:
|
||||
virtual ~DeathTestFactory() { }
|
||||
virtual bool Create(const char* statement, const RE* regex,
|
||||
const char* file, int line, DeathTest** test) = 0;
|
||||
};
|
||||
|
||||
// A concrete DeathTestFactory implementation for normal use.
|
||||
class DefaultDeathTestFactory : public DeathTestFactory {
|
||||
public:
|
||||
virtual bool Create(const char* statement, const RE* regex,
|
||||
const char* file, int line, DeathTest** test);
|
||||
};
|
||||
|
||||
// Returns true if exit_status describes a process that was terminated
|
||||
// by a signal, or exited normally with a nonzero exit code.
|
||||
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
|
||||
|
||||
// Traps C++ exceptions escaping statement and reports them as test
|
||||
// failures. Note that trapping SEH exceptions is not implemented here.
|
||||
# if GTEST_HAS_EXCEPTIONS
|
||||
# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
|
||||
try { \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
} catch (const ::std::exception& gtest_exception) { \
|
||||
fprintf(\
|
||||
stderr, \
|
||||
"\n%s: Caught std::exception-derived exception escaping the " \
|
||||
"death test statement. Exception message: %s\n", \
|
||||
::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
|
||||
gtest_exception.what()); \
|
||||
fflush(stderr); \
|
||||
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
|
||||
} catch (...) { \
|
||||
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
|
||||
}
|
||||
|
||||
# else
|
||||
# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
|
||||
|
||||
# endif
|
||||
|
||||
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
|
||||
// ASSERT_EXIT*, and EXPECT_EXIT*.
|
||||
# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::internal::AlwaysTrue()) { \
|
||||
const ::testing::internal::RE& gtest_regex = (regex); \
|
||||
::testing::internal::DeathTest* gtest_dt; \
|
||||
if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \
|
||||
__FILE__, __LINE__, >est_dt)) { \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
|
||||
} \
|
||||
if (gtest_dt != NULL) { \
|
||||
::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \
|
||||
gtest_dt_ptr(gtest_dt); \
|
||||
switch (gtest_dt->AssumeRole()) { \
|
||||
case ::testing::internal::DeathTest::OVERSEE_TEST: \
|
||||
if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
|
||||
} \
|
||||
break; \
|
||||
case ::testing::internal::DeathTest::EXECUTE_TEST: { \
|
||||
::testing::internal::DeathTest::ReturnSentinel \
|
||||
gtest_sentinel(gtest_dt); \
|
||||
GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
|
||||
gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
|
||||
break; \
|
||||
} \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} else \
|
||||
GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \
|
||||
fail(::testing::internal::DeathTest::LastMessage())
|
||||
// The symbol "fail" here expands to something into which a message
|
||||
// can be streamed.
|
||||
|
||||
// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
|
||||
// NDEBUG mode. In this case we need the statements to be executed, the regex is
|
||||
// ignored, and the macro must accept a streamed message even though the message
|
||||
// is never printed.
|
||||
# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::internal::AlwaysTrue()) { \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
} else \
|
||||
::testing::Message()
|
||||
|
||||
// A class representing the parsed contents of the
|
||||
// --gtest_internal_run_death_test flag, as it existed when
|
||||
// RUN_ALL_TESTS was called.
|
||||
class InternalRunDeathTestFlag {
|
||||
public:
|
||||
InternalRunDeathTestFlag(const std::string& a_file,
|
||||
int a_line,
|
||||
int an_index,
|
||||
int a_write_fd)
|
||||
: file_(a_file), line_(a_line), index_(an_index),
|
||||
write_fd_(a_write_fd) {}
|
||||
|
||||
~InternalRunDeathTestFlag() {
|
||||
if (write_fd_ >= 0)
|
||||
posix::Close(write_fd_);
|
||||
}
|
||||
|
||||
const std::string& file() const { return file_; }
|
||||
int line() const { return line_; }
|
||||
int index() const { return index_; }
|
||||
int write_fd() const { return write_fd_; }
|
||||
|
||||
private:
|
||||
std::string file_;
|
||||
int line_;
|
||||
int index_;
|
||||
int write_fd_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag);
|
||||
};
|
||||
|
||||
// Returns a newly created InternalRunDeathTestFlag object with fields
|
||||
// initialized from the GTEST_FLAG(internal_run_death_test) flag if
|
||||
// the flag is specified; otherwise returns NULL.
|
||||
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
|
||||
|
||||
#else // GTEST_HAS_DEATH_TEST
|
||||
|
||||
// This macro is used for implementing macros such as
|
||||
// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
|
||||
// death tests are not supported. Those macros must compile on such systems
|
||||
// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
|
||||
// systems that support death tests. This allows one to write such a macro
|
||||
// on a system that does not support death tests and be sure that it will
|
||||
// compile on a death-test supporting system.
|
||||
//
|
||||
// Parameters:
|
||||
// statement - A statement that a macro such as EXPECT_DEATH would test
|
||||
// for program termination. This macro has to make sure this
|
||||
// statement is compiled but not executed, to ensure that
|
||||
// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
|
||||
// parameter iff EXPECT_DEATH compiles with it.
|
||||
// regex - A regex that a macro such as EXPECT_DEATH would use to test
|
||||
// the output of statement. This parameter has to be
|
||||
// compiled but not evaluated by this macro, to ensure that
|
||||
// this macro only accepts expressions that a macro such as
|
||||
// EXPECT_DEATH would accept.
|
||||
// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
|
||||
// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
|
||||
// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
|
||||
// compile inside functions where ASSERT_DEATH doesn't
|
||||
// compile.
|
||||
//
|
||||
// The branch that has an always false condition is used to ensure that
|
||||
// statement and regex are compiled (and thus syntactically correct) but
|
||||
// never executed. The unreachable code macro protects the terminator
|
||||
// statement from generating an 'unreachable code' warning in case
|
||||
// statement unconditionally returns or throws. The Message constructor at
|
||||
// the end allows the syntax of streaming additional messages into the
|
||||
// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
|
||||
# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::internal::AlwaysTrue()) { \
|
||||
GTEST_LOG_(WARNING) \
|
||||
<< "Death tests are not supported on this platform.\n" \
|
||||
<< "Statement '" #statement "' cannot be verified."; \
|
||||
} else if (::testing::internal::AlwaysFalse()) { \
|
||||
::testing::internal::RE::PartialMatch(".*", (regex)); \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
terminator; \
|
||||
} else \
|
||||
::testing::Message()
|
||||
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
|
||||
206
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-filepath.h
vendored
Normal file
206
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-filepath.h
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright 2008, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: keith.ray@gmail.com (Keith Ray)
|
||||
//
|
||||
// Google Test filepath utilities
|
||||
//
|
||||
// This header file declares classes and functions used internally by
|
||||
// Google Test. They are subject to change without notice.
|
||||
//
|
||||
// This file is #included in <gtest/internal/gtest-internal.h>.
|
||||
// Do not include this header file separately!
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
||||
|
||||
#include "gtest/internal/gtest-string.h"
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
// FilePath - a class for file and directory pathname manipulation which
|
||||
// handles platform-specific conventions (like the pathname separator).
|
||||
// Used for helper functions for naming files in a directory for xml output.
|
||||
// Except for Set methods, all methods are const or static, which provides an
|
||||
// "immutable value object" -- useful for peace of mind.
|
||||
// A FilePath with a value ending in a path separator ("like/this/") represents
|
||||
// a directory, otherwise it is assumed to represent a file. In either case,
|
||||
// it may or may not represent an actual file or directory in the file system.
|
||||
// Names are NOT checked for syntax correctness -- no checking for illegal
|
||||
// characters, malformed paths, etc.
|
||||
|
||||
class GTEST_API_ FilePath {
|
||||
public:
|
||||
FilePath() : pathname_("") { }
|
||||
FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
|
||||
|
||||
explicit FilePath(const std::string& pathname) : pathname_(pathname) {
|
||||
Normalize();
|
||||
}
|
||||
|
||||
FilePath& operator=(const FilePath& rhs) {
|
||||
Set(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Set(const FilePath& rhs) {
|
||||
pathname_ = rhs.pathname_;
|
||||
}
|
||||
|
||||
const std::string& string() const { return pathname_; }
|
||||
const char* c_str() const { return pathname_.c_str(); }
|
||||
|
||||
// Returns the current working directory, or "" if unsuccessful.
|
||||
static FilePath GetCurrentDir();
|
||||
|
||||
// Given directory = "dir", base_name = "test", number = 0,
|
||||
// extension = "xml", returns "dir/test.xml". If number is greater
|
||||
// than zero (e.g., 12), returns "dir/test_12.xml".
|
||||
// On Windows platform, uses \ as the separator rather than /.
|
||||
static FilePath MakeFileName(const FilePath& directory,
|
||||
const FilePath& base_name,
|
||||
int number,
|
||||
const char* extension);
|
||||
|
||||
// Given directory = "dir", relative_path = "test.xml",
|
||||
// returns "dir/test.xml".
|
||||
// On Windows, uses \ as the separator rather than /.
|
||||
static FilePath ConcatPaths(const FilePath& directory,
|
||||
const FilePath& relative_path);
|
||||
|
||||
// Returns a pathname for a file that does not currently exist. The pathname
|
||||
// will be directory/base_name.extension or
|
||||
// directory/base_name_<number>.extension if directory/base_name.extension
|
||||
// already exists. The number will be incremented until a pathname is found
|
||||
// that does not already exist.
|
||||
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
|
||||
// There could be a race condition if two or more processes are calling this
|
||||
// function at the same time -- they could both pick the same filename.
|
||||
static FilePath GenerateUniqueFileName(const FilePath& directory,
|
||||
const FilePath& base_name,
|
||||
const char* extension);
|
||||
|
||||
// Returns true iff the path is "".
|
||||
bool IsEmpty() const { return pathname_.empty(); }
|
||||
|
||||
// If input name has a trailing separator character, removes it and returns
|
||||
// the name, otherwise return the name string unmodified.
|
||||
// On Windows platform, uses \ as the separator, other platforms use /.
|
||||
FilePath RemoveTrailingPathSeparator() const;
|
||||
|
||||
// Returns a copy of the FilePath with the directory part removed.
|
||||
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
|
||||
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
|
||||
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
|
||||
// returns an empty FilePath ("").
|
||||
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||
FilePath RemoveDirectoryName() const;
|
||||
|
||||
// RemoveFileName returns the directory path with the filename removed.
|
||||
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
|
||||
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
|
||||
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
|
||||
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
|
||||
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||
FilePath RemoveFileName() const;
|
||||
|
||||
// Returns a copy of the FilePath with the case-insensitive extension removed.
|
||||
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
|
||||
// FilePath("dir/file"). If a case-insensitive extension is not
|
||||
// found, returns a copy of the original FilePath.
|
||||
FilePath RemoveExtension(const char* extension) const;
|
||||
|
||||
// Creates directories so that path exists. Returns true if successful or if
|
||||
// the directories already exist; returns false if unable to create
|
||||
// directories for any reason. Will also return false if the FilePath does
|
||||
// not represent a directory (that is, it doesn't end with a path separator).
|
||||
bool CreateDirectoriesRecursively() const;
|
||||
|
||||
// Create the directory so that path exists. Returns true if successful or
|
||||
// if the directory already exists; returns false if unable to create the
|
||||
// directory for any reason, including if the parent directory does not
|
||||
// exist. Not named "CreateDirectory" because that's a macro on Windows.
|
||||
bool CreateFolder() const;
|
||||
|
||||
// Returns true if FilePath describes something in the file-system,
|
||||
// either a file, directory, or whatever, and that something exists.
|
||||
bool FileOrDirectoryExists() const;
|
||||
|
||||
// Returns true if pathname describes a directory in the file-system
|
||||
// that exists.
|
||||
bool DirectoryExists() const;
|
||||
|
||||
// Returns true if FilePath ends with a path separator, which indicates that
|
||||
// it is intended to represent a directory. Returns false otherwise.
|
||||
// This does NOT check that a directory (or file) actually exists.
|
||||
bool IsDirectory() const;
|
||||
|
||||
// Returns true if pathname describes a root directory. (Windows has one
|
||||
// root directory per disk drive.)
|
||||
bool IsRootDirectory() const;
|
||||
|
||||
// Returns true if pathname describes an absolute path.
|
||||
bool IsAbsolutePath() const;
|
||||
|
||||
private:
|
||||
// Replaces multiple consecutive separators with a single separator.
|
||||
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
||||
// redundancies that might be in a pathname involving "." or "..".
|
||||
//
|
||||
// A pathname with multiple consecutive separators may occur either through
|
||||
// user error or as a result of some scripts or APIs that generate a pathname
|
||||
// with a trailing separator. On other platforms the same API or script
|
||||
// may NOT generate a pathname with a trailing "/". Then elsewhere that
|
||||
// pathname may have another "/" and pathname components added to it,
|
||||
// without checking for the separator already being there.
|
||||
// The script language and operating system may allow paths like "foo//bar"
|
||||
// but some of the functions in FilePath will not handle that correctly. In
|
||||
// particular, RemoveTrailingPathSeparator() only removes one separator, and
|
||||
// it is called in CreateDirectoriesRecursively() assuming that it will change
|
||||
// a pathname from directory syntax (trailing separator) to filename syntax.
|
||||
//
|
||||
// On Windows this method also replaces the alternate path separator '/' with
|
||||
// the primary path separator '\\', so that for example "bar\\/\\foo" becomes
|
||||
// "bar\\foo".
|
||||
|
||||
void Normalize();
|
||||
|
||||
// Returns a pointer to the last occurence of a valid path separator in
|
||||
// the FilePath. On Windows, for example, both '/' and '\' are valid path
|
||||
// separators. Returns NULL if no path separator was found.
|
||||
const char* FindLastPathSeparator() const;
|
||||
|
||||
std::string pathname_;
|
||||
}; // class FilePath
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
||||
1238
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-internal.h
vendored
Normal file
1238
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-internal.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
// Copyright 2003 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Authors: Dan Egnor (egnor@google.com)
|
||||
//
|
||||
// A "smart" pointer type with reference tracking. Every pointer to a
|
||||
// particular object is kept on a circular linked list. When the last pointer
|
||||
// to an object is destroyed or reassigned, the object is deleted.
|
||||
//
|
||||
// Used properly, this deletes the object when the last reference goes away.
|
||||
// There are several caveats:
|
||||
// - Like all reference counting schemes, cycles lead to leaks.
|
||||
// - Each smart pointer is actually two pointers (8 bytes instead of 4).
|
||||
// - Every time a pointer is assigned, the entire list of pointers to that
|
||||
// object is traversed. This class is therefore NOT SUITABLE when there
|
||||
// will often be more than two or three pointers to a particular object.
|
||||
// - References are only tracked as long as linked_ptr<> objects are copied.
|
||||
// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
|
||||
// will happen (double deletion).
|
||||
//
|
||||
// A good use of this class is storing object references in STL containers.
|
||||
// You can safely put linked_ptr<> in a vector<>.
|
||||
// Other uses may not be as good.
|
||||
//
|
||||
// Note: If you use an incomplete type with linked_ptr<>, the class
|
||||
// *containing* linked_ptr<> must have a constructor and destructor (even
|
||||
// if they do nothing!).
|
||||
//
|
||||
// Bill Gibbons suggested we use something like this.
|
||||
//
|
||||
// Thread Safety:
|
||||
// Unlike other linked_ptr implementations, in this implementation
|
||||
// a linked_ptr object is thread-safe in the sense that:
|
||||
// - it's safe to copy linked_ptr objects concurrently,
|
||||
// - it's safe to copy *from* a linked_ptr and read its underlying
|
||||
// raw pointer (e.g. via get()) concurrently, and
|
||||
// - it's safe to write to two linked_ptrs that point to the same
|
||||
// shared object concurrently.
|
||||
// TODO(wan@google.com): rename this to safe_linked_ptr to avoid
|
||||
// confusion with normal linked_ptr.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
// Protects copying of all linked_ptr objects.
|
||||
GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex);
|
||||
|
||||
// This is used internally by all instances of linked_ptr<>. It needs to be
|
||||
// a non-template class because different types of linked_ptr<> can refer to
|
||||
// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
|
||||
// So, it needs to be possible for different types of linked_ptr to participate
|
||||
// in the same circular linked list, so we need a single class type here.
|
||||
//
|
||||
// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
|
||||
class linked_ptr_internal {
|
||||
public:
|
||||
// Create a new circle that includes only this instance.
|
||||
void join_new() {
|
||||
next_ = this;
|
||||
}
|
||||
|
||||
// Many linked_ptr operations may change p.link_ for some linked_ptr
|
||||
// variable p in the same circle as this object. Therefore we need
|
||||
// to prevent two such operations from occurring concurrently.
|
||||
//
|
||||
// Note that different types of linked_ptr objects can coexist in a
|
||||
// circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and
|
||||
// linked_ptr<Derived2>). Therefore we must use a single mutex to
|
||||
// protect all linked_ptr objects. This can create serious
|
||||
// contention in production code, but is acceptable in a testing
|
||||
// framework.
|
||||
|
||||
// Join an existing circle.
|
||||
void join(linked_ptr_internal const* ptr)
|
||||
GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
|
||||
MutexLock lock(&g_linked_ptr_mutex);
|
||||
|
||||
linked_ptr_internal const* p = ptr;
|
||||
while (p->next_ != ptr) {
|
||||
assert(p->next_ != this &&
|
||||
"Trying to join() a linked ring we are already in. "
|
||||
"Is GMock thread safety enabled?");
|
||||
p = p->next_;
|
||||
}
|
||||
p->next_ = this;
|
||||
next_ = ptr;
|
||||
}
|
||||
|
||||
// Leave whatever circle we're part of. Returns true if we were the
|
||||
// last member of the circle. Once this is done, you can join() another.
|
||||
bool depart()
|
||||
GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
|
||||
MutexLock lock(&g_linked_ptr_mutex);
|
||||
|
||||
if (next_ == this) return true;
|
||||
linked_ptr_internal const* p = next_;
|
||||
while (p->next_ != this) {
|
||||
assert(p->next_ != next_ &&
|
||||
"Trying to depart() a linked ring we are not in. "
|
||||
"Is GMock thread safety enabled?");
|
||||
p = p->next_;
|
||||
}
|
||||
p->next_ = next_;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable linked_ptr_internal const* next_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class linked_ptr {
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
// Take over ownership of a raw pointer. This should happen as soon as
|
||||
// possible after the object is created.
|
||||
explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
|
||||
~linked_ptr() { depart(); }
|
||||
|
||||
// Copy an existing linked_ptr<>, adding ourselves to the list of references.
|
||||
template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
|
||||
linked_ptr(linked_ptr const& ptr) { // NOLINT
|
||||
assert(&ptr != this);
|
||||
copy(&ptr);
|
||||
}
|
||||
|
||||
// Assignment releases the old value and acquires the new.
|
||||
template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
|
||||
depart();
|
||||
copy(&ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
linked_ptr& operator=(linked_ptr const& ptr) {
|
||||
if (&ptr != this) {
|
||||
depart();
|
||||
copy(&ptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Smart pointer members.
|
||||
void reset(T* ptr = NULL) {
|
||||
depart();
|
||||
capture(ptr);
|
||||
}
|
||||
T* get() const { return value_; }
|
||||
T* operator->() const { return value_; }
|
||||
T& operator*() const { return *value_; }
|
||||
|
||||
bool operator==(T* p) const { return value_ == p; }
|
||||
bool operator!=(T* p) const { return value_ != p; }
|
||||
template <typename U>
|
||||
bool operator==(linked_ptr<U> const& ptr) const {
|
||||
return value_ == ptr.get();
|
||||
}
|
||||
template <typename U>
|
||||
bool operator!=(linked_ptr<U> const& ptr) const {
|
||||
return value_ != ptr.get();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
friend class linked_ptr;
|
||||
|
||||
T* value_;
|
||||
linked_ptr_internal link_;
|
||||
|
||||
void depart() {
|
||||
if (link_.depart()) delete value_;
|
||||
}
|
||||
|
||||
void capture(T* ptr) {
|
||||
value_ = ptr;
|
||||
link_.join_new();
|
||||
}
|
||||
|
||||
template <typename U> void copy(linked_ptr<U> const* ptr) {
|
||||
value_ = ptr->get();
|
||||
if (value_)
|
||||
link_.join(&ptr->link_);
|
||||
else
|
||||
link_.join_new();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> inline
|
||||
bool operator==(T* ptr, const linked_ptr<T>& x) {
|
||||
return ptr == x.get();
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator!=(T* ptr, const linked_ptr<T>& x) {
|
||||
return ptr != x.get();
|
||||
}
|
||||
|
||||
// A function to convert T* into linked_ptr<T>
|
||||
// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
|
||||
// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
|
||||
template <typename T>
|
||||
linked_ptr<T> make_linked_ptr(T* ptr) {
|
||||
return linked_ptr<T>(ptr);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,286 @@
|
||||
$$ -*- mode: c++; -*-
|
||||
$var n = 50 $$ Maximum length of Values arguments we want to support.
|
||||
$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support.
|
||||
// Copyright 2008 Google Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: vladl@google.com (Vlad Losev)
|
||||
|
||||
// Type and function utilities for implementing parameterized tests.
|
||||
// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
|
||||
//
|
||||
// Currently Google Test supports at most $n arguments in Values,
|
||||
// and at most $maxtuple arguments in Combine. Please contact
|
||||
// googletestframework@googlegroups.com if you need more.
|
||||
// Please note that the number of arguments to Combine is limited
|
||||
// by the maximum arity of the implementation of tuple which is
|
||||
// currently set at $maxtuple.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
|
||||
|
||||
// scripts/fuse_gtest.py depends on gtest's own header being #included
|
||||
// *unconditionally*. Therefore these #includes cannot be moved
|
||||
// inside #if GTEST_HAS_PARAM_TEST.
|
||||
#include "gtest/internal/gtest-param-util.h"
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
#if GTEST_HAS_PARAM_TEST
|
||||
|
||||
namespace testing {
|
||||
|
||||
// Forward declarations of ValuesIn(), which is implemented in
|
||||
// include/gtest/gtest-param-test.h.
|
||||
template <typename ForwardIterator>
|
||||
internal::ParamGenerator<
|
||||
typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
|
||||
ValuesIn(ForwardIterator begin, ForwardIterator end);
|
||||
|
||||
template <typename T, size_t N>
|
||||
internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
|
||||
|
||||
template <class Container>
|
||||
internal::ParamGenerator<typename Container::value_type> ValuesIn(
|
||||
const Container& container);
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Used in the Values() function to provide polymorphic capabilities.
|
||||
$range i 1..n
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
|
||||
template <$for j, [[typename T$j]]>
|
||||
class ValueArray$i {
|
||||
public:
|
||||
$if i==1 [[explicit ]]ValueArray$i($for j, [[T$j v$j]]) : $for j, [[v$(j)_(v$j)]] {}
|
||||
|
||||
template <typename T>
|
||||
operator ParamGenerator<T>() const {
|
||||
const T array[] = {$for j, [[static_cast<T>(v$(j)_)]]};
|
||||
return ValuesIn(array);
|
||||
}
|
||||
|
||||
private:
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const ValueArray$i& other);
|
||||
|
||||
$for j [[
|
||||
|
||||
const T$j v$(j)_;
|
||||
]]
|
||||
|
||||
};
|
||||
|
||||
]]
|
||||
|
||||
# if GTEST_HAS_COMBINE
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Generates values from the Cartesian product of values produced
|
||||
// by the argument generators.
|
||||
//
|
||||
$range i 2..maxtuple
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
$range k 2..i
|
||||
|
||||
template <$for j, [[typename T$j]]>
|
||||
class CartesianProductGenerator$i
|
||||
: public ParamGeneratorInterface< ::testing::tuple<$for j, [[T$j]]> > {
|
||||
public:
|
||||
typedef ::testing::tuple<$for j, [[T$j]]> ParamType;
|
||||
|
||||
CartesianProductGenerator$i($for j, [[const ParamGenerator<T$j>& g$j]])
|
||||
: $for j, [[g$(j)_(g$j)]] {}
|
||||
virtual ~CartesianProductGenerator$i() {}
|
||||
|
||||
virtual ParamIteratorInterface<ParamType>* Begin() const {
|
||||
return new Iterator(this, $for j, [[g$(j)_, g$(j)_.begin()]]);
|
||||
}
|
||||
virtual ParamIteratorInterface<ParamType>* End() const {
|
||||
return new Iterator(this, $for j, [[g$(j)_, g$(j)_.end()]]);
|
||||
}
|
||||
|
||||
private:
|
||||
class Iterator : public ParamIteratorInterface<ParamType> {
|
||||
public:
|
||||
Iterator(const ParamGeneratorInterface<ParamType>* base, $for j, [[
|
||||
|
||||
const ParamGenerator<T$j>& g$j,
|
||||
const typename ParamGenerator<T$j>::iterator& current$(j)]])
|
||||
: base_(base),
|
||||
$for j, [[
|
||||
|
||||
begin$(j)_(g$j.begin()), end$(j)_(g$j.end()), current$(j)_(current$j)
|
||||
]] {
|
||||
ComputeCurrentValue();
|
||||
}
|
||||
virtual ~Iterator() {}
|
||||
|
||||
virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
|
||||
return base_;
|
||||
}
|
||||
// Advance should not be called on beyond-of-range iterators
|
||||
// so no component iterators must be beyond end of range, either.
|
||||
virtual void Advance() {
|
||||
assert(!AtEnd());
|
||||
++current$(i)_;
|
||||
|
||||
$for k [[
|
||||
if (current$(i+2-k)_ == end$(i+2-k)_) {
|
||||
current$(i+2-k)_ = begin$(i+2-k)_;
|
||||
++current$(i+2-k-1)_;
|
||||
}
|
||||
|
||||
]]
|
||||
ComputeCurrentValue();
|
||||
}
|
||||
virtual ParamIteratorInterface<ParamType>* Clone() const {
|
||||
return new Iterator(*this);
|
||||
}
|
||||
virtual const ParamType* Current() const { return ¤t_value_; }
|
||||
virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
|
||||
// Having the same base generator guarantees that the other
|
||||
// iterator is of the same type and we can downcast.
|
||||
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
|
||||
<< "The program attempted to compare iterators "
|
||||
<< "from different generators." << std::endl;
|
||||
const Iterator* typed_other =
|
||||
CheckedDowncastToActualType<const Iterator>(&other);
|
||||
// We must report iterators equal if they both point beyond their
|
||||
// respective ranges. That can happen in a variety of fashions,
|
||||
// so we have to consult AtEnd().
|
||||
return (AtEnd() && typed_other->AtEnd()) ||
|
||||
($for j && [[
|
||||
|
||||
current$(j)_ == typed_other->current$(j)_
|
||||
]]);
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator(const Iterator& other)
|
||||
: base_(other.base_), $for j, [[
|
||||
|
||||
begin$(j)_(other.begin$(j)_),
|
||||
end$(j)_(other.end$(j)_),
|
||||
current$(j)_(other.current$(j)_)
|
||||
]] {
|
||||
ComputeCurrentValue();
|
||||
}
|
||||
|
||||
void ComputeCurrentValue() {
|
||||
if (!AtEnd())
|
||||
current_value_ = ParamType($for j, [[*current$(j)_]]);
|
||||
}
|
||||
bool AtEnd() const {
|
||||
// We must report iterator past the end of the range when either of the
|
||||
// component iterators has reached the end of its range.
|
||||
return
|
||||
$for j || [[
|
||||
|
||||
current$(j)_ == end$(j)_
|
||||
]];
|
||||
}
|
||||
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const Iterator& other);
|
||||
|
||||
const ParamGeneratorInterface<ParamType>* const base_;
|
||||
// begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
|
||||
// current[i]_ is the actual traversing iterator.
|
||||
$for j [[
|
||||
|
||||
const typename ParamGenerator<T$j>::iterator begin$(j)_;
|
||||
const typename ParamGenerator<T$j>::iterator end$(j)_;
|
||||
typename ParamGenerator<T$j>::iterator current$(j)_;
|
||||
]]
|
||||
|
||||
ParamType current_value_;
|
||||
}; // class CartesianProductGenerator$i::Iterator
|
||||
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const CartesianProductGenerator$i& other);
|
||||
|
||||
|
||||
$for j [[
|
||||
const ParamGenerator<T$j> g$(j)_;
|
||||
|
||||
]]
|
||||
}; // class CartesianProductGenerator$i
|
||||
|
||||
|
||||
]]
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Helper classes providing Combine() with polymorphic features. They allow
|
||||
// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
|
||||
// convertible to U.
|
||||
//
|
||||
$range i 2..maxtuple
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
|
||||
template <$for j, [[class Generator$j]]>
|
||||
class CartesianProductHolder$i {
|
||||
public:
|
||||
CartesianProductHolder$i($for j, [[const Generator$j& g$j]])
|
||||
: $for j, [[g$(j)_(g$j)]] {}
|
||||
template <$for j, [[typename T$j]]>
|
||||
operator ParamGenerator< ::testing::tuple<$for j, [[T$j]]> >() const {
|
||||
return ParamGenerator< ::testing::tuple<$for j, [[T$j]]> >(
|
||||
new CartesianProductGenerator$i<$for j, [[T$j]]>(
|
||||
$for j,[[
|
||||
|
||||
static_cast<ParamGenerator<T$j> >(g$(j)_)
|
||||
]]));
|
||||
}
|
||||
|
||||
private:
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const CartesianProductHolder$i& other);
|
||||
|
||||
|
||||
$for j [[
|
||||
const Generator$j g$(j)_;
|
||||
|
||||
]]
|
||||
}; // class CartesianProductHolder$i
|
||||
|
||||
]]
|
||||
|
||||
# endif // GTEST_HAS_COMBINE
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_HAS_PARAM_TEST
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
|
||||
@@ -0,0 +1,731 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: vladl@google.com (Vlad Losev)
|
||||
|
||||
// Type and function utilities for implementing parameterized tests.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// scripts/fuse_gtest.py depends on gtest's own header being #included
|
||||
// *unconditionally*. Therefore these #includes cannot be moved
|
||||
// inside #if GTEST_HAS_PARAM_TEST.
|
||||
#include "gtest/internal/gtest-internal.h"
|
||||
#include "gtest/internal/gtest-linked_ptr.h"
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
#include "gtest/gtest-printers.h"
|
||||
|
||||
#if GTEST_HAS_PARAM_TEST
|
||||
|
||||
namespace testing {
|
||||
|
||||
// Input to a parameterized test name generator, describing a test parameter.
|
||||
// Consists of the parameter value and the integer parameter index.
|
||||
template <class ParamType>
|
||||
struct TestParamInfo {
|
||||
TestParamInfo(const ParamType& a_param, size_t an_index) :
|
||||
param(a_param),
|
||||
index(an_index) {}
|
||||
ParamType param;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
// A builtin parameterized test name generator which returns the result of
|
||||
// testing::PrintToString.
|
||||
struct PrintToStringParamName {
|
||||
template <class ParamType>
|
||||
std::string operator()(const TestParamInfo<ParamType>& info) const {
|
||||
return PrintToString(info.param);
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Outputs a message explaining invalid registration of different
|
||||
// fixture class for the same test case. This may happen when
|
||||
// TEST_P macro is used to define two tests with the same name
|
||||
// but in different namespaces.
|
||||
GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
|
||||
CodeLocation code_location);
|
||||
|
||||
template <typename> class ParamGeneratorInterface;
|
||||
template <typename> class ParamGenerator;
|
||||
|
||||
// Interface for iterating over elements provided by an implementation
|
||||
// of ParamGeneratorInterface<T>.
|
||||
template <typename T>
|
||||
class ParamIteratorInterface {
|
||||
public:
|
||||
virtual ~ParamIteratorInterface() {}
|
||||
// A pointer to the base generator instance.
|
||||
// Used only for the purposes of iterator comparison
|
||||
// to make sure that two iterators belong to the same generator.
|
||||
virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
|
||||
// Advances iterator to point to the next element
|
||||
// provided by the generator. The caller is responsible
|
||||
// for not calling Advance() on an iterator equal to
|
||||
// BaseGenerator()->End().
|
||||
virtual void Advance() = 0;
|
||||
// Clones the iterator object. Used for implementing copy semantics
|
||||
// of ParamIterator<T>.
|
||||
virtual ParamIteratorInterface* Clone() const = 0;
|
||||
// Dereferences the current iterator and provides (read-only) access
|
||||
// to the pointed value. It is the caller's responsibility not to call
|
||||
// Current() on an iterator equal to BaseGenerator()->End().
|
||||
// Used for implementing ParamGenerator<T>::operator*().
|
||||
virtual const T* Current() const = 0;
|
||||
// Determines whether the given iterator and other point to the same
|
||||
// element in the sequence generated by the generator.
|
||||
// Used for implementing ParamGenerator<T>::operator==().
|
||||
virtual bool Equals(const ParamIteratorInterface& other) const = 0;
|
||||
};
|
||||
|
||||
// Class iterating over elements provided by an implementation of
|
||||
// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
|
||||
// and implements the const forward iterator concept.
|
||||
template <typename T>
|
||||
class ParamIterator {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef const T& reference;
|
||||
typedef ptrdiff_t difference_type;
|
||||
|
||||
// ParamIterator assumes ownership of the impl_ pointer.
|
||||
ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
|
||||
ParamIterator& operator=(const ParamIterator& other) {
|
||||
if (this != &other)
|
||||
impl_.reset(other.impl_->Clone());
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T& operator*() const { return *impl_->Current(); }
|
||||
const T* operator->() const { return impl_->Current(); }
|
||||
// Prefix version of operator++.
|
||||
ParamIterator& operator++() {
|
||||
impl_->Advance();
|
||||
return *this;
|
||||
}
|
||||
// Postfix version of operator++.
|
||||
ParamIterator operator++(int /*unused*/) {
|
||||
ParamIteratorInterface<T>* clone = impl_->Clone();
|
||||
impl_->Advance();
|
||||
return ParamIterator(clone);
|
||||
}
|
||||
bool operator==(const ParamIterator& other) const {
|
||||
return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
|
||||
}
|
||||
bool operator!=(const ParamIterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ParamGenerator<T>;
|
||||
explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
|
||||
scoped_ptr<ParamIteratorInterface<T> > impl_;
|
||||
};
|
||||
|
||||
// ParamGeneratorInterface<T> is the binary interface to access generators
|
||||
// defined in other translation units.
|
||||
template <typename T>
|
||||
class ParamGeneratorInterface {
|
||||
public:
|
||||
typedef T ParamType;
|
||||
|
||||
virtual ~ParamGeneratorInterface() {}
|
||||
|
||||
// Generator interface definition
|
||||
virtual ParamIteratorInterface<T>* Begin() const = 0;
|
||||
virtual ParamIteratorInterface<T>* End() const = 0;
|
||||
};
|
||||
|
||||
// Wraps ParamGeneratorInterface<T> and provides general generator syntax
|
||||
// compatible with the STL Container concept.
|
||||
// This class implements copy initialization semantics and the contained
|
||||
// ParamGeneratorInterface<T> instance is shared among all copies
|
||||
// of the original object. This is possible because that instance is immutable.
|
||||
template<typename T>
|
||||
class ParamGenerator {
|
||||
public:
|
||||
typedef ParamIterator<T> iterator;
|
||||
|
||||
explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
|
||||
ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
|
||||
|
||||
ParamGenerator& operator=(const ParamGenerator& other) {
|
||||
impl_ = other.impl_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator begin() const { return iterator(impl_->Begin()); }
|
||||
iterator end() const { return iterator(impl_->End()); }
|
||||
|
||||
private:
|
||||
linked_ptr<const ParamGeneratorInterface<T> > impl_;
|
||||
};
|
||||
|
||||
// Generates values from a range of two comparable values. Can be used to
|
||||
// generate sequences of user-defined types that implement operator+() and
|
||||
// operator<().
|
||||
// This class is used in the Range() function.
|
||||
template <typename T, typename IncrementT>
|
||||
class RangeGenerator : public ParamGeneratorInterface<T> {
|
||||
public:
|
||||
RangeGenerator(T begin, T end, IncrementT step)
|
||||
: begin_(begin), end_(end),
|
||||
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
|
||||
virtual ~RangeGenerator() {}
|
||||
|
||||
virtual ParamIteratorInterface<T>* Begin() const {
|
||||
return new Iterator(this, begin_, 0, step_);
|
||||
}
|
||||
virtual ParamIteratorInterface<T>* End() const {
|
||||
return new Iterator(this, end_, end_index_, step_);
|
||||
}
|
||||
|
||||
private:
|
||||
class Iterator : public ParamIteratorInterface<T> {
|
||||
public:
|
||||
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
|
||||
IncrementT step)
|
||||
: base_(base), value_(value), index_(index), step_(step) {}
|
||||
virtual ~Iterator() {}
|
||||
|
||||
virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
|
||||
return base_;
|
||||
}
|
||||
virtual void Advance() {
|
||||
value_ = static_cast<T>(value_ + step_);
|
||||
index_++;
|
||||
}
|
||||
virtual ParamIteratorInterface<T>* Clone() const {
|
||||
return new Iterator(*this);
|
||||
}
|
||||
virtual const T* Current() const { return &value_; }
|
||||
virtual bool Equals(const ParamIteratorInterface<T>& other) const {
|
||||
// Having the same base generator guarantees that the other
|
||||
// iterator is of the same type and we can downcast.
|
||||
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
|
||||
<< "The program attempted to compare iterators "
|
||||
<< "from different generators." << std::endl;
|
||||
const int other_index =
|
||||
CheckedDowncastToActualType<const Iterator>(&other)->index_;
|
||||
return index_ == other_index;
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator(const Iterator& other)
|
||||
: ParamIteratorInterface<T>(),
|
||||
base_(other.base_), value_(other.value_), index_(other.index_),
|
||||
step_(other.step_) {}
|
||||
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const Iterator& other);
|
||||
|
||||
const ParamGeneratorInterface<T>* const base_;
|
||||
T value_;
|
||||
int index_;
|
||||
const IncrementT step_;
|
||||
}; // class RangeGenerator::Iterator
|
||||
|
||||
static int CalculateEndIndex(const T& begin,
|
||||
const T& end,
|
||||
const IncrementT& step) {
|
||||
int end_index = 0;
|
||||
for (T i = begin; i < end; i = static_cast<T>(i + step))
|
||||
end_index++;
|
||||
return end_index;
|
||||
}
|
||||
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const RangeGenerator& other);
|
||||
|
||||
const T begin_;
|
||||
const T end_;
|
||||
const IncrementT step_;
|
||||
// The index for the end() iterator. All the elements in the generated
|
||||
// sequence are indexed (0-based) to aid iterator comparison.
|
||||
const int end_index_;
|
||||
}; // class RangeGenerator
|
||||
|
||||
|
||||
// Generates values from a pair of STL-style iterators. Used in the
|
||||
// ValuesIn() function. The elements are copied from the source range
|
||||
// since the source can be located on the stack, and the generator
|
||||
// is likely to persist beyond that stack frame.
|
||||
template <typename T>
|
||||
class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
|
||||
public:
|
||||
template <typename ForwardIterator>
|
||||
ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
|
||||
: container_(begin, end) {}
|
||||
virtual ~ValuesInIteratorRangeGenerator() {}
|
||||
|
||||
virtual ParamIteratorInterface<T>* Begin() const {
|
||||
return new Iterator(this, container_.begin());
|
||||
}
|
||||
virtual ParamIteratorInterface<T>* End() const {
|
||||
return new Iterator(this, container_.end());
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename ::std::vector<T> ContainerType;
|
||||
|
||||
class Iterator : public ParamIteratorInterface<T> {
|
||||
public:
|
||||
Iterator(const ParamGeneratorInterface<T>* base,
|
||||
typename ContainerType::const_iterator iterator)
|
||||
: base_(base), iterator_(iterator) {}
|
||||
virtual ~Iterator() {}
|
||||
|
||||
virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
|
||||
return base_;
|
||||
}
|
||||
virtual void Advance() {
|
||||
++iterator_;
|
||||
value_.reset();
|
||||
}
|
||||
virtual ParamIteratorInterface<T>* Clone() const {
|
||||
return new Iterator(*this);
|
||||
}
|
||||
// We need to use cached value referenced by iterator_ because *iterator_
|
||||
// can return a temporary object (and of type other then T), so just
|
||||
// having "return &*iterator_;" doesn't work.
|
||||
// value_ is updated here and not in Advance() because Advance()
|
||||
// can advance iterator_ beyond the end of the range, and we cannot
|
||||
// detect that fact. The client code, on the other hand, is
|
||||
// responsible for not calling Current() on an out-of-range iterator.
|
||||
virtual const T* Current() const {
|
||||
if (value_.get() == NULL)
|
||||
value_.reset(new T(*iterator_));
|
||||
return value_.get();
|
||||
}
|
||||
virtual bool Equals(const ParamIteratorInterface<T>& other) const {
|
||||
// Having the same base generator guarantees that the other
|
||||
// iterator is of the same type and we can downcast.
|
||||
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
|
||||
<< "The program attempted to compare iterators "
|
||||
<< "from different generators." << std::endl;
|
||||
return iterator_ ==
|
||||
CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator(const Iterator& other)
|
||||
// The explicit constructor call suppresses a false warning
|
||||
// emitted by gcc when supplied with the -Wextra option.
|
||||
: ParamIteratorInterface<T>(),
|
||||
base_(other.base_),
|
||||
iterator_(other.iterator_) {}
|
||||
|
||||
const ParamGeneratorInterface<T>* const base_;
|
||||
typename ContainerType::const_iterator iterator_;
|
||||
// A cached value of *iterator_. We keep it here to allow access by
|
||||
// pointer in the wrapping iterator's operator->().
|
||||
// value_ needs to be mutable to be accessed in Current().
|
||||
// Use of scoped_ptr helps manage cached value's lifetime,
|
||||
// which is bound by the lifespan of the iterator itself.
|
||||
mutable scoped_ptr<const T> value_;
|
||||
}; // class ValuesInIteratorRangeGenerator::Iterator
|
||||
|
||||
// No implementation - assignment is unsupported.
|
||||
void operator=(const ValuesInIteratorRangeGenerator& other);
|
||||
|
||||
const ContainerType container_;
|
||||
}; // class ValuesInIteratorRangeGenerator
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Default parameterized test name generator, returns a string containing the
|
||||
// integer test parameter index.
|
||||
template <class ParamType>
|
||||
std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
|
||||
Message name_stream;
|
||||
name_stream << info.index;
|
||||
return name_stream.GetString();
|
||||
}
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Parameterized test name overload helpers, which help the
|
||||
// INSTANTIATE_TEST_CASE_P macro choose between the default parameterized
|
||||
// test name generator and user param name generator.
|
||||
template <class ParamType, class ParamNameGenFunctor>
|
||||
ParamNameGenFunctor GetParamNameGen(ParamNameGenFunctor func) {
|
||||
return func;
|
||||
}
|
||||
|
||||
template <class ParamType>
|
||||
struct ParamNameGenFunc {
|
||||
typedef std::string Type(const TestParamInfo<ParamType>&);
|
||||
};
|
||||
|
||||
template <class ParamType>
|
||||
typename ParamNameGenFunc<ParamType>::Type *GetParamNameGen() {
|
||||
return DefaultParamName;
|
||||
}
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// Stores a parameter value and later creates tests parameterized with that
|
||||
// value.
|
||||
template <class TestClass>
|
||||
class ParameterizedTestFactory : public TestFactoryBase {
|
||||
public:
|
||||
typedef typename TestClass::ParamType ParamType;
|
||||
explicit ParameterizedTestFactory(ParamType parameter) :
|
||||
parameter_(parameter) {}
|
||||
virtual Test* CreateTest() {
|
||||
TestClass::SetParam(¶meter_);
|
||||
return new TestClass();
|
||||
}
|
||||
|
||||
private:
|
||||
const ParamType parameter_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
|
||||
};
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// TestMetaFactoryBase is a base class for meta-factories that create
|
||||
// test factories for passing into MakeAndRegisterTestInfo function.
|
||||
template <class ParamType>
|
||||
class TestMetaFactoryBase {
|
||||
public:
|
||||
virtual ~TestMetaFactoryBase() {}
|
||||
|
||||
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
|
||||
};
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// TestMetaFactory creates test factories for passing into
|
||||
// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
|
||||
// ownership of test factory pointer, same factory object cannot be passed
|
||||
// into that method twice. But ParameterizedTestCaseInfo is going to call
|
||||
// it for each Test/Parameter value combination. Thus it needs meta factory
|
||||
// creator class.
|
||||
template <class TestCase>
|
||||
class TestMetaFactory
|
||||
: public TestMetaFactoryBase<typename TestCase::ParamType> {
|
||||
public:
|
||||
typedef typename TestCase::ParamType ParamType;
|
||||
|
||||
TestMetaFactory() {}
|
||||
|
||||
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
|
||||
return new ParameterizedTestFactory<TestCase>(parameter);
|
||||
}
|
||||
|
||||
private:
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
|
||||
};
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// ParameterizedTestCaseInfoBase is a generic interface
|
||||
// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
|
||||
// accumulates test information provided by TEST_P macro invocations
|
||||
// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations
|
||||
// and uses that information to register all resulting test instances
|
||||
// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
|
||||
// a collection of pointers to the ParameterizedTestCaseInfo objects
|
||||
// and calls RegisterTests() on each of them when asked.
|
||||
class ParameterizedTestCaseInfoBase {
|
||||
public:
|
||||
virtual ~ParameterizedTestCaseInfoBase() {}
|
||||
|
||||
// Base part of test case name for display purposes.
|
||||
virtual const string& GetTestCaseName() const = 0;
|
||||
// Test case id to verify identity.
|
||||
virtual TypeId GetTestCaseTypeId() const = 0;
|
||||
// UnitTest class invokes this method to register tests in this
|
||||
// test case right before running them in RUN_ALL_TESTS macro.
|
||||
// This method should not be called more then once on any single
|
||||
// instance of a ParameterizedTestCaseInfoBase derived class.
|
||||
virtual void RegisterTests() = 0;
|
||||
|
||||
protected:
|
||||
ParameterizedTestCaseInfoBase() {}
|
||||
|
||||
private:
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
|
||||
};
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
|
||||
// macro invocations for a particular test case and generators
|
||||
// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
|
||||
// test case. It registers tests with all values generated by all
|
||||
// generators when asked.
|
||||
template <class TestCase>
|
||||
class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
|
||||
public:
|
||||
// ParamType and GeneratorCreationFunc are private types but are required
|
||||
// for declarations of public methods AddTestPattern() and
|
||||
// AddTestCaseInstantiation().
|
||||
typedef typename TestCase::ParamType ParamType;
|
||||
// A function that returns an instance of appropriate generator type.
|
||||
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
|
||||
typedef typename ParamNameGenFunc<ParamType>::Type ParamNameGeneratorFunc;
|
||||
|
||||
explicit ParameterizedTestCaseInfo(
|
||||
const char* name, CodeLocation code_location)
|
||||
: test_case_name_(name), code_location_(code_location) {}
|
||||
|
||||
// Test case base name for display purposes.
|
||||
virtual const string& GetTestCaseName() const { return test_case_name_; }
|
||||
// Test case id to verify identity.
|
||||
virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
|
||||
// TEST_P macro uses AddTestPattern() to record information
|
||||
// about a single test in a LocalTestInfo structure.
|
||||
// test_case_name is the base name of the test case (without invocation
|
||||
// prefix). test_base_name is the name of an individual test without
|
||||
// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
|
||||
// test case base name and DoBar is test base name.
|
||||
void AddTestPattern(const char* test_case_name,
|
||||
const char* test_base_name,
|
||||
TestMetaFactoryBase<ParamType>* meta_factory) {
|
||||
tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
|
||||
test_base_name,
|
||||
meta_factory)));
|
||||
}
|
||||
// INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
|
||||
// about a generator.
|
||||
int AddTestCaseInstantiation(const string& instantiation_name,
|
||||
GeneratorCreationFunc* func,
|
||||
ParamNameGeneratorFunc* name_func,
|
||||
const char* file,
|
||||
int line) {
|
||||
instantiations_.push_back(
|
||||
InstantiationInfo(instantiation_name, func, name_func, file, line));
|
||||
return 0; // Return value used only to run this method in namespace scope.
|
||||
}
|
||||
// UnitTest class invokes this method to register tests in this test case
|
||||
// test cases right before running tests in RUN_ALL_TESTS macro.
|
||||
// This method should not be called more then once on any single
|
||||
// instance of a ParameterizedTestCaseInfoBase derived class.
|
||||
// UnitTest has a guard to prevent from calling this method more then once.
|
||||
virtual void RegisterTests() {
|
||||
for (typename TestInfoContainer::iterator test_it = tests_.begin();
|
||||
test_it != tests_.end(); ++test_it) {
|
||||
linked_ptr<TestInfo> test_info = *test_it;
|
||||
for (typename InstantiationContainer::iterator gen_it =
|
||||
instantiations_.begin(); gen_it != instantiations_.end();
|
||||
++gen_it) {
|
||||
const string& instantiation_name = gen_it->name;
|
||||
ParamGenerator<ParamType> generator((*gen_it->generator)());
|
||||
ParamNameGeneratorFunc* name_func = gen_it->name_func;
|
||||
const char* file = gen_it->file;
|
||||
int line = gen_it->line;
|
||||
|
||||
string test_case_name;
|
||||
if ( !instantiation_name.empty() )
|
||||
test_case_name = instantiation_name + "/";
|
||||
test_case_name += test_info->test_case_base_name;
|
||||
|
||||
size_t i = 0;
|
||||
std::set<std::string> test_param_names;
|
||||
for (typename ParamGenerator<ParamType>::iterator param_it =
|
||||
generator.begin();
|
||||
param_it != generator.end(); ++param_it, ++i) {
|
||||
Message test_name_stream;
|
||||
|
||||
std::string param_name = name_func(
|
||||
TestParamInfo<ParamType>(*param_it, i));
|
||||
|
||||
GTEST_CHECK_(IsValidParamName(param_name))
|
||||
<< "Parameterized test name '" << param_name
|
||||
<< "' is invalid, in " << file
|
||||
<< " line " << line << std::endl;
|
||||
|
||||
GTEST_CHECK_(test_param_names.count(param_name) == 0)
|
||||
<< "Duplicate parameterized test name '" << param_name
|
||||
<< "', in " << file << " line " << line << std::endl;
|
||||
|
||||
test_param_names.insert(param_name);
|
||||
|
||||
test_name_stream << test_info->test_base_name << "/" << param_name;
|
||||
MakeAndRegisterTestInfo(
|
||||
test_case_name.c_str(),
|
||||
test_name_stream.GetString().c_str(),
|
||||
NULL, // No type parameter.
|
||||
PrintToString(*param_it).c_str(),
|
||||
code_location_,
|
||||
GetTestCaseTypeId(),
|
||||
TestCase::SetUpTestCase,
|
||||
TestCase::TearDownTestCase,
|
||||
test_info->test_meta_factory->CreateTestFactory(*param_it));
|
||||
} // for param_it
|
||||
} // for gen_it
|
||||
} // for test_it
|
||||
} // RegisterTests
|
||||
|
||||
private:
|
||||
// LocalTestInfo structure keeps information about a single test registered
|
||||
// with TEST_P macro.
|
||||
struct TestInfo {
|
||||
TestInfo(const char* a_test_case_base_name,
|
||||
const char* a_test_base_name,
|
||||
TestMetaFactoryBase<ParamType>* a_test_meta_factory) :
|
||||
test_case_base_name(a_test_case_base_name),
|
||||
test_base_name(a_test_base_name),
|
||||
test_meta_factory(a_test_meta_factory) {}
|
||||
|
||||
const string test_case_base_name;
|
||||
const string test_base_name;
|
||||
const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
|
||||
};
|
||||
typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
|
||||
// Records data received from INSTANTIATE_TEST_CASE_P macros:
|
||||
// <Instantiation name, Sequence generator creation function,
|
||||
// Name generator function, Source file, Source line>
|
||||
struct InstantiationInfo {
|
||||
InstantiationInfo(const std::string &name_in,
|
||||
GeneratorCreationFunc* generator_in,
|
||||
ParamNameGeneratorFunc* name_func_in,
|
||||
const char* file_in,
|
||||
int line_in)
|
||||
: name(name_in),
|
||||
generator(generator_in),
|
||||
name_func(name_func_in),
|
||||
file(file_in),
|
||||
line(line_in) {}
|
||||
|
||||
std::string name;
|
||||
GeneratorCreationFunc* generator;
|
||||
ParamNameGeneratorFunc* name_func;
|
||||
const char* file;
|
||||
int line;
|
||||
};
|
||||
typedef ::std::vector<InstantiationInfo> InstantiationContainer;
|
||||
|
||||
static bool IsValidParamName(const std::string& name) {
|
||||
// Check for empty string
|
||||
if (name.empty())
|
||||
return false;
|
||||
|
||||
// Check for invalid characters
|
||||
for (std::string::size_type index = 0; index < name.size(); ++index) {
|
||||
if (!isalnum(name[index]) && name[index] != '_')
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const string test_case_name_;
|
||||
CodeLocation code_location_;
|
||||
TestInfoContainer tests_;
|
||||
InstantiationContainer instantiations_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
|
||||
}; // class ParameterizedTestCaseInfo
|
||||
|
||||
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||
//
|
||||
// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
|
||||
// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
|
||||
// macros use it to locate their corresponding ParameterizedTestCaseInfo
|
||||
// descriptors.
|
||||
class ParameterizedTestCaseRegistry {
|
||||
public:
|
||||
ParameterizedTestCaseRegistry() {}
|
||||
~ParameterizedTestCaseRegistry() {
|
||||
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
|
||||
it != test_case_infos_.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
// Looks up or creates and returns a structure containing information about
|
||||
// tests and instantiations of a particular test case.
|
||||
template <class TestCase>
|
||||
ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
|
||||
const char* test_case_name,
|
||||
CodeLocation code_location) {
|
||||
ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
|
||||
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
|
||||
it != test_case_infos_.end(); ++it) {
|
||||
if ((*it)->GetTestCaseName() == test_case_name) {
|
||||
if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
|
||||
// Complain about incorrect usage of Google Test facilities
|
||||
// and terminate the program since we cannot guaranty correct
|
||||
// test case setup and tear-down in this case.
|
||||
ReportInvalidTestCaseType(test_case_name, code_location);
|
||||
posix::Abort();
|
||||
} else {
|
||||
// At this point we are sure that the object we found is of the same
|
||||
// type we are looking for, so we downcast it to that type
|
||||
// without further checks.
|
||||
typed_test_info = CheckedDowncastToActualType<
|
||||
ParameterizedTestCaseInfo<TestCase> >(*it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (typed_test_info == NULL) {
|
||||
typed_test_info = new ParameterizedTestCaseInfo<TestCase>(
|
||||
test_case_name, code_location);
|
||||
test_case_infos_.push_back(typed_test_info);
|
||||
}
|
||||
return typed_test_info;
|
||||
}
|
||||
void RegisterTests() {
|
||||
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
|
||||
it != test_case_infos_.end(); ++it) {
|
||||
(*it)->RegisterTests();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
|
||||
|
||||
TestCaseInfoContainer test_case_infos_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_HAS_PARAM_TEST
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2015, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// The Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// This header file defines the GTEST_OS_* macro.
|
||||
// It is separate from gtest-port.h so that custom/gtest-port.h can include it.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
||||
|
||||
// Determines the platform on which Google Test is compiled.
|
||||
#ifdef __CYGWIN__
|
||||
# define GTEST_OS_CYGWIN 1
|
||||
#elif defined __SYMBIAN32__
|
||||
# define GTEST_OS_SYMBIAN 1
|
||||
#elif defined _WIN32
|
||||
# define GTEST_OS_WINDOWS 1
|
||||
# ifdef _WIN32_WCE
|
||||
# define GTEST_OS_WINDOWS_MOBILE 1
|
||||
# elif defined(__MINGW__) || defined(__MINGW32__)
|
||||
# define GTEST_OS_WINDOWS_MINGW 1
|
||||
# elif defined(WINAPI_FAMILY)
|
||||
# include <winapifamily.h>
|
||||
# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
# define GTEST_OS_WINDOWS_DESKTOP 1
|
||||
# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
|
||||
# define GTEST_OS_WINDOWS_PHONE 1
|
||||
# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||
# define GTEST_OS_WINDOWS_RT 1
|
||||
# else
|
||||
// WINAPI_FAMILY defined but no known partition matched.
|
||||
// Default to desktop.
|
||||
# define GTEST_OS_WINDOWS_DESKTOP 1
|
||||
# endif
|
||||
# else
|
||||
# define GTEST_OS_WINDOWS_DESKTOP 1
|
||||
# endif // _WIN32_WCE
|
||||
#elif defined __APPLE__
|
||||
# define GTEST_OS_MAC 1
|
||||
# if TARGET_OS_IPHONE
|
||||
# define GTEST_OS_IOS 1
|
||||
# endif
|
||||
#elif defined __FreeBSD__
|
||||
# define GTEST_OS_FREEBSD 1
|
||||
#elif defined __linux__
|
||||
# define GTEST_OS_LINUX 1
|
||||
# if defined __ANDROID__
|
||||
# define GTEST_OS_LINUX_ANDROID 1
|
||||
# endif
|
||||
#elif defined __MVS__
|
||||
# define GTEST_OS_ZOS 1
|
||||
#elif defined(__sun) && defined(__SVR4)
|
||||
# define GTEST_OS_SOLARIS 1
|
||||
#elif defined(_AIX)
|
||||
# define GTEST_OS_AIX 1
|
||||
#elif defined(__hpux)
|
||||
# define GTEST_OS_HPUX 1
|
||||
#elif defined __native_client__
|
||||
# define GTEST_OS_NACL 1
|
||||
#elif defined __OpenBSD__
|
||||
# define GTEST_OS_OPENBSD 1
|
||||
#elif defined __QNX__
|
||||
# define GTEST_OS_QNX 1
|
||||
#endif // __CYGWIN__
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
||||
2560
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-port.h
vendored
Normal file
2560
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-port.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
167
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-string.h
vendored
Normal file
167
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-string.h
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
|
||||
//
|
||||
// The Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// This header file declares the String class and functions used internally by
|
||||
// Google Test. They are subject to change without notice. They should not used
|
||||
// by code external to Google Test.
|
||||
//
|
||||
// This header file is #included by <gtest/internal/gtest-internal.h>.
|
||||
// It should not be #included by other files.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
// string.h is not guaranteed to provide strcpy on C++ Builder.
|
||||
# include <mem.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
// String - an abstract class holding static string utilities.
|
||||
class GTEST_API_ String {
|
||||
public:
|
||||
// Static utility methods
|
||||
|
||||
// Clones a 0-terminated C string, allocating memory using new. The
|
||||
// caller is responsible for deleting the return value using
|
||||
// delete[]. Returns the cloned string, or NULL if the input is
|
||||
// NULL.
|
||||
//
|
||||
// This is different from strdup() in string.h, which allocates
|
||||
// memory using malloc().
|
||||
static const char* CloneCString(const char* c_str);
|
||||
|
||||
#if GTEST_OS_WINDOWS_MOBILE
|
||||
// Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
|
||||
// able to pass strings to Win32 APIs on CE we need to convert them
|
||||
// to 'Unicode', UTF-16.
|
||||
|
||||
// Creates a UTF-16 wide string from the given ANSI string, allocating
|
||||
// memory using new. The caller is responsible for deleting the return
|
||||
// value using delete[]. Returns the wide string, or NULL if the
|
||||
// input is NULL.
|
||||
//
|
||||
// The wide string is created using the ANSI codepage (CP_ACP) to
|
||||
// match the behaviour of the ANSI versions of Win32 calls and the
|
||||
// C runtime.
|
||||
static LPCWSTR AnsiToUtf16(const char* c_str);
|
||||
|
||||
// Creates an ANSI string from the given wide string, allocating
|
||||
// memory using new. The caller is responsible for deleting the return
|
||||
// value using delete[]. Returns the ANSI string, or NULL if the
|
||||
// input is NULL.
|
||||
//
|
||||
// The returned string is created using the ANSI codepage (CP_ACP) to
|
||||
// match the behaviour of the ANSI versions of Win32 calls and the
|
||||
// C runtime.
|
||||
static const char* Utf16ToAnsi(LPCWSTR utf16_str);
|
||||
#endif
|
||||
|
||||
// Compares two C strings. Returns true iff they have the same content.
|
||||
//
|
||||
// Unlike strcmp(), this function can handle NULL argument(s). A
|
||||
// NULL C string is considered different to any non-NULL C string,
|
||||
// including the empty string.
|
||||
static bool CStringEquals(const char* lhs, const char* rhs);
|
||||
|
||||
// Converts a wide C string to a String using the UTF-8 encoding.
|
||||
// NULL will be converted to "(null)". If an error occurred during
|
||||
// the conversion, "(failed to convert from wide string)" is
|
||||
// returned.
|
||||
static std::string ShowWideCString(const wchar_t* wide_c_str);
|
||||
|
||||
// Compares two wide C strings. Returns true iff they have the same
|
||||
// content.
|
||||
//
|
||||
// Unlike wcscmp(), this function can handle NULL argument(s). A
|
||||
// NULL C string is considered different to any non-NULL C string,
|
||||
// including the empty string.
|
||||
static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
|
||||
|
||||
// Compares two C strings, ignoring case. Returns true iff they
|
||||
// have the same content.
|
||||
//
|
||||
// Unlike strcasecmp(), this function can handle NULL argument(s).
|
||||
// A NULL C string is considered different to any non-NULL C string,
|
||||
// including the empty string.
|
||||
static bool CaseInsensitiveCStringEquals(const char* lhs,
|
||||
const char* rhs);
|
||||
|
||||
// Compares two wide C strings, ignoring case. Returns true iff they
|
||||
// have the same content.
|
||||
//
|
||||
// Unlike wcscasecmp(), this function can handle NULL argument(s).
|
||||
// A NULL C string is considered different to any non-NULL wide C string,
|
||||
// including the empty string.
|
||||
// NB: The implementations on different platforms slightly differ.
|
||||
// On windows, this method uses _wcsicmp which compares according to LC_CTYPE
|
||||
// environment variable. On GNU platform this method uses wcscasecmp
|
||||
// which compares according to LC_CTYPE category of the current locale.
|
||||
// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
|
||||
// current locale.
|
||||
static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
|
||||
const wchar_t* rhs);
|
||||
|
||||
// Returns true iff the given string ends with the given suffix, ignoring
|
||||
// case. Any string is considered to end with an empty suffix.
|
||||
static bool EndsWithCaseInsensitive(
|
||||
const std::string& str, const std::string& suffix);
|
||||
|
||||
// Formats an int value as "%02d".
|
||||
static std::string FormatIntWidth2(int value); // "%02d" for width == 2
|
||||
|
||||
// Formats an int value as "%X".
|
||||
static std::string FormatHexInt(int value);
|
||||
|
||||
// Formats a byte as "%02X".
|
||||
static std::string FormatByte(unsigned char value);
|
||||
|
||||
private:
|
||||
String(); // Not meant to be instantiated.
|
||||
}; // class String
|
||||
|
||||
// Gets the content of the stringstream's buffer as an std::string. Each '\0'
|
||||
// character in the buffer is replaced with "\\0".
|
||||
GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
|
||||
1020
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-tuple.h
vendored
Normal file
1020
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-tuple.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
$$ -*- mode: c++; -*-
|
||||
$var n = 10 $$ Maximum number of tuple fields we want to support.
|
||||
$$ This meta comment fixes auto-indentation in Emacs. }}
|
||||
// Copyright 2009 Google Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
|
||||
// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
|
||||
|
||||
#include <utility> // For ::std::pair.
|
||||
|
||||
// The compiler used in Symbian has a bug that prevents us from declaring the
|
||||
// tuple template as a friend (it complains that tuple is redefined). This
|
||||
// hack bypasses the bug by declaring the members that should otherwise be
|
||||
// private as public.
|
||||
// Sun Studio versions < 12 also have the above bug.
|
||||
#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
|
||||
# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
|
||||
#else
|
||||
# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
|
||||
template <GTEST_$(n)_TYPENAMES_(U)> friend class tuple; \
|
||||
private:
|
||||
#endif
|
||||
|
||||
// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict
|
||||
// with our own definitions. Therefore using our own tuple does not work on
|
||||
// those compilers.
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */
|
||||
# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \
|
||||
GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers."
|
||||
#endif
|
||||
|
||||
|
||||
$range i 0..n-1
|
||||
$range j 0..n
|
||||
$range k 1..n
|
||||
// GTEST_n_TUPLE_(T) is the type of an n-tuple.
|
||||
#define GTEST_0_TUPLE_(T) tuple<>
|
||||
|
||||
$for k [[
|
||||
$range m 0..k-1
|
||||
$range m2 k..n-1
|
||||
#define GTEST_$(k)_TUPLE_(T) tuple<$for m, [[T##$m]]$for m2 [[, void]]>
|
||||
|
||||
]]
|
||||
|
||||
// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
|
||||
|
||||
$for j [[
|
||||
$range m 0..j-1
|
||||
#define GTEST_$(j)_TYPENAMES_(T) $for m, [[typename T##$m]]
|
||||
|
||||
|
||||
]]
|
||||
|
||||
// In theory, defining stuff in the ::std namespace is undefined
|
||||
// behavior. We can do this as we are playing the role of a standard
|
||||
// library vendor.
|
||||
namespace std {
|
||||
namespace tr1 {
|
||||
|
||||
template <$for i, [[typename T$i = void]]>
|
||||
class tuple;
|
||||
|
||||
// Anything in namespace gtest_internal is Google Test's INTERNAL
|
||||
// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
|
||||
namespace gtest_internal {
|
||||
|
||||
// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
|
||||
template <typename T>
|
||||
struct ByRef { typedef const T& type; }; // NOLINT
|
||||
template <typename T>
|
||||
struct ByRef<T&> { typedef T& type; }; // NOLINT
|
||||
|
||||
// A handy wrapper for ByRef.
|
||||
#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
|
||||
|
||||
// AddRef<T>::type is T if T is a reference; otherwise it's T&. This
|
||||
// is the same as tr1::add_reference<T>::type.
|
||||
template <typename T>
|
||||
struct AddRef { typedef T& type; }; // NOLINT
|
||||
template <typename T>
|
||||
struct AddRef<T&> { typedef T& type; }; // NOLINT
|
||||
|
||||
// A handy wrapper for AddRef.
|
||||
#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
|
||||
|
||||
// A helper for implementing get<k>().
|
||||
template <int k> class Get;
|
||||
|
||||
// A helper for implementing tuple_element<k, T>. kIndexValid is true
|
||||
// iff k < the number of fields in tuple type T.
|
||||
template <bool kIndexValid, int kIndex, class Tuple>
|
||||
struct TupleElement;
|
||||
|
||||
|
||||
$for i [[
|
||||
template <GTEST_$(n)_TYPENAMES_(T)>
|
||||
struct TupleElement<true, $i, GTEST_$(n)_TUPLE_(T) > {
|
||||
typedef T$i type;
|
||||
};
|
||||
|
||||
|
||||
]]
|
||||
} // namespace gtest_internal
|
||||
|
||||
template <>
|
||||
class tuple<> {
|
||||
public:
|
||||
tuple() {}
|
||||
tuple(const tuple& /* t */) {}
|
||||
tuple& operator=(const tuple& /* t */) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
$for k [[
|
||||
$range m 0..k-1
|
||||
template <GTEST_$(k)_TYPENAMES_(T)>
|
||||
class $if k < n [[GTEST_$(k)_TUPLE_(T)]] $else [[tuple]] {
|
||||
public:
|
||||
template <int k> friend class gtest_internal::Get;
|
||||
|
||||
tuple() : $for m, [[f$(m)_()]] {}
|
||||
|
||||
explicit tuple($for m, [[GTEST_BY_REF_(T$m) f$m]]) : [[]]
|
||||
$for m, [[f$(m)_(f$m)]] {}
|
||||
|
||||
tuple(const tuple& t) : $for m, [[f$(m)_(t.f$(m)_)]] {}
|
||||
|
||||
template <GTEST_$(k)_TYPENAMES_(U)>
|
||||
tuple(const GTEST_$(k)_TUPLE_(U)& t) : $for m, [[f$(m)_(t.f$(m)_)]] {}
|
||||
|
||||
$if k == 2 [[
|
||||
template <typename U0, typename U1>
|
||||
tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
|
||||
|
||||
]]
|
||||
|
||||
tuple& operator=(const tuple& t) { return CopyFrom(t); }
|
||||
|
||||
template <GTEST_$(k)_TYPENAMES_(U)>
|
||||
tuple& operator=(const GTEST_$(k)_TUPLE_(U)& t) {
|
||||
return CopyFrom(t);
|
||||
}
|
||||
|
||||
$if k == 2 [[
|
||||
template <typename U0, typename U1>
|
||||
tuple& operator=(const ::std::pair<U0, U1>& p) {
|
||||
f0_ = p.first;
|
||||
f1_ = p.second;
|
||||
return *this;
|
||||
}
|
||||
|
||||
]]
|
||||
|
||||
GTEST_DECLARE_TUPLE_AS_FRIEND_
|
||||
|
||||
template <GTEST_$(k)_TYPENAMES_(U)>
|
||||
tuple& CopyFrom(const GTEST_$(k)_TUPLE_(U)& t) {
|
||||
|
||||
$for m [[
|
||||
f$(m)_ = t.f$(m)_;
|
||||
|
||||
]]
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
$for m [[
|
||||
T$m f$(m)_;
|
||||
|
||||
]]
|
||||
};
|
||||
|
||||
|
||||
]]
|
||||
// 6.1.3.2 Tuple creation functions.
|
||||
|
||||
// Known limitations: we don't support passing an
|
||||
// std::tr1::reference_wrapper<T> to make_tuple(). And we don't
|
||||
// implement tie().
|
||||
|
||||
inline tuple<> make_tuple() { return tuple<>(); }
|
||||
|
||||
$for k [[
|
||||
$range m 0..k-1
|
||||
|
||||
template <GTEST_$(k)_TYPENAMES_(T)>
|
||||
inline GTEST_$(k)_TUPLE_(T) make_tuple($for m, [[const T$m& f$m]]) {
|
||||
return GTEST_$(k)_TUPLE_(T)($for m, [[f$m]]);
|
||||
}
|
||||
|
||||
]]
|
||||
|
||||
// 6.1.3.3 Tuple helper classes.
|
||||
|
||||
template <typename Tuple> struct tuple_size;
|
||||
|
||||
|
||||
$for j [[
|
||||
template <GTEST_$(j)_TYPENAMES_(T)>
|
||||
struct tuple_size<GTEST_$(j)_TUPLE_(T) > {
|
||||
static const int value = $j;
|
||||
};
|
||||
|
||||
|
||||
]]
|
||||
template <int k, class Tuple>
|
||||
struct tuple_element {
|
||||
typedef typename gtest_internal::TupleElement<
|
||||
k < (tuple_size<Tuple>::value), k, Tuple>::type type;
|
||||
};
|
||||
|
||||
#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
|
||||
|
||||
// 6.1.3.4 Element access.
|
||||
|
||||
namespace gtest_internal {
|
||||
|
||||
|
||||
$for i [[
|
||||
template <>
|
||||
class Get<$i> {
|
||||
public:
|
||||
template <class Tuple>
|
||||
static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple))
|
||||
Field(Tuple& t) { return t.f$(i)_; } // NOLINT
|
||||
|
||||
template <class Tuple>
|
||||
static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple))
|
||||
ConstField(const Tuple& t) { return t.f$(i)_; }
|
||||
};
|
||||
|
||||
|
||||
]]
|
||||
} // namespace gtest_internal
|
||||
|
||||
template <int k, GTEST_$(n)_TYPENAMES_(T)>
|
||||
GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T)))
|
||||
get(GTEST_$(n)_TUPLE_(T)& t) {
|
||||
return gtest_internal::Get<k>::Field(t);
|
||||
}
|
||||
|
||||
template <int k, GTEST_$(n)_TYPENAMES_(T)>
|
||||
GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T)))
|
||||
get(const GTEST_$(n)_TUPLE_(T)& t) {
|
||||
return gtest_internal::Get<k>::ConstField(t);
|
||||
}
|
||||
|
||||
// 6.1.3.5 Relational operators
|
||||
|
||||
// We only implement == and !=, as we don't have a need for the rest yet.
|
||||
|
||||
namespace gtest_internal {
|
||||
|
||||
// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
|
||||
// first k fields of t1 equals the first k fields of t2.
|
||||
// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
|
||||
// k1 != k2.
|
||||
template <int kSize1, int kSize2>
|
||||
struct SameSizeTuplePrefixComparator;
|
||||
|
||||
template <>
|
||||
struct SameSizeTuplePrefixComparator<0, 0> {
|
||||
template <class Tuple1, class Tuple2>
|
||||
static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <int k>
|
||||
struct SameSizeTuplePrefixComparator<k, k> {
|
||||
template <class Tuple1, class Tuple2>
|
||||
static bool Eq(const Tuple1& t1, const Tuple2& t2) {
|
||||
return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
|
||||
::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gtest_internal
|
||||
|
||||
template <GTEST_$(n)_TYPENAMES_(T), GTEST_$(n)_TYPENAMES_(U)>
|
||||
inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t,
|
||||
const GTEST_$(n)_TUPLE_(U)& u) {
|
||||
return gtest_internal::SameSizeTuplePrefixComparator<
|
||||
tuple_size<GTEST_$(n)_TUPLE_(T) >::value,
|
||||
tuple_size<GTEST_$(n)_TUPLE_(U) >::value>::Eq(t, u);
|
||||
}
|
||||
|
||||
template <GTEST_$(n)_TYPENAMES_(T), GTEST_$(n)_TYPENAMES_(U)>
|
||||
inline bool operator!=(const GTEST_$(n)_TUPLE_(T)& t,
|
||||
const GTEST_$(n)_TUPLE_(U)& u) { return !(t == u); }
|
||||
|
||||
// 6.1.4 Pairs.
|
||||
// Unimplemented.
|
||||
|
||||
} // namespace tr1
|
||||
} // namespace std
|
||||
|
||||
|
||||
$for j [[
|
||||
#undef GTEST_$(j)_TUPLE_
|
||||
|
||||
]]
|
||||
|
||||
|
||||
$for j [[
|
||||
#undef GTEST_$(j)_TYPENAMES_
|
||||
|
||||
]]
|
||||
|
||||
#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
|
||||
#undef GTEST_BY_REF_
|
||||
#undef GTEST_ADD_REF_
|
||||
#undef GTEST_TUPLE_ELEMENT_
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
|
||||
3331
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-type-util.h
vendored
Normal file
3331
venv/lib/python3.12/site-packages/mypyc/external/googletest/include/gtest/internal/gtest-type-util.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,297 @@
|
||||
$$ -*- mode: c++; -*-
|
||||
$var n = 50 $$ Maximum length of type lists we want to support.
|
||||
// Copyright 2008 Google Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
|
||||
// Type utilities needed for implementing typed and type-parameterized
|
||||
// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
|
||||
//
|
||||
// Currently we support at most $n types in a list, and at most $n
|
||||
// type-parameterized tests in one type-parameterized test case.
|
||||
// Please contact googletestframework@googlegroups.com if you need
|
||||
// more.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
|
||||
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
|
||||
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
// #ifdef __GNUC__ is too general here. It is possible to use gcc without using
|
||||
// libstdc++ (which is where cxxabi.h comes from).
|
||||
# if GTEST_HAS_CXXABI_H_
|
||||
# include <cxxabi.h>
|
||||
# elif defined(__HP_aCC)
|
||||
# include <acxx_demangle.h>
|
||||
# endif // GTEST_HASH_CXXABI_H_
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
// GetTypeName<T>() returns a human-readable name of type T.
|
||||
// NB: This function is also used in Google Mock, so don't move it inside of
|
||||
// the typed-test-only section below.
|
||||
template <typename T>
|
||||
std::string GetTypeName() {
|
||||
# if GTEST_HAS_RTTI
|
||||
|
||||
const char* const name = typeid(T).name();
|
||||
# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
|
||||
int status = 0;
|
||||
// gcc's implementation of typeid(T).name() mangles the type name,
|
||||
// so we have to demangle it.
|
||||
# if GTEST_HAS_CXXABI_H_
|
||||
using abi::__cxa_demangle;
|
||||
# endif // GTEST_HAS_CXXABI_H_
|
||||
char* const readable_name = __cxa_demangle(name, 0, 0, &status);
|
||||
const std::string name_str(status == 0 ? readable_name : name);
|
||||
free(readable_name);
|
||||
return name_str;
|
||||
# else
|
||||
return name;
|
||||
# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
|
||||
|
||||
# else
|
||||
|
||||
return "<type>";
|
||||
|
||||
# endif // GTEST_HAS_RTTI
|
||||
}
|
||||
|
||||
#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
|
||||
|
||||
// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
|
||||
// type. This can be used as a compile-time assertion to ensure that
|
||||
// two types are equal.
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct AssertTypeEq;
|
||||
|
||||
template <typename T>
|
||||
struct AssertTypeEq<T, T> {
|
||||
typedef bool type;
|
||||
};
|
||||
|
||||
// A unique type used as the default value for the arguments of class
|
||||
// template Types. This allows us to simulate variadic templates
|
||||
// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
|
||||
// support directly.
|
||||
struct None {};
|
||||
|
||||
// The following family of struct and struct templates are used to
|
||||
// represent type lists. In particular, TypesN<T1, T2, ..., TN>
|
||||
// represents a type list with N types (T1, T2, ..., and TN) in it.
|
||||
// Except for Types0, every struct in the family has two member types:
|
||||
// Head for the first type in the list, and Tail for the rest of the
|
||||
// list.
|
||||
|
||||
// The empty type list.
|
||||
struct Types0 {};
|
||||
|
||||
// Type lists of length 1, 2, 3, and so on.
|
||||
|
||||
template <typename T1>
|
||||
struct Types1 {
|
||||
typedef T1 Head;
|
||||
typedef Types0 Tail;
|
||||
};
|
||||
|
||||
$range i 2..n
|
||||
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
$range k 2..i
|
||||
template <$for j, [[typename T$j]]>
|
||||
struct Types$i {
|
||||
typedef T1 Head;
|
||||
typedef Types$(i-1)<$for k, [[T$k]]> Tail;
|
||||
};
|
||||
|
||||
|
||||
]]
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// We don't want to require the users to write TypesN<...> directly,
|
||||
// as that would require them to count the length. Types<...> is much
|
||||
// easier to write, but generates horrible messages when there is a
|
||||
// compiler error, as gcc insists on printing out each template
|
||||
// argument, even if it has the default value (this means Types<int>
|
||||
// will appear as Types<int, None, None, ..., None> in the compiler
|
||||
// errors).
|
||||
//
|
||||
// Our solution is to combine the best part of the two approaches: a
|
||||
// user would write Types<T1, ..., TN>, and Google Test will translate
|
||||
// that to TypesN<T1, ..., TN> internally to make error messages
|
||||
// readable. The translation is done by the 'type' member of the
|
||||
// Types template.
|
||||
|
||||
$range i 1..n
|
||||
template <$for i, [[typename T$i = internal::None]]>
|
||||
struct Types {
|
||||
typedef internal::Types$n<$for i, [[T$i]]> type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Types<$for i, [[internal::None]]> {
|
||||
typedef internal::Types0 type;
|
||||
};
|
||||
|
||||
$range i 1..n-1
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
$range k i+1..n
|
||||
template <$for j, [[typename T$j]]>
|
||||
struct Types<$for j, [[T$j]]$for k[[, internal::None]]> {
|
||||
typedef internal::Types$i<$for j, [[T$j]]> type;
|
||||
};
|
||||
|
||||
]]
|
||||
|
||||
namespace internal {
|
||||
|
||||
# define GTEST_TEMPLATE_ template <typename T> class
|
||||
|
||||
// The template "selector" struct TemplateSel<Tmpl> is used to
|
||||
// represent Tmpl, which must be a class template with one type
|
||||
// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined
|
||||
// as the type Tmpl<T>. This allows us to actually instantiate the
|
||||
// template "selected" by TemplateSel<Tmpl>.
|
||||
//
|
||||
// This trick is necessary for simulating typedef for class templates,
|
||||
// which C++ doesn't support directly.
|
||||
template <GTEST_TEMPLATE_ Tmpl>
|
||||
struct TemplateSel {
|
||||
template <typename T>
|
||||
struct Bind {
|
||||
typedef Tmpl<T> type;
|
||||
};
|
||||
};
|
||||
|
||||
# define GTEST_BIND_(TmplSel, T) \
|
||||
TmplSel::template Bind<T>::type
|
||||
|
||||
// A unique struct template used as the default value for the
|
||||
// arguments of class template Templates. This allows us to simulate
|
||||
// variadic templates (e.g. Templates<int>, Templates<int, double>,
|
||||
// and etc), which C++ doesn't support directly.
|
||||
template <typename T>
|
||||
struct NoneT {};
|
||||
|
||||
// The following family of struct and struct templates are used to
|
||||
// represent template lists. In particular, TemplatesN<T1, T2, ...,
|
||||
// TN> represents a list of N templates (T1, T2, ..., and TN). Except
|
||||
// for Templates0, every struct in the family has two member types:
|
||||
// Head for the selector of the first template in the list, and Tail
|
||||
// for the rest of the list.
|
||||
|
||||
// The empty template list.
|
||||
struct Templates0 {};
|
||||
|
||||
// Template lists of length 1, 2, 3, and so on.
|
||||
|
||||
template <GTEST_TEMPLATE_ T1>
|
||||
struct Templates1 {
|
||||
typedef TemplateSel<T1> Head;
|
||||
typedef Templates0 Tail;
|
||||
};
|
||||
|
||||
$range i 2..n
|
||||
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
$range k 2..i
|
||||
template <$for j, [[GTEST_TEMPLATE_ T$j]]>
|
||||
struct Templates$i {
|
||||
typedef TemplateSel<T1> Head;
|
||||
typedef Templates$(i-1)<$for k, [[T$k]]> Tail;
|
||||
};
|
||||
|
||||
|
||||
]]
|
||||
|
||||
// We don't want to require the users to write TemplatesN<...> directly,
|
||||
// as that would require them to count the length. Templates<...> is much
|
||||
// easier to write, but generates horrible messages when there is a
|
||||
// compiler error, as gcc insists on printing out each template
|
||||
// argument, even if it has the default value (this means Templates<list>
|
||||
// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
|
||||
// errors).
|
||||
//
|
||||
// Our solution is to combine the best part of the two approaches: a
|
||||
// user would write Templates<T1, ..., TN>, and Google Test will translate
|
||||
// that to TemplatesN<T1, ..., TN> internally to make error messages
|
||||
// readable. The translation is done by the 'type' member of the
|
||||
// Templates template.
|
||||
|
||||
$range i 1..n
|
||||
template <$for i, [[GTEST_TEMPLATE_ T$i = NoneT]]>
|
||||
struct Templates {
|
||||
typedef Templates$n<$for i, [[T$i]]> type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Templates<$for i, [[NoneT]]> {
|
||||
typedef Templates0 type;
|
||||
};
|
||||
|
||||
$range i 1..n-1
|
||||
$for i [[
|
||||
$range j 1..i
|
||||
$range k i+1..n
|
||||
template <$for j, [[GTEST_TEMPLATE_ T$j]]>
|
||||
struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> {
|
||||
typedef Templates$i<$for j, [[T$j]]> type;
|
||||
};
|
||||
|
||||
]]
|
||||
|
||||
// The TypeList template makes it possible to use either a single type
|
||||
// or a Types<...> list in TYPED_TEST_CASE() and
|
||||
// INSTANTIATE_TYPED_TEST_CASE_P().
|
||||
|
||||
template <typename T>
|
||||
struct TypeList {
|
||||
typedef Types1<T> type;
|
||||
};
|
||||
|
||||
|
||||
$range i 1..n
|
||||
template <$for i, [[typename T$i]]>
|
||||
struct TypeList<Types<$for i, [[T$i]]> > {
|
||||
typedef typename Types<$for i, [[T$i]]>::type type;
|
||||
};
|
||||
|
||||
#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
|
||||
61
venv/lib/python3.12/site-packages/mypyc/external/googletest/make/Makefile
vendored
Normal file
61
venv/lib/python3.12/site-packages/mypyc/external/googletest/make/Makefile
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# A sample Makefile for building Google Test and using it in user
|
||||
# tests. Please tweak it to suit your environment and project. You
|
||||
# may want to move it to your project's root directory.
|
||||
#
|
||||
# SYNOPSIS:
|
||||
#
|
||||
# make [all] - makes everything.
|
||||
# make TARGET - makes the given target.
|
||||
# make clean - removes all files generated by make.
|
||||
|
||||
# Please tweak the following variable definitions as needed by your
|
||||
# project, except GTEST_HEADERS, which you can use in your own targets
|
||||
# but shouldn't modify.
|
||||
|
||||
# Points to the root of Google Test, relative to where this file is.
|
||||
# Remember to tweak this if you move this file.
|
||||
GTEST_DIR = ..
|
||||
|
||||
# Flags passed to the preprocessor.
|
||||
# Set Google Test's header directory as a system directory, such that
|
||||
# the compiler doesn't generate warnings in Google Test headers.
|
||||
CPPFLAGS += -isystem $(GTEST_DIR)/include
|
||||
|
||||
# Flags passed to the C++ compiler.
|
||||
CXXFLAGS += -g -Wall -Wextra -pthread -fPIC
|
||||
|
||||
# All Google Test headers. Usually you shouldn't change this
|
||||
# definition.
|
||||
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
|
||||
$(GTEST_DIR)/include/gtest/internal/*.h
|
||||
|
||||
# House-keeping build targets.
|
||||
|
||||
all : libgtest.a libgtest_main.a
|
||||
|
||||
clean :
|
||||
rm -f libgtest.a libgtest_main.a *.o
|
||||
|
||||
# Builds libgtest.a and libgtest_main.a.
|
||||
|
||||
# Usually you shouldn't tweak such internal variables, indicated by a
|
||||
# trailing _.
|
||||
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)
|
||||
|
||||
# For simplicity and to avoid depending on Google Test's
|
||||
# implementation details, the dependencies specified below are
|
||||
# conservative and not optimized. This is fine as Google Test
|
||||
# compiles fast and for ordinary users its source rarely changes.
|
||||
gtest-all.o : $(GTEST_SRCS_)
|
||||
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
|
||||
$(GTEST_DIR)/src/gtest-all.cc
|
||||
|
||||
gtest_main.o : $(GTEST_SRCS_)
|
||||
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
|
||||
$(GTEST_DIR)/src/gtest_main.cc
|
||||
|
||||
libgtest.a : gtest-all.o
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
libgtest_main.a : gtest-all.o gtest_main.o
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
48
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-all.cc
vendored
Normal file
48
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-all.cc
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2008, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: mheule@google.com (Markus Heule)
|
||||
//
|
||||
// Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// Sometimes it's desirable to build Google Test by compiling a single file.
|
||||
// This file serves this purpose.
|
||||
|
||||
// This line ensures that gtest.h can be compiled on its own, even
|
||||
// when it's fused.
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// The following lines pull in the real gtest *.cc files.
|
||||
#include "src/gtest.cc"
|
||||
#include "src/gtest-death-test.cc"
|
||||
#include "src/gtest-filepath.cc"
|
||||
#include "src/gtest-port.cc"
|
||||
#include "src/gtest-printers.cc"
|
||||
#include "src/gtest-test-part.cc"
|
||||
#include "src/gtest-typed-test.cc"
|
||||
1342
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-death-test.cc
vendored
Normal file
1342
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-death-test.cc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
387
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-filepath.cc
vendored
Normal file
387
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-filepath.cc
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
// Copyright 2008, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Authors: keith.ray@gmail.com (Keith Ray)
|
||||
|
||||
#include "gtest/gtest-message.h"
|
||||
#include "gtest/internal/gtest-filepath.h"
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if GTEST_OS_WINDOWS_MOBILE
|
||||
# include <windows.h>
|
||||
#elif GTEST_OS_WINDOWS
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
#elif GTEST_OS_SYMBIAN
|
||||
// Symbian OpenC has PATH_MAX in sys/syslimits.h
|
||||
# include <sys/syslimits.h>
|
||||
#else
|
||||
# include <limits.h>
|
||||
# include <climits> // Some Linux distributions define PATH_MAX here.
|
||||
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||
|
||||
#if GTEST_OS_WINDOWS
|
||||
# define GTEST_PATH_MAX_ _MAX_PATH
|
||||
#elif defined(PATH_MAX)
|
||||
# define GTEST_PATH_MAX_ PATH_MAX
|
||||
#elif defined(_XOPEN_PATH_MAX)
|
||||
# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
|
||||
#else
|
||||
# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
|
||||
#endif // GTEST_OS_WINDOWS
|
||||
|
||||
#include "gtest/internal/gtest-string.h"
|
||||
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
|
||||
#if GTEST_OS_WINDOWS
|
||||
// On Windows, '\\' is the standard path separator, but many tools and the
|
||||
// Windows API also accept '/' as an alternate path separator. Unless otherwise
|
||||
// noted, a file path can contain either kind of path separators, or a mixture
|
||||
// of them.
|
||||
const char kPathSeparator = '\\';
|
||||
const char kAlternatePathSeparator = '/';
|
||||
const char kAlternatePathSeparatorString[] = "/";
|
||||
# if GTEST_OS_WINDOWS_MOBILE
|
||||
// Windows CE doesn't have a current directory. You should not use
|
||||
// the current directory in tests on Windows CE, but this at least
|
||||
// provides a reasonable fallback.
|
||||
const char kCurrentDirectoryString[] = "\\";
|
||||
// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
|
||||
const DWORD kInvalidFileAttributes = 0xffffffff;
|
||||
# else
|
||||
const char kCurrentDirectoryString[] = ".\\";
|
||||
# endif // GTEST_OS_WINDOWS_MOBILE
|
||||
#else
|
||||
const char kPathSeparator = '/';
|
||||
const char kCurrentDirectoryString[] = "./";
|
||||
#endif // GTEST_OS_WINDOWS
|
||||
|
||||
// Returns whether the given character is a valid path separator.
|
||||
static bool IsPathSeparator(char c) {
|
||||
#if GTEST_HAS_ALT_PATH_SEP_
|
||||
return (c == kPathSeparator) || (c == kAlternatePathSeparator);
|
||||
#else
|
||||
return c == kPathSeparator;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the current working directory, or "" if unsuccessful.
|
||||
FilePath FilePath::GetCurrentDir() {
|
||||
#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
|
||||
// Windows CE doesn't have a current directory, so we just return
|
||||
// something reasonable.
|
||||
return FilePath(kCurrentDirectoryString);
|
||||
#elif GTEST_OS_WINDOWS
|
||||
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
|
||||
return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
|
||||
#else
|
||||
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
|
||||
char* result = getcwd(cwd, sizeof(cwd));
|
||||
# if GTEST_OS_NACL
|
||||
// getcwd will likely fail in NaCl due to the sandbox, so return something
|
||||
// reasonable. The user may have provided a shim implementation for getcwd,
|
||||
// however, so fallback only when failure is detected.
|
||||
return FilePath(result == NULL ? kCurrentDirectoryString : cwd);
|
||||
# endif // GTEST_OS_NACL
|
||||
return FilePath(result == NULL ? "" : cwd);
|
||||
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||
}
|
||||
|
||||
// Returns a copy of the FilePath with the case-insensitive extension removed.
|
||||
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
|
||||
// FilePath("dir/file"). If a case-insensitive extension is not
|
||||
// found, returns a copy of the original FilePath.
|
||||
FilePath FilePath::RemoveExtension(const char* extension) const {
|
||||
const std::string dot_extension = std::string(".") + extension;
|
||||
if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
|
||||
return FilePath(pathname_.substr(
|
||||
0, pathname_.length() - dot_extension.length()));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns a pointer to the last occurence of a valid path separator in
|
||||
// the FilePath. On Windows, for example, both '/' and '\' are valid path
|
||||
// separators. Returns NULL if no path separator was found.
|
||||
const char* FilePath::FindLastPathSeparator() const {
|
||||
const char* const last_sep = strrchr(c_str(), kPathSeparator);
|
||||
#if GTEST_HAS_ALT_PATH_SEP_
|
||||
const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
|
||||
// Comparing two pointers of which only one is NULL is undefined.
|
||||
if (last_alt_sep != NULL &&
|
||||
(last_sep == NULL || last_alt_sep > last_sep)) {
|
||||
return last_alt_sep;
|
||||
}
|
||||
#endif
|
||||
return last_sep;
|
||||
}
|
||||
|
||||
// Returns a copy of the FilePath with the directory part removed.
|
||||
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
|
||||
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
|
||||
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
|
||||
// returns an empty FilePath ("").
|
||||
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||
FilePath FilePath::RemoveDirectoryName() const {
|
||||
const char* const last_sep = FindLastPathSeparator();
|
||||
return last_sep ? FilePath(last_sep + 1) : *this;
|
||||
}
|
||||
|
||||
// RemoveFileName returns the directory path with the filename removed.
|
||||
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
|
||||
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
|
||||
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
|
||||
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
|
||||
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||
FilePath FilePath::RemoveFileName() const {
|
||||
const char* const last_sep = FindLastPathSeparator();
|
||||
std::string dir;
|
||||
if (last_sep) {
|
||||
dir = std::string(c_str(), last_sep + 1 - c_str());
|
||||
} else {
|
||||
dir = kCurrentDirectoryString;
|
||||
}
|
||||
return FilePath(dir);
|
||||
}
|
||||
|
||||
// Helper functions for naming files in a directory for xml output.
|
||||
|
||||
// Given directory = "dir", base_name = "test", number = 0,
|
||||
// extension = "xml", returns "dir/test.xml". If number is greater
|
||||
// than zero (e.g., 12), returns "dir/test_12.xml".
|
||||
// On Windows platform, uses \ as the separator rather than /.
|
||||
FilePath FilePath::MakeFileName(const FilePath& directory,
|
||||
const FilePath& base_name,
|
||||
int number,
|
||||
const char* extension) {
|
||||
std::string file;
|
||||
if (number == 0) {
|
||||
file = base_name.string() + "." + extension;
|
||||
} else {
|
||||
file = base_name.string() + "_" + StreamableToString(number)
|
||||
+ "." + extension;
|
||||
}
|
||||
return ConcatPaths(directory, FilePath(file));
|
||||
}
|
||||
|
||||
// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
|
||||
// On Windows, uses \ as the separator rather than /.
|
||||
FilePath FilePath::ConcatPaths(const FilePath& directory,
|
||||
const FilePath& relative_path) {
|
||||
if (directory.IsEmpty())
|
||||
return relative_path;
|
||||
const FilePath dir(directory.RemoveTrailingPathSeparator());
|
||||
return FilePath(dir.string() + kPathSeparator + relative_path.string());
|
||||
}
|
||||
|
||||
// Returns true if pathname describes something findable in the file-system,
|
||||
// either a file, directory, or whatever.
|
||||
bool FilePath::FileOrDirectoryExists() const {
|
||||
#if GTEST_OS_WINDOWS_MOBILE
|
||||
LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
|
||||
const DWORD attributes = GetFileAttributes(unicode);
|
||||
delete [] unicode;
|
||||
return attributes != kInvalidFileAttributes;
|
||||
#else
|
||||
posix::StatStruct file_stat;
|
||||
return posix::Stat(pathname_.c_str(), &file_stat) == 0;
|
||||
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||
}
|
||||
|
||||
// Returns true if pathname describes a directory in the file-system
|
||||
// that exists.
|
||||
bool FilePath::DirectoryExists() const {
|
||||
bool result = false;
|
||||
#if GTEST_OS_WINDOWS
|
||||
// Don't strip off trailing separator if path is a root directory on
|
||||
// Windows (like "C:\\").
|
||||
const FilePath& path(IsRootDirectory() ? *this :
|
||||
RemoveTrailingPathSeparator());
|
||||
#else
|
||||
const FilePath& path(*this);
|
||||
#endif
|
||||
|
||||
#if GTEST_OS_WINDOWS_MOBILE
|
||||
LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
|
||||
const DWORD attributes = GetFileAttributes(unicode);
|
||||
delete [] unicode;
|
||||
if ((attributes != kInvalidFileAttributes) &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
result = true;
|
||||
}
|
||||
#else
|
||||
posix::StatStruct file_stat;
|
||||
result = posix::Stat(path.c_str(), &file_stat) == 0 &&
|
||||
posix::IsDir(file_stat);
|
||||
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns true if pathname describes a root directory. (Windows has one
|
||||
// root directory per disk drive.)
|
||||
bool FilePath::IsRootDirectory() const {
|
||||
#if GTEST_OS_WINDOWS
|
||||
// TODO(wan@google.com): on Windows a network share like
|
||||
// \\server\share can be a root directory, although it cannot be the
|
||||
// current directory. Handle this properly.
|
||||
return pathname_.length() == 3 && IsAbsolutePath();
|
||||
#else
|
||||
return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns true if pathname describes an absolute path.
|
||||
bool FilePath::IsAbsolutePath() const {
|
||||
const char* const name = pathname_.c_str();
|
||||
#if GTEST_OS_WINDOWS
|
||||
return pathname_.length() >= 3 &&
|
||||
((name[0] >= 'a' && name[0] <= 'z') ||
|
||||
(name[0] >= 'A' && name[0] <= 'Z')) &&
|
||||
name[1] == ':' &&
|
||||
IsPathSeparator(name[2]);
|
||||
#else
|
||||
return IsPathSeparator(name[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns a pathname for a file that does not currently exist. The pathname
|
||||
// will be directory/base_name.extension or
|
||||
// directory/base_name_<number>.extension if directory/base_name.extension
|
||||
// already exists. The number will be incremented until a pathname is found
|
||||
// that does not already exist.
|
||||
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
|
||||
// There could be a race condition if two or more processes are calling this
|
||||
// function at the same time -- they could both pick the same filename.
|
||||
FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
|
||||
const FilePath& base_name,
|
||||
const char* extension) {
|
||||
FilePath full_pathname;
|
||||
int number = 0;
|
||||
do {
|
||||
full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
|
||||
} while (full_pathname.FileOrDirectoryExists());
|
||||
return full_pathname;
|
||||
}
|
||||
|
||||
// Returns true if FilePath ends with a path separator, which indicates that
|
||||
// it is intended to represent a directory. Returns false otherwise.
|
||||
// This does NOT check that a directory (or file) actually exists.
|
||||
bool FilePath::IsDirectory() const {
|
||||
return !pathname_.empty() &&
|
||||
IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
|
||||
}
|
||||
|
||||
// Create directories so that path exists. Returns true if successful or if
|
||||
// the directories already exist; returns false if unable to create directories
|
||||
// for any reason.
|
||||
bool FilePath::CreateDirectoriesRecursively() const {
|
||||
if (!this->IsDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pathname_.length() == 0 || this->DirectoryExists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
|
||||
return parent.CreateDirectoriesRecursively() && this->CreateFolder();
|
||||
}
|
||||
|
||||
// Create the directory so that path exists. Returns true if successful or
|
||||
// if the directory already exists; returns false if unable to create the
|
||||
// directory for any reason, including if the parent directory does not
|
||||
// exist. Not named "CreateDirectory" because that's a macro on Windows.
|
||||
bool FilePath::CreateFolder() const {
|
||||
#if GTEST_OS_WINDOWS_MOBILE
|
||||
FilePath removed_sep(this->RemoveTrailingPathSeparator());
|
||||
LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
|
||||
int result = CreateDirectory(unicode, NULL) ? 0 : -1;
|
||||
delete [] unicode;
|
||||
#elif GTEST_OS_WINDOWS
|
||||
int result = _mkdir(pathname_.c_str());
|
||||
#else
|
||||
int result = mkdir(pathname_.c_str(), 0777);
|
||||
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||
|
||||
if (result == -1) {
|
||||
return this->DirectoryExists(); // An error is OK if the directory exists.
|
||||
}
|
||||
return true; // No error.
|
||||
}
|
||||
|
||||
// If input name has a trailing separator character, remove it and return the
|
||||
// name, otherwise return the name string unmodified.
|
||||
// On Windows platform, uses \ as the separator, other platforms use /.
|
||||
FilePath FilePath::RemoveTrailingPathSeparator() const {
|
||||
return IsDirectory()
|
||||
? FilePath(pathname_.substr(0, pathname_.length() - 1))
|
||||
: *this;
|
||||
}
|
||||
|
||||
// Removes any redundant separators that might be in the pathname.
|
||||
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
||||
// redundancies that might be in a pathname involving "." or "..".
|
||||
// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
|
||||
void FilePath::Normalize() {
|
||||
if (pathname_.c_str() == NULL) {
|
||||
pathname_ = "";
|
||||
return;
|
||||
}
|
||||
const char* src = pathname_.c_str();
|
||||
char* const dest = new char[pathname_.length() + 1];
|
||||
char* dest_ptr = dest;
|
||||
memset(dest_ptr, 0, pathname_.length() + 1);
|
||||
|
||||
while (*src != '\0') {
|
||||
*dest_ptr = *src;
|
||||
if (!IsPathSeparator(*src)) {
|
||||
src++;
|
||||
} else {
|
||||
#if GTEST_HAS_ALT_PATH_SEP_
|
||||
if (*dest_ptr == kAlternatePathSeparator) {
|
||||
*dest_ptr = kPathSeparator;
|
||||
}
|
||||
#endif
|
||||
while (IsPathSeparator(*src))
|
||||
src++;
|
||||
}
|
||||
dest_ptr++;
|
||||
}
|
||||
*dest_ptr = '\0';
|
||||
pathname_ = dest;
|
||||
delete[] dest;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
1183
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-internal-inl.h
vendored
Normal file
1183
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-internal-inl.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1259
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-port.cc
vendored
Normal file
1259
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-port.cc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
373
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-printers.cc
vendored
Normal file
373
venv/lib/python3.12/site-packages/mypyc/external/googletest/src/gtest-printers.cc
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
// Copyright 2007, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wan@google.com (Zhanyong Wan)
|
||||
|
||||
// Google Test - The Google C++ Testing Framework
|
||||
//
|
||||
// This file implements a universal value printer that can print a
|
||||
// value of any type T:
|
||||
//
|
||||
// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
|
||||
//
|
||||
// It uses the << operator when possible, and prints the bytes in the
|
||||
// object otherwise. A user can override its behavior for a class
|
||||
// type Foo by defining either operator<<(::std::ostream&, const Foo&)
|
||||
// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
|
||||
// defines Foo.
|
||||
|
||||
#include "gtest/gtest-printers.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <cwchar>
|
||||
#include <ostream> // NOLINT
|
||||
#include <string>
|
||||
#include "gtest/internal/gtest-port.h"
|
||||
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::std::ostream;
|
||||
|
||||
// Prints a segment of bytes in the given object.
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
|
||||
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
|
||||
size_t count, ostream* os) {
|
||||
char text[5] = "";
|
||||
for (size_t i = 0; i != count; i++) {
|
||||
const size_t j = start + i;
|
||||
if (i != 0) {
|
||||
// Organizes the bytes into groups of 2 for easy parsing by
|
||||
// human.
|
||||
if ((j % 2) == 0)
|
||||
*os << ' ';
|
||||
else
|
||||
*os << '-';
|
||||
}
|
||||
GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
|
||||
*os << text;
|
||||
}
|
||||
}
|
||||
|
||||
// Prints the bytes in the given value to the given ostream.
|
||||
void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
|
||||
ostream* os) {
|
||||
// Tells the user how big the object is.
|
||||
*os << count << "-byte object <";
|
||||
|
||||
const size_t kThreshold = 132;
|
||||
const size_t kChunkSize = 64;
|
||||
// If the object size is bigger than kThreshold, we'll have to omit
|
||||
// some details by printing only the first and the last kChunkSize
|
||||
// bytes.
|
||||
// TODO(wan): let the user control the threshold using a flag.
|
||||
if (count < kThreshold) {
|
||||
PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
|
||||
} else {
|
||||
PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
|
||||
*os << " ... ";
|
||||
// Rounds up to 2-byte boundary.
|
||||
const size_t resume_pos = (count - kChunkSize + 1)/2*2;
|
||||
PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
|
||||
}
|
||||
*os << ">";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace internal2 {
|
||||
|
||||
// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
|
||||
// given object. The delegation simplifies the implementation, which
|
||||
// uses the << operator and thus is easier done outside of the
|
||||
// ::testing::internal namespace, which contains a << operator that
|
||||
// sometimes conflicts with the one in STL.
|
||||
void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
|
||||
ostream* os) {
|
||||
PrintBytesInObjectToImpl(obj_bytes, count, os);
|
||||
}
|
||||
|
||||
} // namespace internal2
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Depending on the value of a char (or wchar_t), we print it in one
|
||||
// of three formats:
|
||||
// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
|
||||
// - as a hexidecimal escape sequence (e.g. '\x7F'), or
|
||||
// - as a special escape sequence (e.g. '\r', '\n').
|
||||
enum CharFormat {
|
||||
kAsIs,
|
||||
kHexEscape,
|
||||
kSpecialEscape
|
||||
};
|
||||
|
||||
// Returns true if c is a printable ASCII character. We test the
|
||||
// value of c directly instead of calling isprint(), which is buggy on
|
||||
// Windows Mobile.
|
||||
inline bool IsPrintableAscii(wchar_t c) {
|
||||
return 0x20 <= c && c <= 0x7E;
|
||||
}
|
||||
|
||||
// Prints a wide or narrow char c as a character literal without the
|
||||
// quotes, escaping it when necessary; returns how c was formatted.
|
||||
// The template argument UnsignedChar is the unsigned version of Char,
|
||||
// which is the type of c.
|
||||
template <typename UnsignedChar, typename Char>
|
||||
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
|
||||
switch (static_cast<wchar_t>(c)) {
|
||||
case L'\0':
|
||||
*os << "\\0";
|
||||
break;
|
||||
case L'\'':
|
||||
*os << "\\'";
|
||||
break;
|
||||
case L'\\':
|
||||
*os << "\\\\";
|
||||
break;
|
||||
case L'\a':
|
||||
*os << "\\a";
|
||||
break;
|
||||
case L'\b':
|
||||
*os << "\\b";
|
||||
break;
|
||||
case L'\f':
|
||||
*os << "\\f";
|
||||
break;
|
||||
case L'\n':
|
||||
*os << "\\n";
|
||||
break;
|
||||
case L'\r':
|
||||
*os << "\\r";
|
||||
break;
|
||||
case L'\t':
|
||||
*os << "\\t";
|
||||
break;
|
||||
case L'\v':
|
||||
*os << "\\v";
|
||||
break;
|
||||
default:
|
||||
if (IsPrintableAscii(c)) {
|
||||
*os << static_cast<char>(c);
|
||||
return kAsIs;
|
||||
} else {
|
||||
*os << "\\x" + String::FormatHexInt(static_cast<UnsignedChar>(c));
|
||||
return kHexEscape;
|
||||
}
|
||||
}
|
||||
return kSpecialEscape;
|
||||
}
|
||||
|
||||
// Prints a wchar_t c as if it's part of a string literal, escaping it when
|
||||
// necessary; returns how c was formatted.
|
||||
static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
|
||||
switch (c) {
|
||||
case L'\'':
|
||||
*os << "'";
|
||||
return kAsIs;
|
||||
case L'"':
|
||||
*os << "\\\"";
|
||||
return kSpecialEscape;
|
||||
default:
|
||||
return PrintAsCharLiteralTo<wchar_t>(c, os);
|
||||
}
|
||||
}
|
||||
|
||||
// Prints a char c as if it's part of a string literal, escaping it when
|
||||
// necessary; returns how c was formatted.
|
||||
static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
|
||||
return PrintAsStringLiteralTo(
|
||||
static_cast<wchar_t>(static_cast<unsigned char>(c)), os);
|
||||
}
|
||||
|
||||
// Prints a wide or narrow character c and its code. '\0' is printed
|
||||
// as "'\\0'", other unprintable characters are also properly escaped
|
||||
// using the standard C++ escape sequence. The template argument
|
||||
// UnsignedChar is the unsigned version of Char, which is the type of c.
|
||||
template <typename UnsignedChar, typename Char>
|
||||
void PrintCharAndCodeTo(Char c, ostream* os) {
|
||||
// First, print c as a literal in the most readable form we can find.
|
||||
*os << ((sizeof(c) > 1) ? "L'" : "'");
|
||||
const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
|
||||
*os << "'";
|
||||
|
||||
// To aid user debugging, we also print c's code in decimal, unless
|
||||
// it's 0 (in which case c was printed as '\\0', making the code
|
||||
// obvious).
|
||||
if (c == 0)
|
||||
return;
|
||||
*os << " (" << static_cast<int>(c);
|
||||
|
||||
// For more convenience, we print c's code again in hexidecimal,
|
||||
// unless c was already printed in the form '\x##' or the code is in
|
||||
// [1, 9].
|
||||
if (format == kHexEscape || (1 <= c && c <= 9)) {
|
||||
// Do nothing.
|
||||
} else {
|
||||
*os << ", 0x" << String::FormatHexInt(static_cast<UnsignedChar>(c));
|
||||
}
|
||||
*os << ")";
|
||||
}
|
||||
|
||||
void PrintTo(unsigned char c, ::std::ostream* os) {
|
||||
PrintCharAndCodeTo<unsigned char>(c, os);
|
||||
}
|
||||
void PrintTo(signed char c, ::std::ostream* os) {
|
||||
PrintCharAndCodeTo<unsigned char>(c, os);
|
||||
}
|
||||
|
||||
// Prints a wchar_t as a symbol if it is printable or as its internal
|
||||
// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
|
||||
void PrintTo(wchar_t wc, ostream* os) {
|
||||
PrintCharAndCodeTo<wchar_t>(wc, os);
|
||||
}
|
||||
|
||||
// Prints the given array of characters to the ostream. CharType must be either
|
||||
// char or wchar_t.
|
||||
// The array starts at begin, the length is len, it may include '\0' characters
|
||||
// and may not be NUL-terminated.
|
||||
template <typename CharType>
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
|
||||
static void PrintCharsAsStringTo(
|
||||
const CharType* begin, size_t len, ostream* os) {
|
||||
const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
|
||||
*os << kQuoteBegin;
|
||||
bool is_previous_hex = false;
|
||||
for (size_t index = 0; index < len; ++index) {
|
||||
const CharType cur = begin[index];
|
||||
if (is_previous_hex && IsXDigit(cur)) {
|
||||
// Previous character is of '\x..' form and this character can be
|
||||
// interpreted as another hexadecimal digit in its number. Break string to
|
||||
// disambiguate.
|
||||
*os << "\" " << kQuoteBegin;
|
||||
}
|
||||
is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
|
||||
}
|
||||
*os << "\"";
|
||||
}
|
||||
|
||||
// Prints a (const) char/wchar_t array of 'len' elements, starting at address
|
||||
// 'begin'. CharType must be either char or wchar_t.
|
||||
template <typename CharType>
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
|
||||
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
|
||||
static void UniversalPrintCharArray(
|
||||
const CharType* begin, size_t len, ostream* os) {
|
||||
// The code
|
||||
// const char kFoo[] = "foo";
|
||||
// generates an array of 4, not 3, elements, with the last one being '\0'.
|
||||
//
|
||||
// Therefore when printing a char array, we don't print the last element if
|
||||
// it's '\0', such that the output matches the string literal as it's
|
||||
// written in the source code.
|
||||
if (len > 0 && begin[len - 1] == '\0') {
|
||||
PrintCharsAsStringTo(begin, len - 1, os);
|
||||
return;
|
||||
}
|
||||
|
||||
// If, however, the last element in the array is not '\0', e.g.
|
||||
// const char kFoo[] = { 'f', 'o', 'o' };
|
||||
// we must print the entire array. We also print a message to indicate
|
||||
// that the array is not NUL-terminated.
|
||||
PrintCharsAsStringTo(begin, len, os);
|
||||
*os << " (no terminating NUL)";
|
||||
}
|
||||
|
||||
// Prints a (const) char array of 'len' elements, starting at address 'begin'.
|
||||
void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
|
||||
UniversalPrintCharArray(begin, len, os);
|
||||
}
|
||||
|
||||
// Prints a (const) wchar_t array of 'len' elements, starting at address
|
||||
// 'begin'.
|
||||
void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
|
||||
UniversalPrintCharArray(begin, len, os);
|
||||
}
|
||||
|
||||
// Prints the given C string to the ostream.
|
||||
void PrintTo(const char* s, ostream* os) {
|
||||
if (s == NULL) {
|
||||
*os << "NULL";
|
||||
} else {
|
||||
*os << ImplicitCast_<const void*>(s) << " pointing to ";
|
||||
PrintCharsAsStringTo(s, strlen(s), os);
|
||||
}
|
||||
}
|
||||
|
||||
// MSVC compiler can be configured to define whar_t as a typedef
|
||||
// of unsigned short. Defining an overload for const wchar_t* in that case
|
||||
// would cause pointers to unsigned shorts be printed as wide strings,
|
||||
// possibly accessing more memory than intended and causing invalid
|
||||
// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
|
||||
// wchar_t is implemented as a native type.
|
||||
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
|
||||
// Prints the given wide C string to the ostream.
|
||||
void PrintTo(const wchar_t* s, ostream* os) {
|
||||
if (s == NULL) {
|
||||
*os << "NULL";
|
||||
} else {
|
||||
*os << ImplicitCast_<const void*>(s) << " pointing to ";
|
||||
PrintCharsAsStringTo(s, std::wcslen(s), os);
|
||||
}
|
||||
}
|
||||
#endif // wchar_t is native
|
||||
|
||||
// Prints a ::string object.
|
||||
#if GTEST_HAS_GLOBAL_STRING
|
||||
void PrintStringTo(const ::string& s, ostream* os) {
|
||||
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||
}
|
||||
#endif // GTEST_HAS_GLOBAL_STRING
|
||||
|
||||
void PrintStringTo(const ::std::string& s, ostream* os) {
|
||||
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||
}
|
||||
|
||||
// Prints a ::wstring object.
|
||||
#if GTEST_HAS_GLOBAL_WSTRING
|
||||
void PrintWideStringTo(const ::wstring& s, ostream* os) {
|
||||
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||
}
|
||||
#endif // GTEST_HAS_GLOBAL_WSTRING
|
||||
|
||||
#if GTEST_HAS_STD_WSTRING
|
||||
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
|
||||
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||
}
|
||||
#endif // GTEST_HAS_STD_WSTRING
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace testing
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user