Пример #1
0
def test_eval_reference():
    x = Reference(pc=0,
                  value=parse_expr('2 * ap + 3 * fp - 5'),
                  ap_tracking_data=RegTrackingData(group=1, offset=5))
    with pytest.raises(FlowTrackingError):
        x.eval(RegTrackingData(group=2, offset=5))
    assert x.eval(RegTrackingData(
        group=1, offset=8)).format() == '2 * (ap - 3) + 3 * fp - 5'
Пример #2
0
    def handle_ReferenceDefinition(self, name: str,
                                   identifier: ReferenceDefinition,
                                   scope: ScopedName,
                                   set_value: Optional[MaybeRelocatable]):
        # In set mode, take the address of the given reference instead.
        reference = self._context.flow_tracking_data.resolve_reference(
            reference_manager=self._context.reference_manager,
            name=identifier.full_name)

        if set_value is None:
            expr = reference.eval(self._context.flow_tracking_data.ap_tracking)
            expr, expr_type = simplify_type_system(expr)
            if isinstance(expr_type, TypeStruct):
                # If the reference is of type T, take its address and treat it as T*.
                assert isinstance(expr, ExprDeref), \
                    f"Expected expression of type '{expr_type.format()}' to have an address."
                expr = expr.addr
                expr_type = TypePointer(pointee=expr_type)
            val = self._context.evaluator(expr)

            # Check if the type is felt* or any_type**.
            is_pointer_to_felt_or_pointer = (
                isinstance(expr_type, TypePointer)
                and isinstance(expr_type.pointee, (TypePointer, TypeFelt)))
            if isinstance(expr_type,
                          TypeFelt) or is_pointer_to_felt_or_pointer:
                return val
            else:
                # Typed reference, return VmConstsReference which allows accessing members.
                assert isinstance(expr_type, TypePointer) and \
                    isinstance(expr_type.pointee, TypeStruct), \
                    'Type must be of the form T*.'
                return VmConstsReference(
                    context=self._context,
                    accessible_scopes=[expr_type.pointee.scope],
                    reference_value=val,
                    add_addr_var=True)
        else:
            assert str(scope[-1:]) == name, 'Expecting scope to end with name.'
            value, value_type = simplify_type_system(reference.value)
            assert isinstance(value, ExprDeref), f"""\
{scope} (= {value.format()}) does not reference memory and cannot be assigned."""

            value_ref = Reference(
                pc=reference.pc,
                value=ExprCast(expr=value.addr, dest_type=value_type),
                ap_tracking_data=reference.ap_tracking_data,
            )

            addr = self._context.evaluator(
                value_ref.eval(self._context.flow_tracking_data.ap_tracking))
            self._context.memory[addr] = set_value
Пример #3
0
def test_revoked_reference():
    reference_manager = ReferenceManager()
    ref_id = reference_manager.alloc_id(reference=Reference(
        pc=0,
        value=parse_expr('[ap + 1]'),
        ap_tracking_data=RegTrackingData(group=0, offset=2),
    ))

    identifier_values = {
        scope('x'):
        ReferenceDefinition(full_name=scope('x'),
                            cairo_type=TypeFelt(),
                            references=[]),
    }
    prime = 2**64 + 13
    ap = 100
    fp = 200
    memory = {}

    flow_tracking_data = FlowTrackingDataActual(
        ap_tracking=RegTrackingData(group=1, offset=4),
        reference_ids={scope('x'): ref_id},
    )
    context = VmConstsContext(
        identifiers=IdentifierManager.from_dict(identifier_values),
        evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval,
        reference_manager=reference_manager,
        flow_tracking_data=flow_tracking_data,
        memory=memory,
        pc=0)
    consts = VmConsts(context=context, accessible_scopes=[ScopedName()])

    with pytest.raises(FlowTrackingError, match='Failed to deduce ap.'):
        assert consts.x
