Exemple #1
0
    def _attach_datatypes(self):
        self.Node, self.node_values = z3.EnumSort("Node", self.node_names)

        self.nodes = dict(zip(self.node_names, self.node_values))

        # A datatype for storing a pair of edges
        self.Edge = z3.Datatype('Edge')
        self.Edge.declare('edge', ('node1', self.Node), ('node2', self.Node))
        self.Edge = self.Edge.create()

        self.Nodeset = z3.ArraySort(self.Node, z3.BoolSort())
        self.Edgeset = z3.ArraySort(self.Edge, z3.BoolSort())

        self.Labelset = z3.ArraySort(z3.IntSort(), z3.BoolSort())
        self.Labelmap = z3.ArraySort(self.Node, self.Labelset)

        # Graph, before a rule or action has applied. Merged Pregraph and Postgraph
        # into a single datatype.
        self.Graph = z3.Datatype('Graph')
        self.Graph.declare('graph', ('has', self.Nodeset),
                           ('links', self.Edgeset), ('parents', self.Edgeset),
                           ('labelmap', self.Labelmap))
        self.Graph = self.Graph.create()

        # This represents a set of possible <pregraph, postgraph> pairs.
        self.Rule = z3.Datatype('Rule')
        self.Rule.declare('rule', ('pregraph', self.Graph),
                          ('postgraph', self.Graph))
        self.Rule = self.Rule.create()
Exemple #2
0
    def z3_sort(self, typeexpr):
        ## Check for a base type before reduction, for types
        ## like gmap that we want to avoid unfolding in reduce.
        base_lam = self.base_types.get(typeexpr['name'])
        if base_lam is not None:
            return base_lam(typeexpr['args'])

        typeexpr = self.context.reduce(typeexpr)

        ## Check again for a base type, for types like nat, where
        ## we may have had an alias (like uint32) before.
        base_lam = self.base_types.get(typeexpr['name'])
        if base_lam is not None:
            return base_lam(typeexpr['args'])

        z3name = typeexpr.get('name_with_args', typeexpr['name'])
        datatype = z3.Datatype(str(z3name))
        for c in typeexpr['constructors']:
            cname = str(c['name'])
            datatype.declare(
                cname, *[(cname + '_%d' % idx, self.z3_sort(argtype))
                         for (idx, argtype) in enumerate(c['argtypes'])])

        t = datatype.create()
        return t
Exemple #3
0
    def initialize(self, ctx):
        # clear the flat names
        self.flat_names = []
        flat_args = []
        idx = 0
        for z3_arg_name, z3_arg_type in self.members:
            z3_arg_type = ctx.resolve_type(z3_arg_type)
            if isinstance(z3_arg_type, P4ComplexType):
                member_cls = z3_arg_type.instantiate(f"{self.name}.{idx}")
                propagate_validity_bit(member_cls)
                for sub_member in z3_arg_type.flat_names:
                    flat_args.append((str(idx), sub_member.p4_type))
                    self.flat_names.append(
                        P4Member(z3_arg_name, sub_member.name))
                    idx += 1
                # this is a complex datatype, create a P4ComplexType
                ctx.set_or_add_var(z3_arg_name, member_cls, True)
            else:
                flat_args.append((str(idx), z3_arg_type))
                self.flat_names.append(z3_arg_name)
                idx += 1
        z3_type = z3.Datatype(self.name)
        z3_type.declare(f"mk_{self.name}", *flat_args)
        self.z3_type = z3_type.create()
        self.const = z3.Const(self.name, self.z3_type)

        for type_idx, arg_name in enumerate(self.flat_names):
            member_constructor = self.z3_type.accessor(0, type_idx)
            ctx.set_or_add_var(arg_name, member_constructor(self.const), True)
