-
Notifications
You must be signed in to change notification settings - Fork 488
Slottify expressions #1729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Slottify expressions #1729
Changes from 108 commits
1d24ef9
d76d8b1
f4ba546
5c187bd
3467886
f24b7eb
0cd1bc5
609a37e
b29f3ac
3bbb483
154326c
f4eac7f
4192b6e
7885c84
df96058
cc24e0d
1146104
51a67a2
79aa5cc
7339eb9
c121c38
b40de82
b5d6b9c
198e0d8
d82b398
487f2bb
ec250a0
f922e6f
3476a11
866bc0b
3ff6b95
02b4c69
00e8199
6b7f671
6732eb2
d54ba40
4adeac2
7565b4a
1680397
cbe55b9
315ef64
6155a4d
38dbfd6
1491cbd
264eb04
e0130a4
d0e9abf
9807b37
728e021
e584fb2
bf3a2b9
3eb96f8
5e54189
a0ab429
536ec71
a83e8c2
37144d9
1b8754d
787e51f
dca9adb
dfa7a84
b841038
735a963
b54a448
02563cd
3fab981
f7fb8bb
2f5d4d2
18064f8
52d8eb4
f1dfb80
d5b577e
79b42ed
ec8489f
2113a73
c2a21ba
f87e1e2
2eb630d
8818ea3
27db1b8
e5cebb1
e717bf3
937d988
85263a8
6e161d1
685beed
df4d457
9896b72
5690dc5
0e58c3e
21be706
5ac21a4
19f4d43
cbe2174
354cec3
94232b9
570b1e4
7168829
d669e41
cce9dd4
d5cffe6
37446fc
bc81838
f163497
c232d58
9200178
ef844bd
79c0b66
f17bd7f
b7a5bc0
e57975e
c0ea781
a102dae
87a6887
9123dcf
0595ce0
d45e01b
2aa5b70
188054c
ba5499b
2fe9a06
8d33428
ed11626
5ffe40a
6a9f392
1cb97be
c220799
5eea790
f44d570
2fa2561
2605e46
487e5f4
4323bef
d46eea1
6ff1e87
8e0797d
e0e6584
4843f90
d27068c
49d82da
2c6cffa
38a261c
a81f8e8
6610f0c
dda6d6a
58136f0
f2055be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,26 +6,19 @@ | |
| from ...exceptions import SmtlibError | ||
| from .expression import ( | ||
| Expression, | ||
| BitVecVariable, | ||
| BitvecVariable, | ||
| BoolVariable, | ||
| ArrayVariable, | ||
| Array, | ||
| Bool, | ||
| BitVec, | ||
| Bitvec, | ||
| BoolConstant, | ||
| ArrayProxy, | ||
| MutableArray, | ||
| BoolEqual, | ||
| Variable, | ||
| Constant, | ||
| ) | ||
| from .visitors import ( | ||
| GetDeclarations, | ||
| TranslatorSmtlib, | ||
| get_variables, | ||
| simplify, | ||
| replace, | ||
| pretty_print, | ||
| ) | ||
| from .visitors import GetDeclarations, TranslatorSmtlib, get_variables, simplify, replace | ||
| from ...utils import config | ||
| import logging | ||
|
|
||
|
|
@@ -40,11 +33,15 @@ class ConstraintException(SmtlibError): | |
| pass | ||
|
|
||
|
|
||
| class Model: | ||
| pass | ||
|
|
||
|
|
||
| class ConstraintSet: | ||
| """ Constraint Sets | ||
| """Constraint Sets | ||
|
|
||
| An object containing a set of constraints. Serves also as a factory for | ||
| new variables. | ||
| An object containing a set of constraints. Serves also as a factory for | ||
| new variables. | ||
| """ | ||
|
|
||
| def __init__(self): | ||
|
|
@@ -178,58 +175,33 @@ def related_to(self, *related_to) -> "ConstraintSet": | |
|
|
||
| def to_string(self, replace_constants: bool = False) -> str: | ||
| variables, constraints = self.get_declared_variables(), self.constraints | ||
|
|
||
| if replace_constants: | ||
| constant_bindings = {} | ||
| for expression in constraints: | ||
| if ( | ||
| isinstance(expression, BoolEqual) | ||
| and isinstance(expression.operands[0], Variable) | ||
| and isinstance(expression.operands[1], (*Variable, *Constant)) | ||
| and not isinstance(expression.operands[1], (Variable, Constant)) | ||
feliam marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ): | ||
| constant_bindings[expression.operands[0]] = expression.operands[1] | ||
|
|
||
| tmp = set() | ||
| result = "" | ||
feliam marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for var in variables: | ||
| # FIXME | ||
| # band aid hack around the fact that we are double declaring stuff :( :( | ||
| if var.declaration in tmp: | ||
| logger.warning("Variable '%s' was copied twice somewhere", var.name) | ||
| continue | ||
| tmp.add(var.declaration) | ||
| result += var.declaration + "\n" | ||
|
|
||
| translator = TranslatorSmtlib(use_bindings=True) | ||
| translator = TranslatorSmtlib(use_bindings=False) | ||
| tuple(translator.visit_Variable(v) for v in variables) | ||
|
||
| for constraint in constraints: | ||
| if replace_constants: | ||
| constraint = simplify(replace(constraint, constant_bindings)) | ||
| # if no variables then it is a constant | ||
| if isinstance(constraint, Constant) and constraint.value == True: | ||
| continue | ||
|
|
||
| # Translate one constraint | ||
| translator.visit(constraint) | ||
|
|
||
| if replace_constants: | ||
| for k, v in constant_bindings.items(): | ||
| translator.visit(k == v) | ||
|
|
||
| for name, exp, smtlib in translator.bindings: | ||
| if isinstance(exp, BitVec): | ||
| result += f"(declare-fun {name} () (_ BitVec {exp.size}))" | ||
| elif isinstance(exp, Bool): | ||
| result += f"(declare-fun {name} () Bool)" | ||
| elif isinstance(exp, Array): | ||
| result += f"(declare-fun {name} () (Array (_ BitVec {exp.index_bits}) (_ BitVec {exp.value_bits})))" | ||
| else: | ||
| raise ConstraintException(f"Type not supported {exp!r}") | ||
| result += f"(assert (= {name} {smtlib}))\n" | ||
|
|
||
| constraint_str = translator.pop() | ||
| while constraint_str is not None: | ||
| if constraint_str != "true": | ||
| result += f"(assert {constraint_str})\n" | ||
| constraint_str = translator.pop() | ||
| return result | ||
| return translator.smtlib() | ||
|
|
||
| def _declare(self, var): | ||
| """ Declare the variable `var` """ | ||
|
|
@@ -238,6 +210,10 @@ def _declare(self, var): | |
| self._declarations[var.name] = var | ||
| return var | ||
|
|
||
| @property | ||
| def variables(self): | ||
| return self._declarations.values() | ||
|
|
||
| def get_declared_variables(self): | ||
| """ Returns the variable expressions of this constraint set """ | ||
| return self._declarations.values() | ||
|
|
@@ -298,18 +274,18 @@ def is_declared(self, expression_var) -> bool: | |
| return any(expression_var is x for x in self.get_declared_variables()) | ||
|
|
||
| def migrate(self, expression, name_migration_map=None): | ||
| """ Migrate an expression created for a different constraint set to self. | ||
| Returns an expression that can be used with this constraintSet | ||
| """Migrate an expression created for a different constraint set to self. | ||
| Returns an expression that can be used with this constraintSet | ||
|
|
||
| All the foreign variables used in the expression are replaced by | ||
| variables of this constraint set. If the variable was replaced before | ||
| the replacement is taken from the provided migration map. | ||
| All the foreign variables used in the expression are replaced by | ||
| variables of this constraint set. If the variable was replaced before | ||
| the replacement is taken from the provided migration map. | ||
|
|
||
| The migration mapping is updated with new replacements. | ||
| The migration mapping is updated with new replacements. | ||
|
|
||
| :param expression: the potentially foreign expression | ||
| :param name_migration_map: mapping of already migrated variables. maps from string name of foreign variable to its currently existing migrated string name. this is updated during this migration. | ||
| :return: a migrated expression where all the variables are local. name_migration_map is updated | ||
| :param expression: the potentially foreign expression | ||
| :param name_migration_map: mapping of already migrated variables. maps from string name of foreign variable to its currently existing migrated string name. this is updated during this migration. | ||
| :return: a migrated expression where all the variables are local. name_migration_map is updated | ||
|
|
||
| """ | ||
| if name_migration_map is None: | ||
|
|
@@ -343,16 +319,16 @@ def migrate(self, expression, name_migration_map=None): | |
| # Create and declare a new variable of given type | ||
| if isinstance(foreign_var, Bool): | ||
| new_var = self.new_bool(name=migrated_name) | ||
| elif isinstance(foreign_var, BitVec): | ||
| elif isinstance(foreign_var, Bitvec): | ||
| new_var = self.new_bitvec(foreign_var.size, name=migrated_name) | ||
| elif isinstance(foreign_var, Array): | ||
| # Note that we are discarding the ArrayProxy encapsulation | ||
| new_var = self.new_array( | ||
| index_max=foreign_var.index_max, | ||
| index_bits=foreign_var.index_bits, | ||
| value_bits=foreign_var.value_bits, | ||
| length=foreign_var.length, | ||
| index_size=foreign_var.index_size, | ||
| value_size=foreign_var.value_size, | ||
| name=migrated_name, | ||
| ).array | ||
| ) | ||
| else: | ||
| raise NotImplementedError( | ||
| f"Unknown expression type {type(foreign_var)} encountered during expression migration" | ||
|
|
@@ -367,11 +343,11 @@ def migrate(self, expression, name_migration_map=None): | |
| return migrated_expression | ||
|
|
||
| def new_bool(self, name=None, taint=frozenset(), avoid_collisions=False): | ||
| """ Declares a free symbolic boolean in the constraint store | ||
| :param name: try to assign name to internal variable representation, | ||
| if not unique, a numeric nonce will be appended | ||
| :param avoid_collisions: potentially avoid_collisions the variable to avoid name collisions if True | ||
| :return: a fresh BoolVariable | ||
| """Declares a free symbolic boolean in the constraint store | ||
| :param name: try to assign name to internal variable representation, | ||
| if not unique, a numeric nonce will be appended | ||
| :param avoid_collisions: potentially avoid_collisions the variable to avoid name collisions if True | ||
| :return: a fresh BoolVariable | ||
| """ | ||
| if name is None: | ||
| name = "B" | ||
|
|
@@ -380,16 +356,16 @@ def new_bool(self, name=None, taint=frozenset(), avoid_collisions=False): | |
| name = self._make_unique_name(name) | ||
| if not avoid_collisions and name in self._declarations: | ||
| raise ValueError(f"Name {name} already used") | ||
| var = BoolVariable(name, taint=taint) | ||
| var = BoolVariable(name=name, taint=taint) | ||
| return self._declare(var) | ||
|
|
||
| def new_bitvec(self, size, name=None, taint=frozenset(), avoid_collisions=False): | ||
| """ Declares a free symbolic bitvector in the constraint store | ||
| :param size: size in bits for the bitvector | ||
| :param name: try to assign name to internal variable representation, | ||
| if not unique, a numeric nonce will be appended | ||
| :param avoid_collisions: potentially avoid_collisions the variable to avoid name collisions if True | ||
| :return: a fresh BitVecVariable | ||
| """Declares a free symbolic bitvector in the constraint store | ||
| :param size: size in bits for the bitvector | ||
| :param name: try to assign name to internal variable representation, | ||
| if not unique, a numeric nonce will be appended | ||
| :param avoid_collisions: potentially avoid_collisions the variable to avoid name collisions if True | ||
| :return: a fresh BitvecVariable | ||
| """ | ||
| if size <= 0: | ||
| raise ValueError(f"Bitvec size ({size}) can't be equal to or less than 0") | ||
|
|
@@ -400,28 +376,28 @@ def new_bitvec(self, size, name=None, taint=frozenset(), avoid_collisions=False) | |
| name = self._make_unique_name(name) | ||
| if not avoid_collisions and name in self._declarations: | ||
| raise ValueError(f"Name {name} already used") | ||
| var = BitVecVariable(size, name, taint=taint) | ||
| var = BitvecVariable(size=size, name=name, taint=taint) | ||
| return self._declare(var) | ||
|
|
||
| def new_array( | ||
| self, | ||
| index_bits=32, | ||
| index_size=32, | ||
| name=None, | ||
| index_max=None, | ||
| value_bits=8, | ||
| length=None, | ||
| value_size=8, | ||
| taint=frozenset(), | ||
| avoid_collisions=False, | ||
| default=None, | ||
| ): | ||
| """ Declares a free symbolic array of value_bits long bitvectors in the constraint store. | ||
| :param index_bits: size in bits for the array indexes one of [32, 64] | ||
| :param value_bits: size in bits for the array values | ||
| :param name: try to assign name to internal variable representation, | ||
| if not unique, a numeric nonce will be appended | ||
| :param index_max: upper limit for indexes on this array (#FIXME) | ||
| :param avoid_collisions: potentially avoid_collisions the variable to avoid name collisions if True | ||
| :param default: default for not initialized values | ||
| :return: a fresh ArrayProxy | ||
| """Declares a free symbolic array of value_size long bitvectors in the constraint store. | ||
| :param index_size: size in bits for the array indexes one of [32, 64] | ||
| :param value_size: size in bits for the array values | ||
| :param name: try to assign name to internal variable representation, | ||
| if not unique, a numeric nonce will be appended | ||
| :param length: upper limit for indexes on this array (#FIXME) | ||
| :param avoid_collisions: potentially avoid_collisions the variable to avoid name collisions if True | ||
| :param default: default for not initialized values | ||
| :return: a fresh ArrayProxy | ||
| """ | ||
| if name is None: | ||
| name = "A" | ||
|
|
@@ -430,5 +406,14 @@ def new_array( | |
| name = self._make_unique_name(name) | ||
| if not avoid_collisions and name in self._declarations: | ||
| raise ValueError(f"Name {name} already used") | ||
| var = self._declare(ArrayVariable(index_bits, index_max, value_bits, name, taint=taint)) | ||
| return ArrayProxy(var, default=default) | ||
| var = self._declare( | ||
| ArrayVariable( | ||
| index_size=index_size, | ||
| length=length, | ||
| value_size=value_size, | ||
| name=name, | ||
| taint=taint, | ||
| default=default, | ||
| ) | ||
| ) | ||
| return var | ||
Uh oh!
There was an error while loading. Please reload this page.