Пример #4
0
def test_reference_rebinding():
    identifier_values = {
        scope('ref'):
        ReferenceDefinition(
            full_name=scope('ref'),
            cairo_type=TypeFelt(),
            references=[],
        )
    }

    reference_manager = ReferenceManager()
    flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData())
    consts = get_vm_consts(identifier_values, reference_manager,
                           flow_tracking_data)
    with pytest.raises(FlowTrackingError, match='Reference ref revoked'):
        consts.ref

    flow_tracking_data = flow_tracking_data.add_reference(
        reference_manager=reference_manager,
        name=scope('ref'),
        ref=Reference(
            pc=10,
            value=parse_expr('10'),
            ap_tracking_data=RegTrackingData(group=0, offset=2),
        ),
    )
    consts = get_vm_consts(identifier_values, reference_manager,
                           flow_tracking_data)
    assert consts.ref == 10
Пример #5
0
def test_reference_to_structs():
    t = TypeStruct(scope=scope('T'), is_fully_resolved=True)
    t_star = TypePointer(pointee=t)
    identifier_values = {
        scope('ref'): ReferenceDefinition(full_name=scope('ref'),
                                          references=[]),
        scope('T.x'): MemberDefinition(offset=3, cairo_type=t_star),
    }
    reference_manager = ReferenceManager()
    flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData())
    flow_tracking_data = flow_tracking_data.add_reference(
        reference_manager=reference_manager,
        name=scope('ref'),
        ref=Reference(
            pc=0,
            value=mark_types_in_expr_resolved(parse_expr('cast([100], T)')),
            ap_tracking_data=RegTrackingData(group=0, offset=2),
        ))
    consts = get_vm_consts(identifier_values,
                           reference_manager,
                           flow_tracking_data,
                           memory={103: 200})

    assert consts.ref.address_ == 100
    assert consts.ref.x == 200
Пример #6
0
def test_offset_reference_definition_typed_members():
    t = TypeStruct(scope=scope('T'), is_fully_resolved=True)
    t_star = TypePointer(pointee=t)
    reference_manager = ReferenceManager()

    main_reference = ReferenceDefinition(full_name=scope('a'),
                                         cairo_type=t_star,
                                         references=[])
    references = {
        scope('a'):
        reference_manager.alloc_id(
            Reference(
                pc=0,
                value=mark_types_in_expr_resolved(parse_expr('cast(ap, T*)')),
                ap_tracking_data=RegTrackingData(group=0, offset=0),
            )),
    }

    flow_tracking_data = FlowTrackingDataActual(
        ap_tracking=RegTrackingData(group=0, offset=1),
        reference_ids=references,
    )

    # Create OffsetReferenceDefinition instance for an expression of the form "a.<member_path>",
    # in this case a.x.y.z, and check the result of evaluation of this expression.
    definition = OffsetReferenceDefinition(parent=main_reference,
                                           member_path=scope('x.y.z'))
    assert definition.eval(reference_manager=reference_manager,
                           flow_tracking_data=flow_tracking_data).format(
                           ) == 'cast(ap - 1, T*).x.y.z'
Пример #7
0
def test_flow_tracking_converge_references(refs):
    flow_tracking = FlowTracking()
    flow_tracking.add_flow_to_label(ScopedName.from_string('a'),
                                    RegChangeUnknown())
    flow_tracking.add_flow_to_label(ScopedName.from_string('b'),
                                    RegChangeUnknown())

    # Label a.
    flow_tracking.revoke()
    flow_tracking.converge_with_label(ScopedName.from_string('a'))
    flow_tracking.add_reference(
        name=ScopedName.from_string('x'),
        ref=Reference(
            pc=0,
            value=refs.expr_a,
            ap_tracking_data=flow_tracking.get_ap_tracking(),
        ))
    flow_tracking.add_ap(13)
    flow_tracking.add_flow_to_label(ScopedName.from_string('c'), 0)

    # Label b.
    flow_tracking.revoke()
    flow_tracking.converge_with_label(ScopedName.from_string('b'))
    flow_tracking.add_reference(
        name=ScopedName.from_string('x'),
        ref=Reference(
            pc=0,
            value=refs.expr_b,
            ap_tracking_data=flow_tracking.get_ap_tracking(),
        ))
    flow_tracking.add_ap(15)
    flow_tracking.add_flow_to_label(ScopedName.from_string('c'), 0)

    # Label c - convergence.
    flow_tracking.revoke()
    flow_tracking.converge_with_label(ScopedName.from_string('c'))

    if refs.valid:
        flow_tracking.resolve_reference(ScopedName.from_string('x'))
    else:
        with pytest.raises(FlowTrackingError):
            flow_tracking.resolve_reference(ScopedName.from_string('x'))