Exemple #4
0
def solve(grid):
    """

    . . .
    .1.1.
    . .3.

    """
    s = z3.Solver()

    # Construct atoms to represent the dots
    Dot = z3.Datatype("Dot")
    for d in grid.dots():
        Dot.declare("dot_{}".format(d))
    Dot = Dot.create()

    dot_atom = {d: getattr(Dot, "dot_{}".format(d)) for d in grid.dots()}

    # Booleans for each of the points
    dot = {d: z3.Bool("dot-{}".format(d)) for d in grid.dots()}

    # Booleans for each of the connectors
    line = {l: z3.Bool("line-{}".format(l)) for l in grid.lines()}

    # For each point: if it is on, it must have precisely two adjoining lines activated
    # If it's off, then there are zero adjoining lines activated
    bool_to_int = z3.Function("bool_to_int", z3.BoolSort(), z3.IntSort())
    s.add(bool_to_int(True) == 1)
    s.add(bool_to_int(True) != 0)
    s.add(bool_to_int(False) == 0)
    s.add(bool_to_int(False) != 1)

    for d in grid.dots():
        # Get all lines coming out of this dot
        ls = [line[l] for l in d.lines()]

        sm = z3.Sum(*(bool_to_int(l) for l in ls))
        s.add(z3.Implies(dot[d], sm == 2))
        s.add(z3.Implies(z3.Not(dot[d]), sm == 0))

    # For each line: if it is activated, then the points at both ends are activated
    for l in line:
        d1, d2 = l.ends()
        s.add(z3.Implies(line[l], z3.And(dot[d1], dot[d2])))

    # "Is connected to" relationship
    connected = z3.Function("connected", Dot, Dot, z3.BoolSort())

    # For each line:
    # The Dot at each end is connected to the other iff the line is activated
    for d1 in grid.dots():
        for d2 in grid.dots():
            a = dot_atom[d1]
            b = dot_atom[d2]
            if (l := grid.line(d1, d2)) is None:
                # These dots are never connected
                s.add(connected(a, b) != True)
            else:
                s.add(z3.Implies(line[l], connected(a, b)))
                s.add(z3.Implies(z3.Not(line[l]), z3.Not(connected(a, b))))
Exemple #5
0
    def test_model_is_refreshed(self):
        """
        After a push and a pop, the model should be updated.
        After adding a new assertion, the model should be updated.
        """
        TestVariable = z3.Datatype('TestVariable')
        TestVariable.declare('test_variable', ('number', z3.IntSort()))
        TestVariable = TestVariable.create()

        v1 = z3.Const('v1', TestVariable)
        v2 = z3.Const('v2', TestVariable)

        with solver.context():
            solver.add(TestVariable.number(v1) == 42)
            m1 = solver.model()
            self.assertIsNotNone(m1[v1])
            self.assertIsNone(m1[v2])

            solver.add(TestVariable.number(v2) == 3)
            m2 = solver.model()
            self.assertIsNotNone(m2[v1])
            self.assertIsNotNone(m2[v2])

        m3 = solver.model()
        self.assertIsNone(m3[v1])
        self.assertIsNone(m3[v2])
Exemple #6
0
 def __init__(self, name, type_params, methods):
     self.name = name
     z3_type = z3.Datatype(name)
     z3_type.declare(f"mk_{name}")
     self.z3_type = z3_type.create()
     self.type_params = type_params
     self.type_ctx = {}
     # attach the methods
     self.locals = {}
     for method in methods:
         self.locals.setdefault(method.lval, []).append(method.rval)
Exemple #7
0
    def _mkTypes(self, nodes, addresses):
        # Nodes in a network
        self.node, self.node_list = z3.EnumSort('Node', nodes)
        self.node_list = map(DumbNode, self.node_list)
        nodes = zip(nodes, self.node_list)
        for ndn, ndv in nodes:
            setattr(self, ndn, ndv)

        # Addresses for this network
        self.address, self.address_list = z3.EnumSort('Address', addresses)
        addresses = zip(addresses, self.address_list)
        for adn, adv in addresses:
            setattr(self, adn, adv)

        # Type for packets, contains (some of these are currently represented as relations):
        # -   src: Source address
        # -   dest: Destination address
        # -   origin: Node where the data originated. (Node)
        # -   body: Packet contents. (Integer)
        # -   seq: Sequence number for packets. (Integer)
        # -   options: A representation for IP options. (Integer)

        # TODO: Some of these are out, some of these are in.
        packet = z3.Datatype('Packet')
        packet.declare('packet', \
                       ('src', self.address), \
                       ('dest', self.address), \
                       ('origin', self.node), \
                       ('orig_body', z3.IntSort()), \
                       ('body', z3.IntSort()), \
                       ('seq', z3.IntSort()), \
                       ('options', z3.IntSort()))
        self.packet = packet.create()
        # $$src\_port: packet \rightarrow \mathbb{Z}^{+}$$
        self.src_port = z3.Function('sport', self.packet, z3.IntSort())
        # $$dest\_port: packet \rightarrow \mathbb{Z}^{+}$$
        self.dest_port = z3.Function('dport', self.packet, z3.IntSort())

        # Some commonly used relations
        # $$nodeHasAddr: node \rightarrow address \rightarrow boolean$$
        self.nodeHasAddr = z3.Function('nodeHasAddr', self.node, \
                                            self.address, z3.BoolSort ())
        # $$addrToNode: address \rightarrow node$$
        self.addrToNode = z3.Function('addrToNode', self.address, self.node)

        # Send and receive both have the form:
        # $$ source\rightarrow destination\rightarrow packet\rightarrow int\rightarrow bool$$
        # $$send: node \rightarrow node \rightarrow packet\rightarrow int\rightarrow bool$$
        self.send = z3.Function('send', self.node, self.node, self.packet,
                                z3.IntSort(), z3.BoolSort())
        # $$recv: node \rightarrow node \rightarrow packet\rightarrow int\rightarrow bool$$
        self.recv = z3.Function('recv', self.node, self.node, self.packet,
                                z3.IntSort(), z3.BoolSort())
