예제 #1
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'
def simplify_type_system_test(orig_expr: str,
                              simplified_expr: str,
                              simplified_type: CairoType,
                              identifiers: Optional[IdentifierManager] = None):
    parsed_expr = mark_types_in_expr_resolved(parse_expr(orig_expr))
    assert simplify_type_system(
        parsed_expr, identifiers=identifiers) == (parse_expr(simplified_expr),
                                                  simplified_type)
def verify_exception(expr_str: str,
                     error: str,
                     identifiers: Optional[IdentifierManager] = None,
                     resolve_types=True):
    """
    Verifies that calling simplify_type_system() on the code results in the given error.
    """
    with pytest.raises(CairoTypeError) as e:
        parsed_expr = parse_expr(expr_str)
        if resolve_types:
            parsed_expr = mark_types_in_expr_resolved(parsed_expr)
        simplify_type_system(parsed_expr, identifiers)
    # Remove line and column information from the error using a regular expression.
    assert re.sub(':[0-9]+:[0-9]+: ', 'file:?:?: ',
                  str(e.value)) == error.strip()
예제 #4
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
예제 #5
0
 def _deserialize(self, value, attr, data, **kwargs):
     return mark_types_in_expr_resolved(parse_expr(value))
예제 #6
0
 def _serialize(self, value, attr, obj, **kwargs):
     if value is None:
         return None
     assert mark_types_in_expr_resolved(value) == value, \
         f"Expected types in '{value}' to be resolved."
     return value.format()
예제 #7
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 = TypeStruct(
        scope=scope('MyStruct'), is_fully_resolved=True)
    my_struct_star = TypePointer(pointee=my_struct)
    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()),
                'struct': MemberDefinition(offset=11, cairo_type=my_struct),
            },
            size=11,
        ),
    }
    identifiers = IdentifierManager.from_dict(identifier_values)
    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=identifiers,
        evaluator=ExpressionEvaluator(prime, ap, fp, memory, identifiers).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,
    }

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

    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
def test_type_subscript_op():
    felt_star_star = TypePointer(pointee=TypePointer(pointee=TypeFelt()))
    t = TypeStruct(scope=scope('T'), is_fully_resolved=True)
    t_star = TypePointer(pointee=t)

    identifier_dict = {
        scope('T'): StructDefinition(full_name=scope('T'), members={}, size=7)
    }
    identifiers = IdentifierManager.from_dict(identifier_dict)

    simplify_type_system_test('cast(fp, felt*)[3]', '[fp + 3 * 1]', TypeFelt())
    simplify_type_system_test('cast(fp, felt***)[0]', '[fp + 0 * 1]',
                              felt_star_star)
    simplify_type_system_test('[cast(fp, T****)][ap][ap]',
                              '[[[fp] + ap * 1] + ap * 1]', t_star)
    simplify_type_system_test('cast(fp, T**)[1][2]',
                              '[[fp + 1 * 1] + 2 * 7]',
                              t,
                              identifiers=identifiers)

    # Test that 'cast(fp, T*)[2 * ap + 3]' simplifies into '[fp + (2 * ap + 3) * 7]', but without
    # the parentheses.
    assert simplify_type_system(
        mark_types_in_expr_resolved(parse_expr('cast(fp, T*)[2 * ap + 3]')),
        identifiers=identifiers) == (remove_parentheses(
            parse_expr('[fp + (2 * ap + 3) * 7]')), t)

    # Test subscript operator for tuples.
    simplify_type_system_test('(cast(fp, felt**), fp, cast(fp, T*))[2]', 'fp',
                              t_star)
    simplify_type_system_test('(cast(fp, felt**), fp, cast(fp, T*))[0]', 'fp',
                              felt_star_star)
    simplify_type_system_test('(cast(fp, felt**), ap, cast(fp, T*))[3*4 - 11]',
                              'ap', TypeFelt())
    simplify_type_system_test('[cast(ap, (felt, felt)*)][0]', '[ap + 0]',
                              TypeFelt())
    simplify_type_system_test('[cast(ap, (T*, T, felt, T*, felt*)*)][3]',
                              '[ap + 9]',
                              t_star,
                              identifiers=identifiers)

    # Test failures.

    verify_exception(
        '(fp, fp, fp)[cast(ap, felt*)]', """
file:?:?: Cannot apply subscript-operator with offset of non-felt type 'felt*'.
(fp, fp, fp)[cast(ap, felt*)]
             ^*************^
""")

    verify_exception(
        '(fp, fp, fp)[[ap]]', """
file:?:?: Subscript-operator for tuples supports only constant offsets, found 'ExprDeref'.
(fp, fp, fp)[[ap]]
             ^**^
""")

    # The simplifier in TypeSystemVisitor cannot access PRIME, so PyConsts are unsimplified.
    verify_exception(
        '(fp, fp, fp)[%[1%]]', """
file:?:?: Subscript-operator for tuples supports only constant offsets, found 'ExprPyConst'.
(fp, fp, fp)[%[1%]]
             ^***^
""")

    verify_exception(
        '(fp, fp, fp)[3]', """
file:?:?: Tuple index 3 is out of range [0, 3).
(fp, fp, fp)[3]
^*************^
""")

    verify_exception(
        '[cast(fp, (T*, T, felt)*)][-1]', """
file:?:?: Tuple index -1 is out of range [0, 3).
[cast(fp, (T*, T, felt)*)][-1]
^****************************^
""")

    verify_exception(
        'cast(fp, felt)[0]', """
file:?:?: Cannot apply subscript-operator to non-pointer, non-tuple type 'felt'.
cast(fp, felt)[0]
^***************^
""")

    verify_exception(
        '[cast(fp, T*)][0]', """
file:?:?: Cannot apply subscript-operator to non-pointer, non-tuple type 'T'.
[cast(fp, T*)][0]
^***************^
""")

    verify_exception(
        'cast(fp, felt*)[[cast(ap, T*)]]', """
file:?:?: Cannot apply subscript-operator with offset of non-felt type 'T'.
cast(fp, felt*)[[cast(ap, T*)]]
                ^************^
""")

    verify_exception('cast(fp, Z*)[0]',
                     """
file:?:?: Unknown identifier 'Z'.
cast(fp, Z*)[0]
^*************^
""",
                     identifiers=identifiers)

    verify_exception('cast(fp, T*)[0]',
                     """
file:?:?: Unknown identifier 'T'.
cast(fp, T*)[0]
^*************^
""",
                     identifiers=None)