def test_offset_reference_definition_typed_members():
    t = TypeStruct(scope=scope('T'), is_fully_resolved=True)
    s_star = TypePointer(
        pointee=TypeStruct(scope=scope('S'), is_fully_resolved=True))
    reference_manager = ReferenceManager()
    identifiers = {
        scope('T'): ScopeDefinition(),
        scope('T.x'): MemberDefinition(offset=3, cairo_type=s_star),
        scope('T.flt'): MemberDefinition(offset=4, cairo_type=TypeFelt()),
        scope('S'): ScopeDefinition(),
        scope('S.x'): MemberDefinition(offset=10, cairo_type=t),
    }
    main_reference = ReferenceDefinition(full_name=scope('a'), references=[])
    references = {
        scope('a'):
        reference_manager.get_id(
            Reference(
                pc=0,
                value=mark_types_in_expr_resolved(parse_expr('cast(ap, T*)')),
                ap_tracking_data=RegTrackingData(group=0, offset=0),
            )),
    }

    flow_tracking_data = FlowTrackingDataActual(
        ap_tracking=RegTrackingData(group=0, offset=1),
        reference_ids=references,
    )

    # Create OffsetReferenceDefinition instances for expressions of the form "a.<member_path>",
    # such as a.x and a.x.x, and check the result of evaluation those expressions.
    for member_path, expected_result in [
        ('x', 'cast([ap - 1 + 3], S*)'),
        ('x.x', 'cast([[ap - 1 + 3] + 10], T)'),
        ('x.x.x', 'cast([&[[ap - 1 + 3] + 10] + 3], S*)'),
        ('x.x.flt', 'cast([&[[ap - 1 + 3] + 10] + 4], felt)')
    ]:
        definition = OffsetReferenceDefinition(parent=main_reference,
                                               identifier_values=identifiers,
                                               member_path=scope(member_path))
        assert definition.eval(
            reference_manager=reference_manager,
            flow_tracking_data=flow_tracking_data).format() == expected_result

    definition = OffsetReferenceDefinition(parent=main_reference,
                                           identifier_values=identifiers,
                                           member_path=scope('x.x.flt.x'))
    with pytest.raises(
            DefinitionError,
            match='Member access requires a type of the form Struct*.'):
        assert definition.eval(
            reference_manager=reference_manager,
            flow_tracking_data=flow_tracking_data).format() == expected_result
Пример #9
0
    def converge(
            self, reference_manager: ReferenceManager, other: 'FlowTrackingData',
            group_alloc: Callable):
        if not isinstance(other, FlowTrackingDataActual):
            return other.converge(reference_manager, self, group_alloc)

        new_ap_tracking = self.ap_tracking.converge(other.ap_tracking, group_alloc)
        simplifier = ExpressionSimplifier()

        # Allow different references from different branches to unite if they have the same name
        # and the same expression at the converged ap_tracking.
        reference_ids = {}
        for name, ref_id in self.reference_ids.items():
            other_ref_id = other.reference_ids.get(name)
            if other_ref_id is None:
                continue
            reference = reference_manager.get_ref(ref_id)
            other_ref = reference_manager.get_ref(other_ref_id)
            try:
                ref_expr = reference.eval(self.ap_tracking)
                if simplifier.visit(ref_expr) == \
                        simplifier.visit(other_ref.eval(other.ap_tracking)):
                    # Same expression.
                    if self.ap_tracking != new_ap_tracking:
                        # Create a new reference on the new ap tracking.
                        new_reference = Reference(
                            pc=reference.pc,
                            value=ref_expr,
                            ap_tracking_data=new_ap_tracking,
                        )
                        ref_id = reference_manager.get_id(new_reference)
                    reference_ids[name] = ref_id
            except FlowTrackingError:
                pass

        return FlowTrackingDataActual(
            ap_tracking=new_ap_tracking,
            reference_ids=reference_ids,
        )