Exemple #8
0
def init_default_data():
    Data = z3.Datatype('Data')

    Data.declare('int', ('data', z3.IntSort()))
    Data.declare('others', ('data', Data))
    Data.declare('tuple', ('left', Data), ('right', Data))
    Data.declare('mset', ('body', Data), ('elem', Data))
    Data.declare('list', ('head', Data), ('tail', Data))
    Data.declare('unit')

    Data = Data.create()
    global smt_data
    smt_data = Data
Exemple #9
0
    def _z3_sort(self, json_type, typeargs=[], typeargnames={}):
        # print "Looking up type %s (%s, %s): %s" % (json_type['name'], typeargs, typeargnames, json_type)

        ## Hard-coded Z3 correspondence for some types
        if json_type['name'] == 'nat':
            return z3.IntSort()

        if json_type['name'] == 'string':
            return z3.StringSort()

        if json_type['name'] == 'gmap':
            return z3.ArraySort(self._z3_sort(json_type['args'][0]),
                                self._z3_sort(json_type['args'][1]))

        if json_type['what'] == 'type:var':
            t = typeargnames[json_type['name']]
            return self._z3_sort(t)

        if json_type['what'] == 'type:glob':
            if json_type['name'] == 'buf':
                return z3.SeqSort(z3.BitVecSort(8))

            return self.z3_sort(json_type['name'], [
                self._type_var_lookup(typeargnames, x)
                for x in json_type['args']
            ])

        if str(json_type) in self.json_to_sort:
            return self.json_to_sort[str(json_type)]

        if json_type['name'] == 'list':
            return z3.SeqSort(self._z3_sort(typeargs[0]))

        datatype = z3.Datatype(str(json_type['name']))
        typeargnames = {}
        for i in range(0, len(typeargs)):
            typeargname = json_type['argnames'][i]
            typeargnames[typeargname] = typeargs[i]
        for c in json_type['constructors']:
            cname = str(c['name'])
            datatype.declare(
                cname,
                *[(cname + '_%d' % idx, self._z3_sort(argtype, [],
                                                      typeargnames))
                  for (idx, argtype) in enumerate(c['argtypes'])])

        t = datatype.create()
        self.json_to_sort[str(json_type)] = t
        return t
Exemple #10
0
 def __init__(self, name, z3_args):
     self.name = name
     self.ref_count = 0
     z3_type = z3.Datatype(name)
     stripped_args = []
     for z3_arg in z3_args:
         z3_arg_name = z3_arg[0]
         z3_arg_type = z3_arg[1]
         if isinstance(z3_arg_type, P4ComplexType):
             stripped_args.append((z3_arg_name, z3_arg_type.z3_type))
         else:
             stripped_args.append(z3_arg)
     z3_type.declare(f"mk_{name}", *stripped_args)
     self.z3_type = z3_type.create()
     self.z3_args = z3_args
