def visit_ExprReg(self, expr: ExprReg) -> ExprConst: if expr.reg is Register.AP: assert self.ap is not None, 'Cannot substitute ap in the expression.' return ExprConst(val=self.ap, location=expr.location) elif expr.reg is Register.FP: return ExprConst(val=self.fp, location=expr.location) else: raise NotImplementedError( f'Register of type {expr.reg} is not supported')
def test_hex_int(): expr = parse_expr(' 0x1234 ') assert expr == ExprConst(val=0x1234) assert expr.format_str == '0x1234' assert expr.format() == '0x1234' expr = parse_expr('-0x01234') assert expr == ExprNeg(val=ExprConst(val=0x1234)) assert expr.val.format_str == '0x01234' assert expr.format() == '-0x01234' assert parse_expr('-0x1234') == parse_expr('- 0x1234')
def process_retdata( self, ret_struct_ptr: Expression, ret_struct_type: CairoType, struct_def: StructDefinition, location: Optional[Location]) -> Tuple[Expression, Expression]: """ Processes the return values and return retdata_size and retdata_ptr. """ # Verify all of the return types are felts. for _, member_def in struct_def.members.items(): cairo_type = member_def.cairo_type if not isinstance(cairo_type, TypeFelt): raise PreprocessorError( f'Unsupported argument type {cairo_type.format()}.', location=cairo_type.location) self.add_reference( name=self.current_scope + 'retdata_ptr', value=ExprDeref( addr=ExprReg(reg=Register.AP), location=location, ), cairo_type=TypePointer(TypeFelt()), require_future_definition=False, location=location) self.visit(CodeElementHint( hint=ExprHint( hint_code='memory[ap] = segments.add()', n_prefix_newlines=0, location=location, ), location=location, )) # Skip check of hint whitelist as it fails before the workaround below. super().visit_CodeElementInstruction(CodeElementInstruction(InstructionAst( body=AddApInstruction(ExprConst(1)), inc_ap=False, location=location))) # Remove the references from the last instruction's flow tracking as they are # not needed by the hint and they cause the hint whitelist to fail. assert len(self.instructions[-1].hints) == 1 hint, hint_flow_tracking_data = self.instructions[-1].hints[0] self.instructions[-1].hints[0] = hint, dataclasses.replace( hint_flow_tracking_data, reference_ids={}) self.visit(CodeElementCompoundAssertEq( ExprDeref( ExprCast(ExprIdentifier('retdata_ptr'), TypePointer(ret_struct_type))), ret_struct_ptr)) return (ExprConst(struct_def.size), ExprIdentifier('retdata_ptr'))
def test_deref_expr(): expr = parse_expr('[[fp - 7] + 3]') assert expr == \ ExprDeref( addr=ExprOperator( a=ExprDeref( addr=ExprOperator( a=ExprReg(reg=Register.FP), op='-', b=ExprConst(val=7))), op='+', b=ExprConst(val=3))) assert expr.format() == '[[fp - 7] + 3]'
def eval_reference(self, identifier_definition, var_name: str): pc_offset = self.tracer_data.get_pc_offset(self.pc) current_flow_tracking_data = \ self.tracer_data.program.debug_info.instruction_locations[pc_offset].flow_tracking_data try: substitute_transformer = SubstituteRegisterTransformer( ap=lambda location: ExprConst(val=self.ap, location=location), fp=lambda location: ExprConst(val=self.fp, location=location)) return self.visit(substitute_transformer.visit( identifier_definition.eval( reference_manager=self.tracer_data.program.reference_manager, flow_tracking_data=current_flow_tracking_data))) except FlowTrackingError: raise FlowTrackingError(f"Invalid reference '{var_name}'.")
def rewrite_ExprNeg(self, expr: ExprNeg, sim: SimplicityLevel): # Treat "-val" as "val * (-1)". return self.rewrite( ExprOperator(a=expr.val, op='*', b=ExprConst(val=-1, location=expr.location), location=expr.location), sim)
def test_from_expr(): assert RegChange.from_expr(5) == RegChangeKnown(5) assert RegChange.from_expr(RegChangeKnown(6)) == RegChangeKnown(6) assert RegChange.from_expr(ExprConst(7)) == RegChangeKnown(7) assert RegChange.from_expr(ExprIdentifier('asd')) == RegChangeUnknown() with pytest.raises(TypeError): RegChange.from_expr('wrong type')
def visit_ExprDeref(self, expr: ExprDeref) -> Expression: addr = self.visit(expr.addr) if not isinstance(addr, ExprConst): return expr assert self.prime is not None try: return ExprConst(val=self.memory[addr.val % self.prime], location=expr.location) except Exception as exc: raise ExpressionEvaluatorError(str(exc), location=expr.location)
def visit_ExprDot(self, expr: ExprDot) -> Tuple[ExprDeref, CairoType]: self.verify_identifier_manager_initialized(location=expr.location) inner_expr, inner_type = self.visit(expr.expr) if isinstance(inner_type, TypePointer): if not isinstance(inner_type.pointee, TypeStruct): raise CairoTypeError( f'Cannot apply dot-operator to pointer-to-non-struct type ' f"'{inner_type.format()}'.", location=expr.location) # Allow for . as ->, once. inner_type = inner_type.pointee elif isinstance(inner_type, TypeStruct): if isinstance(inner_expr, ExprTuple): raise CairoTypeError( 'Accessing struct members for r-value structs is not supported yet.', location=expr.location) # Get the address, to evaluate . as ->. inner_expr = get_expr_addr(inner_expr) else: raise CairoTypeError( f"Cannot apply dot-operator to non-struct type '{inner_type.format()}'.", location=expr.location) try: struct_def = get_struct_definition( struct_name=inner_type.resolved_scope, identifier_manager=self.identifiers) except Exception as exc: raise CairoTypeError(str(exc), location=expr.location) if expr.member.name not in struct_def.members: raise CairoTypeError( f"Member '{expr.member.name}' does not appear in definition of struct " f"'{inner_type.format()}'.", location=expr.location) member_definition = struct_def.members[expr.member.name] member_type = member_definition.cairo_type member_offset = member_definition.offset if member_offset == 0: simplified_expr = ExprDeref(addr=inner_expr, location=expr.location) else: mem_offset_expr = ExprConst(val=member_offset, location=expr.location) simplified_expr = ExprDeref(addr=ExprOperator( a=inner_expr, op='+', b=mem_offset_expr, location=expr.location), location=expr.location) return simplified_expr, member_type
def create_simple_ref_expr(reg: Register, offset: int, cairo_type: CairoType, location: Optional[Location]) -> Expression: """ Creates an expression of the form '[cast(reg + offset, cairo_type*)]'. """ return ExprDeref(addr=ExprCast(expr=ExprOperator( a=ExprReg(reg=reg, location=location), op='+', b=ExprConst(val=offset, location=location), location=location), dest_type=TypePointer(pointee=cairo_type, location=location), location=location), location=location)
def test_negative_numbers(): assert ExprConst(-1).format() == '-1' assert ExprNeg(val=ExprConst(val=1)).format() == '-1' assert ExprOperator(a=ExprConst(val=-1), op='+', b=ExprConst(val=-2)).format() == '(-1) + (-2)' assert ExprOperator( a=ExprNeg(val=ExprConst(val=1)), op='+', b=ExprNeg(val=ExprConst(val=2))).format() == '(-1) + (-2)'
def test_add_expr(): expr = parse_expr('[fp + 1] + [ap - x]') assert expr == \ ExprOperator( a=ExprDeref( addr=ExprOperator( a=ExprReg(reg=Register.FP), op='+', b=ExprConst(val=1))), op='+', b=ExprDeref( addr=ExprOperator( a=ExprReg(reg=Register.AP), op='-', b=ExprIdentifier(name='x')))) assert expr.format() == '[fp + 1] + [ap - x]' assert parse_expr('[ap-7]+37').format() == '[ap - 7] + 37'
def visit_ExprPow(self, expr: ExprPow): a = self.visit(expr.a) # The exponent must not be computed modulo prime (as b = c (mod prime) does not imply # a**b = a**c (mod prime)). no_prime_simplifier = type(self)(prime=None) b = no_prime_simplifier.visit(expr.b) if isinstance(a, ExprConst) and isinstance(b, ExprConst): if b.val < 0: raise SimplifierError( 'Power is not supported with a negative exponent.', location=expr.location) if self.prime is not None: val = pow(a.val, b.val, self.prime) else: val = a.val ** b.val return ExprConst(val=val, location=expr.location) return ExprPow(a=a, b=b, location=expr.location)
def eval( self, reference_manager: ReferenceManager, flow_tracking_data: FlowTrackingData) -> \ Expression: reference = flow_tracking_data.resolve_reference( reference_manager=reference_manager, name=self.parent.full_name) assert isinstance(flow_tracking_data, FlowTrackingDataActual), \ 'Resolved references can only come from FlowTrackingDataActual.' expr, expr_type = simplify_type_system(reference.eval(flow_tracking_data.ap_tracking)) for member_name in self.member_path.path: if isinstance(expr_type, TypeStruct): expr_type = expr_type.get_pointer_type() # In this case, take the address of the reference value. to_addr = lambda expr: ExprAddressOf(expr=expr) else: to_addr = lambda expr: expr if not isinstance(expr_type, TypePointer) or \ not isinstance(expr_type.pointee, TypeStruct): raise DefinitionError('Member access requires a type of the form Struct*.') qualified_member = expr_type.pointee.resolved_scope + member_name if qualified_member not in self.identifier_values: raise DefinitionError(f"Member '{qualified_member}' was not found.") member_definition = self.identifier_values[qualified_member] if not isinstance(member_definition, MemberDefinition): raise DefinitionError( f"Expected reference offset '{qualified_member}' to be a member, " f'found {member_definition.TYPE}.') offset_value = member_definition.offset expr_type = member_definition.cairo_type expr = ExprDeref(addr=ExprOperator(a=to_addr(expr), op='+', b=ExprConst(offset_value))) return ExprCast( expr=expr, dest_type=expr_type, )
def test_instruction(): # AssertEq. expr = parse_instruction('[ap] = [fp]; ap++') assert expr == \ InstructionAst( body=AssertEqInstruction( a=ExprDeref( addr=ExprReg(reg=Register.AP)), b=ExprDeref( addr=ExprReg(reg=Register.FP))), inc_ap=True) assert expr.format() == '[ap] = [fp]; ap++' assert parse_instruction( '[ap+5] = [fp]+[ap] - 5').format() == '[ap + 5] = [fp] + [ap] - 5' assert parse_instruction('[ap+5]+3= [fp]*7;ap ++ ').format() == \ '[ap + 5] + 3 = [fp] * 7; ap++' # Jump. expr = parse_instruction('jmp rel [ap] + x; ap++') assert expr == \ InstructionAst( body=JumpInstruction( val=ExprOperator( a=ExprDeref(addr=ExprReg(reg=Register.AP)), op='+', b=ExprIdentifier(name='x')), relative=True), inc_ap=True) assert expr.format() == 'jmp rel [ap] + x; ap++' assert parse_instruction(' jmp abs[ap]+x').format() == 'jmp abs [ap] + x' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('jmp abs') with pytest.raises(ParserError): parse_instruction('jmpabs[ap]') # JumpToLabel. expr = parse_instruction('jmp label') assert expr == \ InstructionAst( body=JumpToLabelInstruction( label=ExprIdentifier(name='label'), condition=None), inc_ap=False) assert expr.format() == 'jmp label' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('jmp [fp]') with pytest.raises(ParserError): parse_instruction('jmp 7') # Jnz. expr = parse_instruction('jmp rel [ap] + x if [fp + 3] != 0') assert expr == \ InstructionAst( body=JnzInstruction( jump_offset=ExprOperator( a=ExprDeref(addr=ExprReg(reg=Register.AP)), op='+', b=ExprIdentifier(name='x')), condition=ExprDeref( addr=ExprOperator( a=ExprReg(reg=Register.FP), op='+', b=ExprConst(val=3)))), inc_ap=False) assert expr.format() == 'jmp rel [ap] + x if [fp + 3] != 0' assert parse_instruction(' jmp rel 17 if[fp]!=0;ap++').format() == \ 'jmp rel 17 if [fp] != 0; ap++' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('jmprel 17 if x != 0') with pytest.raises(ParserError): parse_instruction('jmp 17 if x') with pytest.raises(ParserError, match='!= 0'): parse_instruction('jmp rel 17 if x != 2') with pytest.raises(ParserError): parse_instruction('jmp rel [fp] ifx != 0') # Jnz to label. expr = parse_instruction('jmp label if [fp] != 0') assert expr == \ InstructionAst( body=JumpToLabelInstruction( label=ExprIdentifier('label'), condition=ExprDeref(addr=ExprReg(reg=Register.FP))), inc_ap=False) assert expr.format() == 'jmp label if [fp] != 0' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('jmp [fp] if [fp] != 0') with pytest.raises(ParserError): parse_instruction('jmp 7 if [fp] != 0') # Call abs. expr = parse_instruction('call abs [fp] + x') assert expr == \ InstructionAst( body=CallInstruction( val=ExprOperator( a=ExprDeref(addr=ExprReg(reg=Register.FP)), op='+', b=ExprIdentifier(name='x')), relative=False), inc_ap=False) assert expr.format() == 'call abs [fp] + x' assert parse_instruction( 'call abs 17;ap++').format() == 'call abs 17; ap++' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('call abs') with pytest.raises(ParserError): parse_instruction('callabs 7') # Call rel. expr = parse_instruction('call rel [ap] + x') assert expr == \ InstructionAst( body=CallInstruction( val=ExprOperator( a=ExprDeref(addr=ExprReg(reg=Register.AP)), op='+', b=ExprIdentifier(name='x')), relative=True), inc_ap=False) assert expr.format() == 'call rel [ap] + x' assert parse_instruction( 'call rel 17;ap++').format() == 'call rel 17; ap++' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('call rel') with pytest.raises(ParserError): parse_instruction('callrel 7') # Call label. expr = parse_instruction('call label') assert expr == \ InstructionAst( body=CallLabelInstruction( label=ExprIdentifier(name='label')), inc_ap=False) assert expr.format() == 'call label' assert parse_instruction( 'call label ;ap++').format() == 'call label; ap++' # Make sure the following are not OK. with pytest.raises(ParserError): parse_instruction('call [fp]') with pytest.raises(ParserError): parse_instruction('call 7') # Ret. expr = parse_instruction('ret') assert expr == \ InstructionAst( body=RetInstruction(), inc_ap=False) assert expr.format() == 'ret' # AddAp. expr = parse_instruction('ap += [fp] + 2') assert expr == \ InstructionAst( body=AddApInstruction( expr=ExprOperator( a=ExprDeref( addr=ExprReg(reg=Register.FP)), op='+', b=ExprConst(val=2))), inc_ap=False) assert expr.format() == 'ap += [fp] + 2' assert parse_instruction('ap +=[ fp]+ 2').format() == 'ap += [fp] + 2' assert parse_instruction( 'ap +=[ fp]+ 2;ap ++').format() == 'ap += [fp] + 2; ap++'
def visit_ExprSubscript( self, expr: ExprSubscript) -> Tuple[Expression, CairoType]: inner_expr, inner_type = self.visit(expr.expr) offset_expr, offset_type = self.visit(expr.offset) if isinstance(inner_type, TypeTuple): self.verify_offset_is_felt(offset_type, offset_expr.location) offset_expr = ExpressionSimplifier().visit(offset_expr) if not isinstance(offset_expr, ExprConst): raise CairoTypeError( 'Subscript-operator for tuples supports only constant offsets, found ' f"'{type(offset_expr).__name__}'.", location=offset_expr.location) offset_value = offset_expr.val tuple_len = len(inner_type.members) if not 0 <= offset_value < tuple_len: raise CairoTypeError( f'Tuple index {offset_value} is out of range [0, {tuple_len}).', location=expr.location) item_type = inner_type.members[offset_value] if isinstance(inner_expr, ExprTuple): assert len(inner_expr.members.args) == tuple_len return ( # Take the inner item, but keep the original expression's location. dataclasses.replace( inner_expr.members.args[offset_value].expr, location=expr.location), item_type) elif isinstance(inner_expr, ExprDeref): # Handles pointers cast as tuples*, e.g. `[cast(ap, (felt, felt)*][0]`. addr = inner_expr.addr offset_in_felts = ExprConst(val=sum( map(self.get_size, inner_type.members[:offset_value])), location=offset_expr.location) addr_with_offset = ExprOperator(a=addr, op='+', b=offset_in_felts, location=expr.location) return ExprDeref(addr=addr_with_offset, location=expr.location), item_type else: raise CairoTypeError( 'Unexpected expression typed as TypeTuple. Expected ExprTuple or ExprDeref, ' f"found '{type(inner_expr).__name__}'.", location=expr.location) elif isinstance(inner_type, TypePointer): self.verify_offset_is_felt(offset_type, offset_expr.location) try: # If pointed type is struct, get_size could throw IdentifierErrors. We catch and # convert them to CairoTypeErrors. element_size = self.get_size(inner_type.pointee) except Exception as exc: raise CairoTypeError(str(exc), location=expr.location) element_size_expr = ExprConst(val=element_size, location=expr.location) modified_offset_expr = ExprOperator(a=offset_expr, op='*', b=element_size_expr, location=expr.location) simplified_expr = ExprDeref(addr=ExprOperator( a=inner_expr, op='+', b=modified_offset_expr, location=expr.location), location=expr.location) return simplified_expr, inner_type.pointee else: raise CairoTypeError( 'Cannot apply subscript-operator to non-pointer, non-tuple type ' f"'{inner_type.format()}'.", location=expr.location)
def visit_ExprNeg(self, expr: ExprNeg): val = self.visit(expr.val) if isinstance(val, ExprConst): return ExprConst( val=self._to_field_element(-val.val), location=expr.location) return ExprNeg(val=val, location=expr.location)
def atom_hex_number(self, value, meta): return ExprConst(val=int(value[0], 16), format_str=value[0].value, location=self.meta2loc(meta))
def visit_ExprConst(self, expr: ExprConst): return ExprConst(val=expr.val, format_str=expr.format_str, location=self.location_modifier(expr.location))
def visit_ExprOperator(self, expr: ExprOperator): a = self.visit(expr.a) b = self.visit(expr.b) op = expr.op if isinstance(b, ExprConst) and op == '/' and b.val == 0: raise SimplifierError('Division by zero.', location=b.location) if isinstance(a, ExprConst) and isinstance(b, ExprConst): val = None if op == '/' and self.prime is not None: if b.val % self.prime == 0: raise SimplifierError('Division by zero.', location=b.location) val = div_mod(a.val, b.val, self.prime) if op != '/': val = self._to_field_element(OPERATOR_DICT[op](a.val, b.val)) if val is not None: return ExprConst(val, location=expr.location) if isinstance(a, ExprConst) and op == '+': assert not isinstance(b, ExprConst) # Move constant expression to the right. E.g., "5 + fp" -> "fp + 5" a, b = b, a if isinstance(b, ExprConst) and op == '-': # Replace x - y with x + (-y) for constant y. op = '+' b = ExprConst(val=self._to_field_element(-b.val), location=b.location) if isinstance(b, ExprConst) and op == '/' and self.prime is not None: # Replace x / y with x * (1/y) for constant y. op = '*' if b.val % self.prime == 0: raise SimplifierError('Division by zero.', location=b.location) inv_val = div_mod(1, b.val, self.prime) b = ExprConst(val=self._to_field_element(inv_val), location=b.location) if isinstance(b, ExprConst) and b.val == 0 and op in ['+', '-']: # Replace x + 0 and x - 0 by x. return a if isinstance(b, ExprConst) and b.val == 1 and op in ['*', '/']: # Replace x * 1 and x / 1 by x. return a if isinstance(a, ExprConst) and a.val == 1 and op == '*': # Replace 1 * x by x. return b if isinstance(b, ExprConst) and isinstance(a, ExprOperator) and \ ((op == '+' and a.op in ['+', '-']) or (op == '*' and a.op == '*')): # If the expression is of the form "(a + b) + c" where c is constant, change it to # "a + (b + c)", this allows compiling expressions of the form: "[fp + x + y]". # Rotate right. return self.visit(ExprOperator( a=a.a, op=a.op, b=ExprOperator( a=a.b, op=a.op, b=b, location=expr.location), location=expr.location)) return ExprOperator(a=a, op=op, b=b, location=expr.location)
def visit_ExprConst(self, expr: ExprConst): return ExprConst( val=self._to_field_element(expr.val), location=expr.location)
def ap(location): return ExprOperator(ExprReg(reg=Register.AP, location=location), '-', ExprConst(val=diff, location=location), location=location)
def visit_ExprIdentifier(self, expr: ExprIdentifier) -> Expression: val = self.get_identifier_callback(expr) if isinstance(val, int): return ExprConst(val, location=expr.location) return val
def visit_ExprPyConst(self, expr: ExprPyConst): if self.prime is None: return expr val = eval(expr.code, {'PRIME': self.prime}, {}) return ExprConst(val=val, location=expr.location)
def atom_number(self, value, meta): return ExprConst(val=int(value[0]), location=self.meta2loc(meta))
def create_func_wrapper(self, elm: CodeElementFunction, func_alias_name: str): """ Generates a wrapper that converts between the StarkNet contract ABI and the Cairo calling convention. Arguments: elm - the CodeElementFunction of the wrapped function. func_alias_name - an alias for the FunctionDefention in the current scope. """ os_context = self.get_os_context() func_location = elm.identifier.location assert func_location is not None # We expect the call stack to look as follows: # pointer to os_context struct. # calldata size. # pointer to the call data array. # ret_fp. # ret_pc. os_context_ptr = ExprDeref( addr=ExprOperator( ExprReg(reg=Register.FP, location=func_location), '+', ExprConst(-5, location=func_location), location=func_location), location=func_location) calldata_size = ExprDeref( addr=ExprOperator( ExprReg(reg=Register.FP, location=func_location), '+', ExprConst(-4, location=func_location), location=func_location), location=func_location) calldata_ptr = ExprDeref( addr=ExprOperator( ExprReg(reg=Register.FP, location=func_location), '+', ExprConst(-3, location=func_location), location=func_location), location=func_location) implicit_arguments = None implicit_arguments_identifiers: Dict[str, TypedIdentifier] = {} if elm.implicit_arguments is not None: args = [] for typed_identifier in elm.implicit_arguments.identifiers: ptr_name = typed_identifier.name if ptr_name not in os_context: raise PreprocessorError( f"Unexpected implicit argument '{ptr_name}' in an external function.", location=typed_identifier.identifier.location) implicit_arguments_identifiers[ptr_name] = typed_identifier # Add the assignment expression 'ptr_name = ptr_name' to the implicit arg list. args.append(ExprAssignment( identifier=typed_identifier.identifier, expr=typed_identifier.identifier, location=typed_identifier.location, )) implicit_arguments = ArgList( args=args, notes=[], has_trailing_comma=True, location=elm.implicit_arguments.location) return_args_exprs: List[Expression] = [] # Create references. for ptr_name, index in os_context.items(): ref_name = self.current_scope + ptr_name arg_identifier = implicit_arguments_identifiers.get(ptr_name) if arg_identifier is None: location: Optional[Location] = func_location cairo_type: CairoType = TypeFelt(location=location) else: location = arg_identifier.location cairo_type = self.resolve_type(arg_identifier.get_type()) # Add a reference of the form # 'let ref_name = [cast(os_context_ptr + index, cairo_type*)]'. self.add_reference( name=ref_name, value=ExprDeref( addr=ExprCast( ExprOperator( os_context_ptr, '+', ExprConst(index, location=location), location=location), dest_type=TypePointer(pointee=cairo_type, location=cairo_type.location), location=cairo_type.location), location=location), cairo_type=cairo_type, location=location, require_future_definition=False) assert index == len(return_args_exprs), 'Unexpected index.' return_args_exprs.append(ExprIdentifier(name=ptr_name, location=func_location)) arg_struct_def = self.get_struct_definition( name=ScopedName.from_string(func_alias_name) + CodeElementFunction.ARGUMENT_SCOPE, location=func_location) code_elements, call_args = process_calldata( calldata_ptr=calldata_ptr, calldata_size=calldata_size, identifiers=self.identifiers, struct_def=arg_struct_def, has_range_check_builtin='range_check_ptr' in os_context, location=func_location, ) for code_element in code_elements: self.visit(code_element) self.visit(CodeElementFuncCall( func_call=RvalueFuncCall( func_ident=ExprIdentifier(name=func_alias_name, location=func_location), arguments=call_args, implicit_arguments=implicit_arguments, location=func_location))) ret_struct_name = ScopedName.from_string(func_alias_name) + CodeElementFunction.RETURN_SCOPE ret_struct_type = self.resolve_type(TypeStruct(ret_struct_name, False)) ret_struct_def = self.get_struct_definition( name=ret_struct_name, location=func_location) ret_struct_expr = create_simple_ref_expr( reg=Register.AP, offset=-ret_struct_def.size, cairo_type=ret_struct_type, location=func_location) self.add_reference( name=self.current_scope + 'ret_struct', value=ret_struct_expr, cairo_type=ret_struct_type, require_future_definition=False, location=func_location) # Add function return values. retdata_size, retdata_ptr = self.process_retdata( ret_struct_ptr=ExprIdentifier(name='ret_struct'), ret_struct_type=ret_struct_type, struct_def=ret_struct_def, location=func_location, ) return_args_exprs += [retdata_size, retdata_ptr] # Push the return values. self.push_compound_expressions( compound_expressions=[self.simplify_expr_as_felt(expr) for expr in return_args_exprs], location=func_location, ) # Add a ret instruction. self.visit(CodeElementInstruction( instruction=InstructionAst( body=RetInstruction(), inc_ap=False, location=func_location))) # Add an entry to the ABI. external_decorator = self.get_external_decorator(elm) assert external_decorator is not None is_view = external_decorator.name == 'view' if external_decorator.name == L1_HANDLER_DECORATOR: entry_type = 'l1_handler' elif external_decorator.name in [EXTERNAL_DECORATOR, VIEW_DECORATOR]: entry_type = 'function' else: raise NotImplementedError(f'Unsupported decorator {external_decorator.name}') entry_type = ( 'function' if external_decorator.name != L1_HANDLER_DECORATOR else L1_HANDLER_DECORATOR) self.add_abi_entry( name=elm.name, arg_struct_def=arg_struct_def, ret_struct_def=ret_struct_def, is_view=is_view, entry_type=entry_type)