Пример #10
0
def test_reference_to_structs():
    t = TypeStruct(scope=scope('T'), is_fully_resolved=True)
    t_star = TypePointer(pointee=t)
    identifier_values = {
        scope('ref'): ReferenceDefinition(
            full_name=scope('ref'), cairo_type=t, references=[]
        ),
        scope('T'): StructDefinition(
            full_name=scope('T'),
            members={
                'x': MemberDefinition(offset=3, cairo_type=t_star),
            },
            size=4,
        ),
    }
    reference_manager = ReferenceManager()
    flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData())
    flow_tracking_data = flow_tracking_data.add_reference(
        reference_manager=reference_manager,
        name=scope('ref'),
        ref=Reference(
            pc=0,
            value=mark_types_in_expr_resolved(parse_expr('[cast(100, T*)]')),
            ap_tracking_data=RegTrackingData(group=0, offset=2),
        ),
    )
    memory = {103: 200}
    consts = get_vm_consts(
        identifier_values, reference_manager, flow_tracking_data, memory=memory)

    assert consts.ref.address_ == 100
    assert consts.ref.x.address_ == 200
    # Set the pointer ref.x.x to 300.
    consts.ref.x.x = 300
    assert memory[203] == 300
    assert consts.ref.x.x.address_ == 300

    assert consts.ref.type_ == consts.T