Exemple #11
0
 def __init__(self, name, type_params=[], methods=[]):
     # Externs are this weird bastard child of callables and a complex type
     # FIXME: Unify types
     z3_type = z3.Datatype(name)
     z3_type.declare(f"mk_{name}")
     self.members = OrderedDict()
     self.z3_type = z3_type.create()
     self.const = z3.Const(name, self.z3_type)
     self.p4_attrs = {}
     # simple fix for now until I know how to initialize params for externs
     self.params = {}
     self.name = name
     self.type_params = type_params
     # these are method declarations, not methods
     for method in methods:
         self.p4_attrs[method.lval] = method.rval
Exemple #12
0
    def initialize(self, ctx):
        flat_names = []
        for idx, (member_name, member_type) in enumerate(self.fields):
            try:
                member_type = ctx.resolve_type(member_type)
            except KeyError:
                # A generic type, just use the string for now
                pass

            if isinstance(member_type, P4ComplexType):
                # the member is a complex type
                # retrieve it is flat list of fields
                # append it to the member list
                for sub_member in member_type.flat_names:
                    # FIXME: this is not the right way to construct a member...
                    member = Member(P4Member(member_name, sub_member.name),
                                    sub_member.p4_type, sub_member.width)
                    flat_names.append(member)
                self.width += member_type.width
            else:
                if isinstance(member_type, z3.BoolSortRef):
                    # bools do not have a size attribute unfortunately
                    member_width = 1
                elif isinstance(member_type, z3.BitVecSortRef):
                    member_width = member_type.size()
                else:
                    # a kind of strange sub-type, unclear what its width is
                    # example: generics
                    member_width = 0
                self.width += member_width
                member = Member(member_name, member_type, member_width)
                flat_names.append(member)
            self.fields[idx] = (member_name, member_type)

        if self.width == 0:
            # we are dealing with an empty struct, create a dummy datatype
            z3_type = z3.Datatype(self.name)
            z3_type.declare(f"mk_{self.name}")
            self.z3_type = z3_type.create()
        else:
            # use the flat bit width of the struct as datatype
            self.z3_type = z3.BitVecSort(self.width)
        self.flat_names = flat_names
Exemple #13
0
def f2():
    a, b, c, d = z3.BitVecs('a b c d', 3)  # 4 bitvectors variable

    tuple = z3.Datatype('tuple')  # new data type 'tuple'
    tuple.declare(
        'tuple', ('f1', z3.BitVecSort(3)),
        ('f2', z3.BitVecSort(3)))  # f1, f2 are for accessing element in tuples
    tuple = tuple.create()
    tuple1 = tuple.tuple(a, b)  # tuple1 -> (a, b)
    tuple2 = tuple.tuple(b, c)  # tuple2 -> (b, c)

    tuple1_f2 = tuple.f2(tuple1)  # a
    #tuple1_f2 = tuple.f2(tuple1) # b
    tuple2_f1 = tuple.f1(tuple2)  # c

    print(tuple1_f2, tuple2_f1)

    if (tuple1_f2 == tuple2_f1):
        print("hi")

    arr0 = z3.K(tuple, False)  # arr0 -> arr0[tuple]  = false
    arr1 = z3.Store(arr0, tuple1, True)  # arr1 -> arr0[tuple1] = true
    arr2 = z3.Store(arr1, tuple2, True)  # arr  -> arr0[tuple2] = true
    print(arr0)
    print(arr1)
    print(arr2)

    #print(arr1[tuple1])
    #print(arr2[tuple2])

    #print(arr0)
    #print(arr1)
    #print(arr2)

    s = z3.Solver()

    s.add(tuple1_f1 == tuple2_f2)  # a = c
    s.add(tuple1_f1 == tuple1_f2)  # a = b
Exemple #14
0
try:
    import z3  # type: ignore[import]
    HAS_Z3 = True
    # dynamic type
    dyn = z3.DeclareSort('Dyn')
    dyn_type = z3.Const('dyn', dyn)

    # dimension
    dim = z3.Datatype('dim')
    dim.declare('dim', ('0', z3.IntSort()), ('1', z3.IntSort()))
    dim = dim.create()

    # tensors
    tensor_type = z3.Datatype('TensorType')
    tensor_type.declare('Dyn', ('dyn', dyn))
    tensor_type.declare('tensor1', ('0', dim))
    tensor_type.declare('tensor2', ('0', dim), ('1', dim))
    tensor_type.declare('tensor3', ('0', dim), ('1', dim), ('2', dim))
    tensor_type.declare('tensor4', ('0', dim), ('1', dim), ('2', dim),
                        ('3', dim))
    tensor_type = tensor_type.create()

    # create dimension
    D = dim.dim

    z3_dyn = tensor_type.Dyn(dyn_type)

