Module tourniquet.patch_lang
Expand source code
import itertools
from abc import ABC, abstractmethod
from typing import Callable, Iterator, List, Optional
from . import models
from .error import PatchConcretizationError
from .location import Location
class Expression(ABC):
"""
Represents an abstract source "expression".
`Expression` is an abstract base class whose interfaces should be consumed via
specific derived classes.
"""
@abstractmethod
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
yield from ()
def view(self, db: models.DB, location: Location) -> str:
return "Expression()"
class Lit(Expression):
"""
Represents a literal source string.
Template authors can use this to insert pre-formed source code, or code that doesn't need
to be concretized by location.
"""
def __init__(self, expr: str):
"""
Create a new `Lit` with the given source string.
Args:
expr: The source literal
"""
self.expr = expr
def concretize(self, _db, _location) -> Iterator[str]:
"""
Concretize this literal into its underlying source string.
"""
yield self.expr
def view(self, _db, _location):
return f"Lit({self.expr})"
class Variable(Expression):
"""
Represents an abstract source variable.
"""
# NOTE(ww): This is probably slightly unsound, in terms of concretizations
# produced: the set of variables in a function is not the set of variables
# guaranteed to be in scope at a line number.
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `Variable` into its potential names.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a concrete variable name
Raises:
PatchConcretizationError: If the scope of the variable can't be resolved
"""
# TODO(ww): We should also concretize with the available globals.
function = db.function_at(location)
if function is None:
raise PatchConcretizationError(
f"no function contains ({location.line}, {location.column})"
)
for var_decl in function.var_decls:
yield var_decl.name
def view(self, _db, _location) -> str:
return "Variable()"
class StaticBufferSize(Expression):
"""
Represents an abstract "sizeof(...)" expression.
"""
# Query for all static array types and return list of sizeof()..
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `StaticBufferSize` into its potential sizes.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a concrete `sizeof(...)` expression
Raises:
PatchConcretizationError: If the scope of the location is not a function
"""
# TODO(ww): We should also concretize with available globals.
function = db.function_at(location)
if function is None:
raise PatchConcretizationError(
f"no function contains ({location.line}, {location.column})"
)
for var_decl in function.var_decls:
if not var_decl.is_array:
continue
yield f"sizeof({var_decl.name})"
def view(self, _db, _location) -> str:
return "StaticBufferSize()"
class BinaryMathOperator(Expression):
"""
Represents the possible binary math operators, along with their lhs and rhs expressions.
"""
def __init__(self, lhs: Expression, rhs: Expression):
"""
Create a new `BinaryMathOperator` with the given `lhs` and `rhs`.
Args:
lhs: An `Expression` to use for the left-hand side
rhs: An `Expression` to use for the right-hand side
"""
self.lhs = lhs
self.rhs = rhs
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `BinaryMathOperator` into its possible operator expressions.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a concrete binary math expression
"""
lhs_exprs = self.lhs.concretize(db, location)
rhs_exprs = self.rhs.concretize(db, location)
for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs):
yield f"{lhs} + {rhs}"
yield f"{lhs} - {rhs}"
yield f"{lhs} / {rhs}"
yield f"{lhs} * {rhs}"
yield f"{lhs} << {rhs}"
# TODO(ww): Missing for unknown reasons: >>, &, |, ^, %
def view(self, db: models.DB, location: Location) -> str:
return f"BinaryMathOperator({self.lhs.view(db, location)}, {self.lhs.view(db, location)})"
class BinaryBoolOperator(Expression):
"""
Represents the possible binary boolean operators, along with their lhs and rhs expressions.
"""
def __init__(self, lhs: Expression, rhs: Expression):
"""
Create a new `BinaryBoolOperator` with the given `lhs` and `rhs`.
Args:
lhs: An `Expression` to use for the left-hand side
rhs: An `Expression` to use for the right-hand side
"""
self.lhs = lhs
self.rhs = rhs
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `BinaryBoolOperator` into its possible operator expressions.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a concrete binary boolean expression
"""
lhs_exprs = self.lhs.concretize(db, location)
rhs_exprs = self.rhs.concretize(db, location)
for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs):
yield f"{lhs} == {rhs}"
yield f"{lhs} != {rhs}"
yield f"{lhs} <= {rhs}"
yield f"{lhs} < {rhs}"
yield f"{lhs} >= {rhs}"
yield f"{lhs} > {rhs}"
# TODO(ww): If location is in a C++ source file, maybe add <=>
def view(self, db: models.DB, location: Location) -> str:
return f"BinaryBoolOperator({self.lhs.view(db, location)}, {self.rhs.view(db, location)})"
class LessThanExpr(Expression):
"""
Represents the less-than boolean operator, along with its lhs and rhs expressions.
"""
def __init__(self, lhs: Expression, rhs: Expression):
"""
Create a new `LessThanExpr` with the given `lhs` and `rhs`.
Args:
lhs: An `Expression` to use for the left-hand side.
rhs: An `Expression` to use for the right-hand side.
"""
self.lhs = lhs
self.rhs = rhs
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `LessThanExpr` into its possible operator expressions.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a concrete less-than boolean expression
"""
lhs_exprs = self.lhs.concretize(db, location)
rhs_exprs = self.rhs.concretize(db, location)
for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs):
yield f"{lhs} < {rhs}"
def view(self, db: models.DB, location: Location):
self.lhs.view(db, location) + " < " + self.rhs.view(db, location)
class Statement(ABC):
"""
Represents an source "statement".
`Statement` is an abstract base whose interfaces should be consumed via specific
derived classes.
"""
@abstractmethod
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
pass
@abstractmethod
def view(self, db: models.DB, location: Location):
pass
class StatementList:
"""
Represents a sequence of statements.
"""
def __init__(self, *args):
self.statements: List[Statement] = args
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `StatementList` into its possible statement seqences.
This involves concretizing each statement and taking their unique
product, such that every possible permutation of statements in the list
is produced.
Args:
db: The AST database to concretize against
locaton: The location to concretize at
Returns:
A generator of strings, each of which is a concreqte sequence of statements
"""
concretized = [stmt.concretize(db, location) for stmt in self.statements]
for items in set(itertools.product(*concretized)):
yield "\n".join(items)
def view(self, db: models.DB, location: Location) -> str:
final_str = ""
for stmt in self.statements:
final_str += stmt.view(db, location) + "\n"
return final_str
class IfStmt(Statement):
"""
Represents an `if(...) { ... }` statement.
"""
def __init__(self, cond_expr: Expression, *args: List[Statement]):
"""
Creates a new `IfStmt`.
Args:
cond_expr: The expression to evaluate
args: The statements to use in the body of the `if`
"""
self.cond_expr = cond_expr
self.statement_list = StatementList(*args)
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `IfStmt` into its possible statements.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is an `if` statement
"""
cond_list = self.cond_expr.concretize(db, location)
stmt_list = self.statement_list.concretize(db, location)
for (cond, stmt) in itertools.product(cond_list, stmt_list):
cand_str = "if (" + cond + ") {\n" + stmt + "\n}\n"
yield cand_str
def view(self, db: models.DB, location: Location) -> str:
if_str = "if (" + self.cond_expr.view(db, location) + ") {\n"
if_str += self.statement_list.view(db, location)
if_str += "\n}\n"
return if_str
class ElseStmt(Statement):
"""
Represents an `else { ... }` statement.
"""
def __init__(self, *args: List[Statement]):
"""
Creates a new `ElseStmt`.
Args:
args: The statements to use in the body of the `else`
"""
self.statement_list = StatementList(*args)
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `ElseStmt` into its possible statements.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is an `else` statement
"""
stmt_list = self.statement_list.concretize(db, location)
for stmt in stmt_list:
cand_str = "else {\n" + stmt + "\n}\n"
yield cand_str
def view(self, db: models.DB, location: Location) -> str:
return "else {\n" + self.statement_list.view(db, location) + "\n}\n"
class ReturnStmt(Statement):
"""
Represents a `return ...;` statement.
"""
def __init__(self, expr: Expression):
"""
Creates a new `ReturnStmt`.
Args:
expr: The expression to concretize for the `return`
"""
self.expr = expr
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `ReturnStmt` into its possible statements.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is an `return` statement
"""
expr_list = self.expr.concretize(db, location)
for exp in expr_list:
candidate_str = f"return {exp};"
yield candidate_str
def view(self, db: models.DB, location: Location):
return f"return {self.expr.view(db, location)};"
# TODO This should take a Node, which is something from... the DB? Maybe?
# TODO(ww): This should probably be renamed to ASTStmt or similar.
class NodeStmt(Statement):
"""
Represents a statement from the AST database, identified by location.
"""
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `NodeStmt` into its concrete statement.
Args:
db: The AST database to concretize against
location: The statement's location
Returns:
A generator of strings, whose single item is the extracted AST statement
"""
source_info = self.fetch_node(db, location)
yield source_info
def view(self, db, location) -> str:
# Fetch and create a node.
source_info = self.fetch_node(db, location)
return source_info
def fetch_node(self, db, location) -> str:
"""
Fetches the statement at the given location.
Args:
db: The AST database to query against.
location: The location of the statement.
Returns:
The statement, including trailing semicolon.
Raises:
PatchConcretizationError: If there is no statement at the supplied location.
"""
statement = db.statement_at(location)
if statement is None:
raise PatchConcretizationError(f"no statement at ({location.line}, {location.column})")
if statement.expr.endswith(";"):
return statement.expr
else:
return f"{statement.expr};"
class FixPattern:
"""
Represents a program "fix" that can be concretized into a sequence of candidate patches.
"""
def __init__(self, *args: List[Statement]):
"""
Create a new `FixPattern`.
Args:
args: The statements to use in the fix
"""
self.statement_list = StatementList(*args)
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize this `FixPattern` into a sequence of candidate patches.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a candidate patch
"""
yield from self.statement_list.concretize(db, location)
def view(self, db: models.DB, location: Location) -> str:
return self.statement_list.view(db, location)
class PatchTemplate:
"""
Represents a templatized patch, including a matcher callable.
"""
def __init__(
self, fix_pattern: FixPattern, matcher_func: Optional[Callable[[int, int], bool]] = None
):
"""
Create a new `PatchTemplate`.
Args:
fix_pattern: The fix pattern to concretize into patches
matcher_func: The callable to filter patch locations with, if any
"""
self.matcher_func = matcher_func
self.fix_pattern = fix_pattern
def matches(self, line: int, col: int) -> bool:
"""
Returns whether the given line and column is suitable for patch situation.
Calls `self.matcher_func` internally, if supplied.
Args:
line: The line to patch on
column: The column to patch on
Returns:
`True` if `matcher_func` was not supplied or returns `True`, `False` otherwise
"""
if self.matcher_func is None:
return True
return self.matcher_func(line, col)
def concretize(self, db: models.DB, location: Location) -> Iterator[str]:
"""
Concretize the inner `FixPattern` into a sequence of patch candidates.
Args:
db: The AST database to concretize against
location: The location to concretize at
Returns:
A generator of strings, each of which is a candidate patch
"""
yield from self.fix_pattern.concretize(db, location)
def view(self, db: models.DB, location: Location) -> str:
return self.fix_pattern.view(db, location)
Classes
class BinaryBoolOperator (lhs: Expression, rhs: Expression)-
Represents the possible binary boolean operators, along with their lhs and rhs expressions.
Create a new
BinaryBoolOperatorwith the givenlhsandrhs.Args
lhs- An
Expressionto use for the left-hand side rhs- An
Expressionto use for the right-hand side
Expand source code
class BinaryBoolOperator(Expression): """ Represents the possible binary boolean operators, along with their lhs and rhs expressions. """ def __init__(self, lhs: Expression, rhs: Expression): """ Create a new `BinaryBoolOperator` with the given `lhs` and `rhs`. Args: lhs: An `Expression` to use for the left-hand side rhs: An `Expression` to use for the right-hand side """ self.lhs = lhs self.rhs = rhs def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `BinaryBoolOperator` into its possible operator expressions. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete binary boolean expression """ lhs_exprs = self.lhs.concretize(db, location) rhs_exprs = self.rhs.concretize(db, location) for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs): yield f"{lhs} == {rhs}" yield f"{lhs} != {rhs}" yield f"{lhs} <= {rhs}" yield f"{lhs} < {rhs}" yield f"{lhs} >= {rhs}" yield f"{lhs} > {rhs}" # TODO(ww): If location is in a C++ source file, maybe add <=> def view(self, db: models.DB, location: Location) -> str: return f"BinaryBoolOperator({self.lhs.view(db, location)}, {self.rhs.view(db, location)})"Ancestors
- Expression
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
BinaryBoolOperatorinto its possible operator expressions.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a concrete binary boolean expression
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `BinaryBoolOperator` into its possible operator expressions. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete binary boolean expression """ lhs_exprs = self.lhs.concretize(db, location) rhs_exprs = self.rhs.concretize(db, location) for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs): yield f"{lhs} == {rhs}" yield f"{lhs} != {rhs}" yield f"{lhs} <= {rhs}" yield f"{lhs} < {rhs}" yield f"{lhs} >= {rhs}" yield f"{lhs} > {rhs}" # TODO(ww): If location is in a C++ source file, maybe add <=> def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: return f"BinaryBoolOperator({self.lhs.view(db, location)}, {self.rhs.view(db, location)})"
class BinaryMathOperator (lhs: Expression, rhs: Expression)-
Represents the possible binary math operators, along with their lhs and rhs expressions.
Create a new
BinaryMathOperatorwith the givenlhsandrhs.Args
lhs- An
Expressionto use for the left-hand side rhs- An
Expressionto use for the right-hand side
Expand source code
class BinaryMathOperator(Expression): """ Represents the possible binary math operators, along with their lhs and rhs expressions. """ def __init__(self, lhs: Expression, rhs: Expression): """ Create a new `BinaryMathOperator` with the given `lhs` and `rhs`. Args: lhs: An `Expression` to use for the left-hand side rhs: An `Expression` to use for the right-hand side """ self.lhs = lhs self.rhs = rhs def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `BinaryMathOperator` into its possible operator expressions. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete binary math expression """ lhs_exprs = self.lhs.concretize(db, location) rhs_exprs = self.rhs.concretize(db, location) for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs): yield f"{lhs} + {rhs}" yield f"{lhs} - {rhs}" yield f"{lhs} / {rhs}" yield f"{lhs} * {rhs}" yield f"{lhs} << {rhs}" # TODO(ww): Missing for unknown reasons: >>, &, |, ^, % def view(self, db: models.DB, location: Location) -> str: return f"BinaryMathOperator({self.lhs.view(db, location)}, {self.lhs.view(db, location)})"Ancestors
- Expression
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
BinaryMathOperatorinto its possible operator expressions.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a concrete binary math expression
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `BinaryMathOperator` into its possible operator expressions. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete binary math expression """ lhs_exprs = self.lhs.concretize(db, location) rhs_exprs = self.rhs.concretize(db, location) for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs): yield f"{lhs} + {rhs}" yield f"{lhs} - {rhs}" yield f"{lhs} / {rhs}" yield f"{lhs} * {rhs}" yield f"{lhs} << {rhs}" # TODO(ww): Missing for unknown reasons: >>, &, |, ^, % def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: return f"BinaryMathOperator({self.lhs.view(db, location)}, {self.lhs.view(db, location)})"
class ElseStmt (*args: List[Statement])-
Represents an
else { ... }statement.Creates a new
ElseStmt.Args
args- The statements to use in the body of the
else
Expand source code
class ElseStmt(Statement): """ Represents an `else { ... }` statement. """ def __init__(self, *args: List[Statement]): """ Creates a new `ElseStmt`. Args: args: The statements to use in the body of the `else` """ self.statement_list = StatementList(*args) def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `ElseStmt` into its possible statements. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is an `else` statement """ stmt_list = self.statement_list.concretize(db, location) for stmt in stmt_list: cand_str = "else {\n" + stmt + "\n}\n" yield cand_str def view(self, db: models.DB, location: Location) -> str: return "else {\n" + self.statement_list.view(db, location) + "\n}\n"Ancestors
- Statement
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
ElseStmtinto its possible statements.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is an
elsestatementExpand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `ElseStmt` into its possible statements. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is an `else` statement """ stmt_list = self.statement_list.concretize(db, location) for stmt in stmt_list: cand_str = "else {\n" + stmt + "\n}\n" yield cand_str def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: return "else {\n" + self.statement_list.view(db, location) + "\n}\n"
class Expression-
Represents an abstract source "expression".
Expressionis an abstract base class whose interfaces should be consumed via specific derived classes.Expand source code
class Expression(ABC): """ Represents an abstract source "expression". `Expression` is an abstract base class whose interfaces should be consumed via specific derived classes. """ @abstractmethod def concretize(self, db: models.DB, location: Location) -> Iterator[str]: yield from () def view(self, db: models.DB, location: Location) -> str: return "Expression()"Ancestors
- abc.ABC
Subclasses
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Expand source code
@abstractmethod def concretize(self, db: models.DB, location: Location) -> Iterator[str]: yield from () def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: return "Expression()"
class FixPattern (*args: List[Statement])-
Represents a program "fix" that can be concretized into a sequence of candidate patches.
Create a new
FixPattern.Args
args- The statements to use in the fix
Expand source code
class FixPattern: """ Represents a program "fix" that can be concretized into a sequence of candidate patches. """ def __init__(self, *args: List[Statement]): """ Create a new `FixPattern`. Args: args: The statements to use in the fix """ self.statement_list = StatementList(*args) def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `FixPattern` into a sequence of candidate patches. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a candidate patch """ yield from self.statement_list.concretize(db, location) def view(self, db: models.DB, location: Location) -> str: return self.statement_list.view(db, location)Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
FixPatterninto a sequence of candidate patches.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a candidate patch
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `FixPattern` into a sequence of candidate patches. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a candidate patch """ yield from self.statement_list.concretize(db, location) def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: return self.statement_list.view(db, location)
class IfStmt (cond_expr: Expression, *args: List[Statement])-
Represents an
if(...) { ... }statement.Creates a new
IfStmt.Args
cond_expr- The expression to evaluate
args- The statements to use in the body of the
if
Expand source code
class IfStmt(Statement): """ Represents an `if(...) { ... }` statement. """ def __init__(self, cond_expr: Expression, *args: List[Statement]): """ Creates a new `IfStmt`. Args: cond_expr: The expression to evaluate args: The statements to use in the body of the `if` """ self.cond_expr = cond_expr self.statement_list = StatementList(*args) def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `IfStmt` into its possible statements. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is an `if` statement """ cond_list = self.cond_expr.concretize(db, location) stmt_list = self.statement_list.concretize(db, location) for (cond, stmt) in itertools.product(cond_list, stmt_list): cand_str = "if (" + cond + ") {\n" + stmt + "\n}\n" yield cand_str def view(self, db: models.DB, location: Location) -> str: if_str = "if (" + self.cond_expr.view(db, location) + ") {\n" if_str += self.statement_list.view(db, location) if_str += "\n}\n" return if_strAncestors
- Statement
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
IfStmtinto its possible statements.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is an
ifstatementExpand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `IfStmt` into its possible statements. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is an `if` statement """ cond_list = self.cond_expr.concretize(db, location) stmt_list = self.statement_list.concretize(db, location) for (cond, stmt) in itertools.product(cond_list, stmt_list): cand_str = "if (" + cond + ") {\n" + stmt + "\n}\n" yield cand_str def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: if_str = "if (" + self.cond_expr.view(db, location) + ") {\n" if_str += self.statement_list.view(db, location) if_str += "\n}\n" return if_str
class LessThanExpr (lhs: Expression, rhs: Expression)-
Represents the less-than boolean operator, along with its lhs and rhs expressions.
Create a new
LessThanExprwith the givenlhsandrhs.Args
lhs- An
Expressionto use for the left-hand side. rhs- An
Expressionto use for the right-hand side.
Expand source code
class LessThanExpr(Expression): """ Represents the less-than boolean operator, along with its lhs and rhs expressions. """ def __init__(self, lhs: Expression, rhs: Expression): """ Create a new `LessThanExpr` with the given `lhs` and `rhs`. Args: lhs: An `Expression` to use for the left-hand side. rhs: An `Expression` to use for the right-hand side. """ self.lhs = lhs self.rhs = rhs def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `LessThanExpr` into its possible operator expressions. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete less-than boolean expression """ lhs_exprs = self.lhs.concretize(db, location) rhs_exprs = self.rhs.concretize(db, location) for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs): yield f"{lhs} < {rhs}" def view(self, db: models.DB, location: Location): self.lhs.view(db, location) + " < " + self.rhs.view(db, location)Ancestors
- Expression
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
LessThanExprinto its possible operator expressions.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a concrete less-than boolean expression
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `LessThanExpr` into its possible operator expressions. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete less-than boolean expression """ lhs_exprs = self.lhs.concretize(db, location) rhs_exprs = self.rhs.concretize(db, location) for (lhs, rhs) in itertools.product(lhs_exprs, rhs_exprs): yield f"{lhs} < {rhs}" def view(self, db: DB, location: Location)-
Expand source code
def view(self, db: models.DB, location: Location): self.lhs.view(db, location) + " < " + self.rhs.view(db, location)
class Lit (expr: str)-
Represents a literal source string.
Template authors can use this to insert pre-formed source code, or code that doesn't need to be concretized by location.
Create a new
Litwith the given source string.Args
expr- The source literal
Expand source code
class Lit(Expression): """ Represents a literal source string. Template authors can use this to insert pre-formed source code, or code that doesn't need to be concretized by location. """ def __init__(self, expr: str): """ Create a new `Lit` with the given source string. Args: expr: The source literal """ self.expr = expr def concretize(self, _db, _location) -> Iterator[str]: """ Concretize this literal into its underlying source string. """ yield self.expr def view(self, _db, _location): return f"Lit({self.expr})"Ancestors
- Expression
- abc.ABC
Methods
def concretize(self, _db, _location) ‑> Iterator[str]-
Concretize this literal into its underlying source string.
Expand source code
def concretize(self, _db, _location) -> Iterator[str]: """ Concretize this literal into its underlying source string. """ yield self.expr def view(self, _db, _location)-
Expand source code
def view(self, _db, _location): return f"Lit({self.expr})"
class NodeStmt-
Represents a statement from the AST database, identified by location.
Expand source code
class NodeStmt(Statement): """ Represents a statement from the AST database, identified by location. """ def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `NodeStmt` into its concrete statement. Args: db: The AST database to concretize against location: The statement's location Returns: A generator of strings, whose single item is the extracted AST statement """ source_info = self.fetch_node(db, location) yield source_info def view(self, db, location) -> str: # Fetch and create a node. source_info = self.fetch_node(db, location) return source_info def fetch_node(self, db, location) -> str: """ Fetches the statement at the given location. Args: db: The AST database to query against. location: The location of the statement. Returns: The statement, including trailing semicolon. Raises: PatchConcretizationError: If there is no statement at the supplied location. """ statement = db.statement_at(location) if statement is None: raise PatchConcretizationError(f"no statement at ({location.line}, {location.column})") if statement.expr.endswith(";"): return statement.expr else: return f"{statement.expr};"Ancestors
- Statement
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
NodeStmtinto its concrete statement.Args
db- The AST database to concretize against
location- The statement's location
Returns
A generator of strings, whose single item is the extracted AST statement
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `NodeStmt` into its concrete statement. Args: db: The AST database to concretize against location: The statement's location Returns: A generator of strings, whose single item is the extracted AST statement """ source_info = self.fetch_node(db, location) yield source_info def fetch_node(self, db, location) ‑> str-
Fetches the statement at the given location.
Args
db- The AST database to query against.
location- The location of the statement.
Returns
The statement, including trailing semicolon.
Raises
PatchConcretizationError- If there is no statement at the supplied location.
Expand source code
def fetch_node(self, db, location) -> str: """ Fetches the statement at the given location. Args: db: The AST database to query against. location: The location of the statement. Returns: The statement, including trailing semicolon. Raises: PatchConcretizationError: If there is no statement at the supplied location. """ statement = db.statement_at(location) if statement is None: raise PatchConcretizationError(f"no statement at ({location.line}, {location.column})") if statement.expr.endswith(";"): return statement.expr else: return f"{statement.expr};" def view(self, db, location) ‑> str-
Expand source code
def view(self, db, location) -> str: # Fetch and create a node. source_info = self.fetch_node(db, location) return source_info
class PatchTemplate (fix_pattern: FixPattern, matcher_func: Optional[Callable[[int, int], bool]] = None)-
Represents a templatized patch, including a matcher callable.
Create a new
PatchTemplate.Args
fix_pattern- The fix pattern to concretize into patches
matcher_func- The callable to filter patch locations with, if any
Expand source code
class PatchTemplate: """ Represents a templatized patch, including a matcher callable. """ def __init__( self, fix_pattern: FixPattern, matcher_func: Optional[Callable[[int, int], bool]] = None ): """ Create a new `PatchTemplate`. Args: fix_pattern: The fix pattern to concretize into patches matcher_func: The callable to filter patch locations with, if any """ self.matcher_func = matcher_func self.fix_pattern = fix_pattern def matches(self, line: int, col: int) -> bool: """ Returns whether the given line and column is suitable for patch situation. Calls `self.matcher_func` internally, if supplied. Args: line: The line to patch on column: The column to patch on Returns: `True` if `matcher_func` was not supplied or returns `True`, `False` otherwise """ if self.matcher_func is None: return True return self.matcher_func(line, col) def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize the inner `FixPattern` into a sequence of patch candidates. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a candidate patch """ yield from self.fix_pattern.concretize(db, location) def view(self, db: models.DB, location: Location) -> str: return self.fix_pattern.view(db, location)Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize the inner
FixPatterninto a sequence of patch candidates.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a candidate patch
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize the inner `FixPattern` into a sequence of patch candidates. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a candidate patch """ yield from self.fix_pattern.concretize(db, location) def matches(self, line: int, col: int) ‑> bool-
Returns whether the given line and column is suitable for patch situation.
Calls
self.matcher_funcinternally, if supplied.Args
line- The line to patch on
column- The column to patch on
Returns
Trueifmatcher_funcwas not supplied or returnsTrue,FalseotherwiseExpand source code
def matches(self, line: int, col: int) -> bool: """ Returns whether the given line and column is suitable for patch situation. Calls `self.matcher_func` internally, if supplied. Args: line: The line to patch on column: The column to patch on Returns: `True` if `matcher_func` was not supplied or returns `True`, `False` otherwise """ if self.matcher_func is None: return True return self.matcher_func(line, col) def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: return self.fix_pattern.view(db, location)
class ReturnStmt (expr: Expression)-
Represents a
return ...;statement.Creates a new
ReturnStmt.Args
expr- The expression to concretize for the
return
Expand source code
class ReturnStmt(Statement): """ Represents a `return ...;` statement. """ def __init__(self, expr: Expression): """ Creates a new `ReturnStmt`. Args: expr: The expression to concretize for the `return` """ self.expr = expr def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `ReturnStmt` into its possible statements. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is an `return` statement """ expr_list = self.expr.concretize(db, location) for exp in expr_list: candidate_str = f"return {exp};" yield candidate_str def view(self, db: models.DB, location: Location): return f"return {self.expr.view(db, location)};"Ancestors
- Statement
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
ReturnStmtinto its possible statements.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is an
returnstatementExpand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `ReturnStmt` into its possible statements. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is an `return` statement """ expr_list = self.expr.concretize(db, location) for exp in expr_list: candidate_str = f"return {exp};" yield candidate_str def view(self, db: DB, location: Location)-
Expand source code
def view(self, db: models.DB, location: Location): return f"return {self.expr.view(db, location)};"
class Statement-
Represents an source "statement".
Statementis an abstract base whose interfaces should be consumed via specific derived classes.Expand source code
class Statement(ABC): """ Represents an source "statement". `Statement` is an abstract base whose interfaces should be consumed via specific derived classes. """ @abstractmethod def concretize(self, db: models.DB, location: Location) -> Iterator[str]: pass @abstractmethod def view(self, db: models.DB, location: Location): passAncestors
- abc.ABC
Subclasses
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Expand source code
@abstractmethod def concretize(self, db: models.DB, location: Location) -> Iterator[str]: pass def view(self, db: DB, location: Location)-
Expand source code
@abstractmethod def view(self, db: models.DB, location: Location): pass
class StatementList (*args)-
Represents a sequence of statements.
Expand source code
class StatementList: """ Represents a sequence of statements. """ def __init__(self, *args): self.statements: List[Statement] = args def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `StatementList` into its possible statement seqences. This involves concretizing each statement and taking their unique product, such that every possible permutation of statements in the list is produced. Args: db: The AST database to concretize against locaton: The location to concretize at Returns: A generator of strings, each of which is a concreqte sequence of statements """ concretized = [stmt.concretize(db, location) for stmt in self.statements] for items in set(itertools.product(*concretized)): yield "\n".join(items) def view(self, db: models.DB, location: Location) -> str: final_str = "" for stmt in self.statements: final_str += stmt.view(db, location) + "\n" return final_strMethods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
StatementListinto its possible statement seqences.This involves concretizing each statement and taking their unique product, such that every possible permutation of statements in the list is produced.
Args
db- The AST database to concretize against
locaton- The location to concretize at
Returns
A generator of strings, each of which is a concreqte sequence of statements
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `StatementList` into its possible statement seqences. This involves concretizing each statement and taking their unique product, such that every possible permutation of statements in the list is produced. Args: db: The AST database to concretize against locaton: The location to concretize at Returns: A generator of strings, each of which is a concreqte sequence of statements """ concretized = [stmt.concretize(db, location) for stmt in self.statements] for items in set(itertools.product(*concretized)): yield "\n".join(items) def view(self, db: DB, location: Location) ‑> str-
Expand source code
def view(self, db: models.DB, location: Location) -> str: final_str = "" for stmt in self.statements: final_str += stmt.view(db, location) + "\n" return final_str
class StaticBufferSize-
Represents an abstract "sizeof(…)" expression.
Expand source code
class StaticBufferSize(Expression): """ Represents an abstract "sizeof(...)" expression. """ # Query for all static array types and return list of sizeof().. def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `StaticBufferSize` into its potential sizes. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete `sizeof(...)` expression Raises: PatchConcretizationError: If the scope of the location is not a function """ # TODO(ww): We should also concretize with available globals. function = db.function_at(location) if function is None: raise PatchConcretizationError( f"no function contains ({location.line}, {location.column})" ) for var_decl in function.var_decls: if not var_decl.is_array: continue yield f"sizeof({var_decl.name})" def view(self, _db, _location) -> str: return "StaticBufferSize()"Ancestors
- Expression
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
StaticBufferSizeinto its potential sizes.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a concrete
sizeof(…)expressionRaises
PatchConcretizationError- If the scope of the location is not a function
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `StaticBufferSize` into its potential sizes. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete `sizeof(...)` expression Raises: PatchConcretizationError: If the scope of the location is not a function """ # TODO(ww): We should also concretize with available globals. function = db.function_at(location) if function is None: raise PatchConcretizationError( f"no function contains ({location.line}, {location.column})" ) for var_decl in function.var_decls: if not var_decl.is_array: continue yield f"sizeof({var_decl.name})" def view(self, _db, _location) ‑> str-
Expand source code
def view(self, _db, _location) -> str: return "StaticBufferSize()"
class Variable-
Represents an abstract source variable.
Expand source code
class Variable(Expression): """ Represents an abstract source variable. """ # NOTE(ww): This is probably slightly unsound, in terms of concretizations # produced: the set of variables in a function is not the set of variables # guaranteed to be in scope at a line number. def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `Variable` into its potential names. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete variable name Raises: PatchConcretizationError: If the scope of the variable can't be resolved """ # TODO(ww): We should also concretize with the available globals. function = db.function_at(location) if function is None: raise PatchConcretizationError( f"no function contains ({location.line}, {location.column})" ) for var_decl in function.var_decls: yield var_decl.name def view(self, _db, _location) -> str: return "Variable()"Ancestors
- Expression
- abc.ABC
Methods
def concretize(self, db: DB, location: Location) ‑> Iterator[str]-
Concretize this
Variableinto its potential names.Args
db- The AST database to concretize against
location- The location to concretize at
Returns
A generator of strings, each of which is a concrete variable name
Raises
PatchConcretizationError- If the scope of the variable can't be resolved
Expand source code
def concretize(self, db: models.DB, location: Location) -> Iterator[str]: """ Concretize this `Variable` into its potential names. Args: db: The AST database to concretize against location: The location to concretize at Returns: A generator of strings, each of which is a concrete variable name Raises: PatchConcretizationError: If the scope of the variable can't be resolved """ # TODO(ww): We should also concretize with the available globals. function = db.function_at(location) if function is None: raise PatchConcretizationError( f"no function contains ({location.line}, {location.column})" ) for var_decl in function.var_decls: yield var_decl.name def view(self, _db, _location) ‑> str-
Expand source code
def view(self, _db, _location) -> str: return "Variable()"