Пример #11
0
def test_references():
    reference_manager = ReferenceManager()
    references = {
        scope('x.ref'):
        reference_manager.alloc_id(
            Reference(
                pc=0,
                value=parse_expr('[ap + 1]'),
                ap_tracking_data=RegTrackingData(group=0, offset=2),
            )),
        scope('x.ref2'):
        reference_manager.alloc_id(
            Reference(
                pc=0,
                value=parse_expr('[ap + 1] + 0'),
                ap_tracking_data=RegTrackingData(group=0, offset=2),
            )),
        scope('x.ref3'):
        reference_manager.alloc_id(
            Reference(
                pc=0,
                value=parse_expr('ap + 1'),
                ap_tracking_data=RegTrackingData(group=0, offset=2),
            )),
        scope('x.typeref'):
        reference_manager.alloc_id(
            Reference(
                pc=0,
                value=mark_types_in_expr_resolved(
                    parse_expr('cast(ap + 1, MyStruct*)')),
                ap_tracking_data=RegTrackingData(group=0, offset=3),
            )),
        scope('x.typeref2'):
        reference_manager.alloc_id(
            Reference(
                pc=0,
                value=mark_types_in_expr_resolved(
                    parse_expr('cast([ap + 1], MyStruct*)')),
                ap_tracking_data=RegTrackingData(group=0, offset=3),
            )),
    }
    my_struct_star = TypePointer(
        pointee=TypeStruct(scope=scope('MyStruct'), is_fully_resolved=True))
    identifier_values = {
        scope('x.ref'):
        ReferenceDefinition(full_name=scope('x.ref'),
                            cairo_type=TypeFelt(),
                            references=[]),
        scope('x.ref2'):
        ReferenceDefinition(full_name=scope('x.ref2'),
                            cairo_type=TypeFelt(),
                            references=[]),
        scope('x.ref3'):
        ReferenceDefinition(full_name=scope('x.ref3'),
                            cairo_type=TypeFelt(),
                            references=[]),
        scope('x.typeref'):
        ReferenceDefinition(full_name=scope('x.typeref'),
                            cairo_type=my_struct_star,
                            references=[]),
        scope('x.typeref2'):
        ReferenceDefinition(full_name=scope('x.typeref2'),
                            cairo_type=my_struct_star,
                            references=[]),
        scope('MyStruct'):
        StructDefinition(
            full_name=scope('MyStruct'),
            members={
                'member': MemberDefinition(offset=10, cairo_type=TypeFelt()),
            },
            size=11,
        ),
    }
    prime = 2**64 + 13
    ap = 100
    fp = 200
    memory = {
        (ap - 2) + 1: 1234,
        (ap - 1) + 1: 1000,
        (ap - 1) + 1 + 2: 13,
        (ap - 1) + 1 + 10: 17,
    }

    flow_tracking_data = FlowTrackingDataActual(
        ap_tracking=RegTrackingData(group=0, offset=4),
        reference_ids=references,
    )
    context = VmConstsContext(
        identifiers=IdentifierManager.from_dict(identifier_values),
        evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval,
        reference_manager=reference_manager,
        flow_tracking_data=flow_tracking_data,
        memory=memory,
        pc=0)
    consts = VmConsts(context=context, accessible_scopes=[ScopedName()])

    assert consts.x.ref == memory[(ap - 2) + 1]
    assert consts.x.typeref.address_ == (ap - 1) + 1
    assert consts.x.typeref.member == memory[(ap - 1) + 1 + 10]
    with pytest.raises(IdentifierError,
                       match="'abc' is not a member of 'MyStruct'."):
        consts.x.typeref.abc

    with pytest.raises(IdentifierError,
                       match="'SIZE' is not a member of 'MyStruct'."):
        consts.x.typeref.SIZE

    with pytest.raises(
            AssertionError,
            match='Cannot change the value of a struct definition.'):
        consts.MyStruct = 13

    assert consts.MyStruct.member == 10
    with pytest.raises(AssertionError,
                       match='Cannot change the value of a constant.'):
        consts.MyStruct.member = 13

    assert consts.MyStruct.SIZE == 11
    with pytest.raises(AssertionError,
                       match='Cannot change the value of a constant.'):
        consts.MyStruct.SIZE = 13

    with pytest.raises(IdentifierError,
                       match="'abc' is not a member of 'MyStruct'."):
        consts.MyStruct.abc

    # Test that VmConsts can be used to assign values to references of the form '[...]'.
    memory.clear()

    consts.x.ref = 1234
    assert memory == {(ap - 2) + 1: 1234}

    memory.clear()
    consts.x.typeref.member = 1001
    assert memory == {(ap - 1) + 1 + 10: 1001}

    memory.clear()
    consts.x.typeref2 = 4321
    assert memory == {(ap - 1) + 1: 4321}

    consts.x.typeref2.member = 1
    assert memory == {
        (ap - 1) + 1: 4321,
        4321 + 10: 1,
    }

    with pytest.raises(AssertionError,
                       match='Cannot change the value of a scope definition'):
        consts.x = 1000
    with pytest.raises(
            AssertionError,
            match=
            r'x.ref2 \(= \[ap \+ 1\] \+ 0\) does not reference memory and cannot be assigned.',
    ):
        consts.x.ref2 = 1000
    with pytest.raises(
            AssertionError,
            match=
            r'x.typeref \(= ap \+ 1\) does not reference memory and cannot be assigned.',
    ):
        consts.x.typeref = 1000