except ImportError:
    HAS_Z3 = False
basic_simplifier = RewriteEngine()
always = lambda x: True
# TODO: rewriting needs to resolve references -
# you cannot just rewrite anything named "isfunc"
# Or can you? Because pure must be imported as * and names cannot be reassigned?
for (patt, repl, condition) in [
        # # ('F(reduce(R, L, I))', 'reduce(R`, map(F, L), I)', reduce_fn_check),
]:
    basic_simplifier.add(exprparse(patt), exprparse(repl), condition)

# expr = exprparse('IsTruthy(isint(c) and isint(d))')
# rewritten = basic_simplifier.rewrite(expr)
# print('rewrite engine test', unparse(expr), unparse(rewritten))

PyFunc = z3.DeclareSort('PyFunc')
Unk = z3.Datatype('Unk')
Unk.declare('none')
Unk.declare('bool', ('tobool', z3.BoolSort()))
Unk.declare('int', ('toint', z3.IntSort()))
Unk.declare('string', ('tostring', z3.StringSort()))
Unk.declare('func', ('tofunc', PyFunc))
Unk.declare('app', ('tl', Unk), ('hd', Unk))
Unk.declare('_')  # empty tuple
Unk.declare('undef')  # error value
(Unk, ) = z3.CreateDatatypes(Unk)
App = z3.Function('.', Unk, Unk, Unk)


class ZHolder():
    pass
Exemple #16
0
def _get_datatype_from_list(values, datatype_name):
    datatype = z3.Datatype(datatype_name)
    for v in values:
        datatype.declare(v)
    return datatype.create()
Exemple #17
0
        rects = rects[0]

    cases = []
    for i in range(len(rects)):
        assert isinstance(rects[i], Rectangle)
        for j in range(i+1, len(rects)):
            cases.append(rects[i].non_intersecting(rects[j]))
    return And(cases)


class AssemblyMachine(Rectangle):
    def __init__(self, size=3, x=None, y=None):
        super().__init__(x=x, y=y, size=size)


Dir = z3.Datatype('Dir')
Dir.declare('u')
Dir.declare('d')
Dir.declare('l')
Dir.declare('r')
Dir = Dir.create()


class DirVal:
    _IDX = 0
    def __init__(self, label=''):
        self.v = Const(f'dirval_{label}{self._IDX}', Dir)
        self.__class__._IDX += 1

    def eval(self):
        return SOL.eval(self.v)
Exemple #18
0
#!/usr/bin/env python

import z3

English = z3.Datatype('English')
English.declare('One')
English.declare('Two')
English.declare('Three')
English.declare('Four')
English.declare('Five')
English.declare('Six')
English.declare('Seven')
English.declare('Eight')
English.declare('Nine')
English.declare('Ten')
English = English.create()

Spanish = z3.Datatype('Spanish')
Spanish.declare('Uno')
Spanish.declare('Dos')
Spanish.declare('Tres')
Spanish.declare('Cuatro')
Spanish.declare('Cinco')
Spanish.declare('Seis')
Spanish.declare('Siete')
Spanish.declare('Ocho')
Spanish.declare('Nueve')
Spanish.declare('Diez')
Spanish.declare('Nada')
Spanish = Spanish.create()
Exemple #19
0
        return self._simpleCheck(event.var, event.val)

    def checkTrigger(self, val):
        if self.hold_t is not None or self.delay is not None:
            return False
        else:
            return self._simpleCheck(self.var, val)

    def __str__(self):
        return 'hold: %s, delay: %s, %s%s%s' % (self.hold_t, self.delay, self.var, self.comp, self.val)

EventExpression = TriggerExpression

# ====== the following is all for symbolic expressions ====== #
# comparator for boolean and set variables
Comparator = z3.Datatype('Comparator')
Comparator.declare('eq')
Comparator.declare('neq')
Comparator = Comparator.create()
# comparator for range variables
ComparatorRange = z3.Datatype('ComparatorRange')
ComparatorRange.declare('gt')
ComparatorRange.declare('lt')
ComparatorRange.declare('geq')
ComparatorRange.declare('leq')
ComparatorRange.declare('eq')
ComparatorRange.declare('neq')
ComparatorRange = ComparatorRange.create()


def checkExpression(value, target_value, comp, typ='bool'):
Exemple #20
0
    def _eval_sep(self, state: State,
                  st_prefix: str) -> Tuple[List[z3.ExprRef], z3.ExprRef]:
        '''
        create new state variable add assertions enforcing its value
        '''
        def p(st: str, must_be_new: bool = True) -> str:
            st = st_prefix + '_' + st
            assert (not must_be_new) or st not in self.seen, (st, self.seen)
            self.seen.add(st)
            return st

        assertions: List[z3.ExprRef] = []  # will be added to the solver
        state_models_sep = z3.Bool(
            p('models_sep'))  # will become self.state_vs[i]

        # create z3 symbols for all relations, functions, and constants, and assert their meaning

        # create universe unary relations
        scope = max(len(elems) for elems in state.univs.values())
        V = z3.Datatype(p('elements'))

        def e(i: int) -> str:
            return f'e{i}'

        for i in range(scope):
            V.declare(e(i))
        V = V.create()

        def is_e(i: int) -> z3.FuncDeclRef:
            return getattr(V, 'is_' + e(i))

        def ee(i: int) -> z3.ExprRef:
            return getattr(V, e(i))

        universes = {
            s.name: z3.Function(p(s.name), V, z3.BoolSort())
            for s in state.univs
        }
        elem_to_z3: Dict[str, z3.ExprRef] = {}
        for s, elems in state.univs.items():
            assertions.extend(universes[s.name](ee(i))
                              for i in range(len(elems)))
            assertions.extend(
                z3.Not(universes[s.name](ee(i)))
                for i in range(len(elems), scope))
            for i, elem in enumerate(elems):
                assert elem not in elem_to_z3
                elem_to_z3[elem] = ee(i)

        # create relations
        def lit(e: z3.ExprRef, polarity: bool) -> z3.ExprRef:
            return e if polarity else z3.Not(e)

        relations: Dict[str, Union[z3.ExprRef, z3.FuncDeclRef]] = {
            r.name:
            z3.Function(p(r.name), *repeat(V, len(r.arity)), z3.BoolSort())
            if len(r.arity) > 0 else z3.Bool(p(r.name))
            for r in state.rel_interps
        }
        for r, ri in state.rel_interps.items():
            if len(r.arity) == 0:
                assert len(ri) == 1 and () in ri, (r, ri)
                a = relations[r.name]
                assert isinstance(a, z3.ExprRef)
                assertions.append(lit(a, ri[()]))
            else:
                for tup, polarity in ri.items():
                    a = relations[r.name]
                    assert isinstance(a, z3.FuncDeclRef)
                    args = [elem_to_z3[x] for x in tup]
                    assertions.append(lit(a(*args), polarity))

        # create functions
        assert all(len(f.arity) > 0 for f in state.func_interps)
        functions: Dict[str, z3.FuncDeclRef] = {
            f.name: z3.Function(p(f.name), *repeat(V, len(f.arity)), V)
            for f in state.func_interps
        }
        for f, fi in state.func_interps.items():
            for tup, img in fi.items():
                args = [elem_to_z3[x] for x in tup]
                assertions.append(functions[f.name](*args) == elem_to_z3[img])

        # create constants
        constants: Dict[str, z3.ExprRef] = {
            c.name: z3.Const(p(c.name), V)
            for c in state.const_interps
        }
        for c, ci in state.const_interps.items():
            assertions.append(constants[c.name] == elem_to_z3[ci])

        # now force state_models_sep
        z3_vs = [z3.Const(p(f'V{i}'), V) for i in range(len(self.vs))]

        def to_z3(e: Expr) -> z3.ExprRef:
            if isinstance(e, Id):
                if e.name in self.var_names:
                    return z3_vs[self.var_names.index(e.name)]
                elif e.name in constants:
                    return constants[e.name]
                elif e.name in relations and isinstance(
                        r := relations[e.name], z3.ExprRef):
                    return r
                else:
                    assert False, e
            elif isinstance(e, AppExpr) and e.callee in functions:
                return functions[e.callee](*(map(to_z3, e.args)))