Пример #12
0
def test_references():
    reference_manager = ReferenceManager()
    references = {
        scope('x.ref'):
        reference_manager.get_id(
            Reference(
                pc=0,
                value=parse_expr('[ap + 1]'),
                ap_tracking_data=RegTrackingData(group=0, offset=2),
            )),
        scope('x.ref2'):
        reference_manager.get_id(
            Reference(
                pc=0,
                value=parse_expr('[ap + 1] + 0'),
                ap_tracking_data=RegTrackingData(group=0, offset=2),
            )),
        scope('x.ref3'):
        reference_manager.get_id(
            Reference(
                pc=0,
                value=parse_expr('ap + 1'),
                ap_tracking_data=RegTrackingData(group=0, offset=2),
            )),
        scope('x.typeref'):
        reference_manager.get_id(
            Reference(
                pc=0,
                value=mark_types_in_expr_resolved(
                    parse_expr('cast(ap + 1, a*)')),
                ap_tracking_data=RegTrackingData(group=0, offset=3),
            )),
        scope('x.typeref2'):
        reference_manager.get_id(
            Reference(
                pc=0,
                value=mark_types_in_expr_resolved(
                    parse_expr('cast([ap + 1], a*)')),
                ap_tracking_data=RegTrackingData(group=0, offset=3),
            )),
    }
    identifier_values = {
        scope('x.ref'):
        ReferenceDefinition(full_name=scope('x.ref'), references=[]),
        scope('x.ref2'):
        ReferenceDefinition(full_name=scope('x.ref2'), references=[]),
        scope('x.ref3'):
        ReferenceDefinition(full_name=scope('x.ref3'), references=[]),
        scope('x.typeref'):
        ReferenceDefinition(full_name=scope('x.typeref'), references=[]),
        scope('x.typeref2'):
        ReferenceDefinition(full_name=scope('x.typeref2'), references=[]),
        scope('a.member'):
        MemberDefinition(offset=10, cairo_type=TypeFelt()),
        scope('a.scope0.member'):
        MemberDefinition(offset=2, cairo_type=TypeFelt()),
    }
    prime = 2**64 + 13
    ap = 100
    fp = 200
    memory = {
        (ap - 2) + 1: 1234,
        (ap - 1) + 1: 1000,
        (ap - 1) + 1 + 2: 13,
        (ap - 1) + 1 + 10: 17,
    }

    flow_tracking_data = FlowTrackingDataActual(
        ap_tracking=RegTrackingData(group=0, offset=4),
        reference_ids=references,
    )
    context = VmConstsContext(
        identifiers=IdentifierManager.from_dict(identifier_values),
        evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval,
        reference_manager=reference_manager,
        flow_tracking_data=flow_tracking_data,
        memory=memory,
        pc=0)
    consts = VmConsts(context=context, accessible_scopes=[ScopedName()])

    assert consts.x.ref == memory[(ap - 2) + 1]
    assert consts.x.typeref.address_ == (ap - 1) + 1
    assert consts.x.typeref.member == memory[(ap - 1) + 1 + 10]
    with pytest.raises(
            NotImplementedError,
            match="Expected a member, found 'scope0' which is 'scope'"):
        consts.x.typeref.scope0

    # Test that VmConsts can be used to assign values to references of the form '[...]'.
    memory.clear()

    consts.x.ref = 1234
    assert memory == {(ap - 2) + 1: 1234}

    memory.clear()
    consts.x.typeref.member = 1001
    assert memory == {(ap - 1) + 1 + 10: 1001}

    memory.clear()
    consts.x.typeref2 = 4321
    assert memory == {(ap - 1) + 1: 4321}

    consts.x.typeref2.member = 1
    assert memory == {
        (ap - 1) + 1: 4321,
        4321 + 10: 1,
    }

    with pytest.raises(AssertionError,
                       match='Cannot change the value of a scope definition'):
        consts.x = 1000
    with pytest.raises(
            AssertionError,
            match=
            r'x.ref2 \(= \[ap \+ 1\] \+ 0\) does not reference memory and cannot be assigned.',
    ):
        consts.x.ref2 = 1000
    with pytest.raises(
            AssertionError,
            match=
            r'x.typeref \(= ap \+ 1\) does not reference memory and cannot be assigned.',
    ):
        consts.x.typeref = 1000
Пример #13
0
def test_eval_reference_fp_only():
    x = Reference(pc=0,
                  value=parse_expr('3 * fp - 5 + fp * fp'),
                  ap_tracking_data=RegTrackingData(group=1, offset=5))
    assert x.eval(RegTrackingData(
        group=2, offset=7)) == parse_expr('3 * fp - 5 + fp * fp')