def test_mapping_node_types(): with raises(Exception): MappingType(int, int) node1 = MappingType(BaseType("int128"), BaseType("int128")) node2 = MappingType(BaseType("int128"), BaseType("int128")) assert node1 == node2 assert str(node1) == "HashMap[int128, int128]"
def test_canonicalize_type(): # Non-basetype not allowed with raises(Exception): canonicalize_type(int) # List of byte arrays not allowed a = ListType(ByteArrayType(12), 2) with raises(Exception): canonicalize_type(a) # Test ABI format of multiple args. c = TupleType([BaseType("int128"), BaseType("address")]) assert canonicalize_type(c) == "(int128,address)"
def to_address(expr, args, kwargs, context): lll_node = [ "with", "_in_arg", args[0], ["seq", address_clamp("_in_arg"), "_in_arg"] ] return LLLnode.from_list(lll_node, typ=BaseType("address"), pos=getpos(expr))
def to_bool(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "Bytes": if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to bool", expr, ) else: num = byte_array_to_num(in_arg, expr, "uint256") return LLLnode.from_list(["iszero", ["iszero", num]], typ=BaseType("bool"), pos=getpos(expr)) else: return LLLnode.from_list(["iszero", ["iszero", in_arg]], typ=BaseType("bool"), pos=getpos(expr))
def keccak256_helper(expr, args, kwargs, context): sub = args[0] # Can hash literals if isinstance(sub, bytes): return LLLnode.from_list( bytes_to_int(keccak256(sub)), typ=BaseType("bytes32"), pos=getpos(expr) ) # Can hash bytes32 objects if is_base_type(sub.typ, "bytes32"): return LLLnode.from_list( [ "seq", ["mstore", MemoryPositions.FREE_VAR_SPACE, sub], ["sha3", MemoryPositions.FREE_VAR_SPACE, 32], ], typ=BaseType("bytes32"), pos=getpos(expr), ) # Copy the data to an in-memory array if sub.location == "memory": # If we are hashing a value in memory, no need to copy it, just hash in-place return LLLnode.from_list( ["with", "_sub", sub, ["sha3", ["add", "_sub", 32], ["mload", "_sub"]]], typ=BaseType("bytes32"), pos=getpos(expr), ) elif sub.location == "storage": lengetter = LLLnode.from_list(["sload", "_sub"], typ=BaseType("int128")) else: # This should never happen, but just left here for future compiler-writers. raise Exception(f"Unsupported location: {sub.location}") # pragma: no test placeholder = context.new_internal_variable(sub.typ) placeholder_node = LLLnode.from_list(placeholder, typ=sub.typ, location="memory") copier = make_byte_array_copier( placeholder_node, LLLnode.from_list("_sub", typ=sub.typ, location=sub.location), ) return LLLnode.from_list( ["with", "_sub", sub, ["seq", copier, ["sha3", ["add", placeholder, 32], lengetter]]], typ=BaseType("bytes32"), pos=getpos(expr), )
def test_bytearray_node_type(): node1 = ByteArrayType(12) node2 = ByteArrayType(12) assert node1 == node2 node3 = ByteArrayType(13) node4 = BaseType("int128") assert node1 != node3 assert node1 != node4
def to_bytes32(expr, args, kwargs, context): in_arg = args[0] input_type, _len = get_type(in_arg) if input_type == "Bytes": if _len > 32: raise TypeMismatch( f"Unable to convert bytes[{_len}] to bytes32, max length is too " "large.") if in_arg.location == "memory": return LLLnode.from_list(["mload", ["add", in_arg, 32]], typ=BaseType("bytes32")) elif in_arg.location == "storage": return LLLnode.from_list(["sload", ["add", in_arg, 1]], typ=BaseType("bytes32")) else: return LLLnode(value=in_arg.value, args=in_arg.args, typ=BaseType("bytes32"), pos=getpos(expr))
def byte_array_to_num( arg, expr, out_type, offset=32, ): if arg.location == "memory": lengetter = LLLnode.from_list(["mload", "_sub"], typ=BaseType("int256")) first_el_getter = LLLnode.from_list(["mload", ["add", 32, "_sub"]], typ=BaseType("int256")) elif arg.location == "storage": lengetter = LLLnode.from_list(["sload", "_sub"], typ=BaseType("int256")) first_el_getter = LLLnode.from_list(["sload", ["add", 1, "_sub"]], typ=BaseType("int256")) if out_type == "int128": result = int128_clamp( ["div", "_el1", ["exp", 256, ["sub", 32, "_len"]]]) elif out_type in ("int256", "uint256"): result = ["div", "_el1", ["exp", 256, ["sub", offset, "_len"]]] return LLLnode.from_list( [ "with", "_sub", arg, [ "with", "_el1", first_el_getter, ["with", "_len", ["clamp", 0, lengetter, 32], result], ], ], typ=BaseType(out_type), annotation=f"bytearray to number ({out_type})", )
def from_list( cls, obj: Any, typ: "BaseType" = None, location: str = None, pos: Tuple[int, int] = None, annotation: Optional[str] = None, mutable: bool = True, add_gas_estimate: int = 0, valency: Optional[int] = None, ) -> "LLLnode": if isinstance(typ, str): typ = BaseType(typ) if isinstance(obj, LLLnode): # note: this modify-and-returnclause is a little weird since # the input gets modified. CC 20191121. if typ is not None: obj.typ = typ if obj.pos is None: obj.pos = pos if obj.location is None: obj.location = location return obj elif not isinstance(obj, list): return cls( obj, [], typ, location=location, pos=pos, annotation=annotation, mutable=mutable, add_gas_estimate=add_gas_estimate, ) else: return cls( obj[0], [cls.from_list(o, pos=pos) for o in obj[1:]], typ, location=location, pos=pos, annotation=annotation, mutable=mutable, add_gas_estimate=add_gas_estimate, valency=valency, )
def to_uint256(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "num_literal": if isinstance(in_arg, int): if not SizeLimits.in_bounds("uint256", in_arg): raise InvalidLiteral(f"Number out of range: {in_arg}") return LLLnode.from_list(in_arg, typ=BaseType("uint256", ), pos=getpos(expr)) elif isinstance(in_arg, Decimal): if not SizeLimits.in_bounds("uint256", math.trunc(in_arg)): raise InvalidLiteral( f"Number out of range: {math.trunc(in_arg)}") return LLLnode.from_list(math.trunc(in_arg), typ=BaseType("uint256"), pos=getpos(expr)) else: raise InvalidLiteral(f"Unknown numeric literal type: {in_arg}") elif isinstance(in_arg, LLLnode) and input_type in ("int128", "int256"): return LLLnode.from_list(["clampge", in_arg, 0], typ=BaseType("uint256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type == "decimal": return LLLnode.from_list( ["div", ["clampge", in_arg, 0], DECIMAL_DIVISOR], typ=BaseType("uint256"), pos=getpos(expr), ) elif isinstance(in_arg, LLLnode) and input_type == "bool": return LLLnode.from_list(in_arg, typ=BaseType("uint256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type in ("bytes32", "address"): return LLLnode(value=in_arg.value, args=in_arg.args, typ=BaseType("uint256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type == "Bytes": if in_arg.typ.maxlen > 32: raise InvalidLiteral( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to uint256", expr, ) return byte_array_to_num(in_arg, expr, "uint256") else: raise InvalidLiteral(f"Invalid input for uint256: {in_arg}", expr)
def test_get_size_of_type(): assert get_size_of_type(BaseType("int128")) == 1 assert get_size_of_type(ByteArrayType(12)) == 3 assert get_size_of_type(ByteArrayType(33)) == 4 assert get_size_of_type(ListType(BaseType("int128"), 10)) == 10 _tuple = TupleType([BaseType("int128"), BaseType("decimal")]) assert get_size_of_type(_tuple) == 2 _struct = StructType({ "a": BaseType("int128"), "b": BaseType("decimal") }, "Foo") assert get_size_of_type(_struct) == 2 # Don't allow unknown types. with raises(Exception): get_size_of_type(int) # Maps are not supported for function arguments or outputs with raises(Exception): get_size_of_type(MappingType(BaseType("int128"), BaseType("int128")))
def to_int128(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "num_literal": if isinstance(in_arg, int): if not SizeLimits.in_bounds("int128", in_arg): raise InvalidLiteral(f"Number out of range: {in_arg}") return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) elif isinstance(in_arg, Decimal): if not SizeLimits.in_bounds("int128", math.trunc(in_arg)): raise InvalidLiteral( f"Number out of range: {math.trunc(in_arg)}") return LLLnode.from_list(math.trunc(in_arg), typ=BaseType("int128"), pos=getpos(expr)) else: raise InvalidLiteral(f"Unknown numeric literal type: {in_arg}") elif input_type in ("bytes32", "int256"): if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", in_arg.value): raise InvalidLiteral(f"Number out of range: {in_arg.value}", expr) else: return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) else: return LLLnode.from_list( int128_clamp(in_arg), typ=BaseType("int128"), pos=getpos(expr), ) elif input_type == "address": return LLLnode.from_list( ["signextend", 15, ["and", in_arg, (SizeLimits.ADDRSIZE - 1)]], typ=BaseType("int128"), pos=getpos(expr), ) elif input_type in ("String", "Bytes"): if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to int128", expr, ) return byte_array_to_num(in_arg, expr, "int128") elif input_type == "uint256": if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", in_arg.value): raise InvalidLiteral(f"Number out of range: {in_arg.value}", expr) else: return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) else: return LLLnode.from_list( ["uclample", in_arg, ["mload", MemoryPositions.MAX_INT128]], typ=BaseType("int128"), pos=getpos(expr), ) elif input_type == "decimal": return LLLnode.from_list( int128_clamp(["sdiv", in_arg, DECIMAL_DIVISOR]), typ=BaseType("int128"), pos=getpos(expr), ) elif input_type == "bool": return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) else: raise InvalidLiteral(f"Invalid input for int128: {in_arg}", expr)
def to_int256(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "num_literal": if isinstance(in_arg, int): if not SizeLimits.in_bounds("int256", in_arg): raise InvalidLiteral(f"Number out of range: {in_arg}") return LLLnode.from_list(in_arg, typ=BaseType("int256", ), pos=getpos(expr)) elif isinstance(in_arg, Decimal): if not SizeLimits.in_bounds("int256", math.trunc(in_arg)): raise InvalidLiteral( f"Number out of range: {math.trunc(in_arg)}") return LLLnode.from_list(math.trunc(in_arg), typ=BaseType("int256"), pos=getpos(expr)) else: raise InvalidLiteral(f"Unknown numeric literal type: {in_arg}") elif isinstance(in_arg, LLLnode) and input_type == "int128": return LLLnode.from_list(in_arg, typ=BaseType("int256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type == "uint256": if version_check(begin="constantinople"): upper_bound = ["shl", 255, 1] else: upper_bound = -(2**255) return LLLnode.from_list(["uclamplt", in_arg, upper_bound], typ=BaseType("int256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type == "decimal": return LLLnode.from_list( ["sdiv", in_arg, DECIMAL_DIVISOR], typ=BaseType("int256"), pos=getpos(expr), ) elif isinstance(in_arg, LLLnode) and input_type == "bool": return LLLnode.from_list(in_arg, typ=BaseType("int256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type in ("bytes32", "address"): return LLLnode(value=in_arg.value, args=in_arg.args, typ=BaseType("int256"), pos=getpos(expr)) elif isinstance(in_arg, LLLnode) and input_type in ("Bytes", "String"): if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to int256", expr, ) return byte_array_to_num(in_arg, expr, "int256") else: raise InvalidLiteral(f"Invalid input for int256: {in_arg}", expr)
def test_tuple_node_types(): node1 = TupleType([BaseType("int128"), BaseType("decimal")]) node2 = TupleType([BaseType("int128"), BaseType("decimal")]) assert node1 == node2 assert str(node1) == "(int128, decimal)"
def to_decimal(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "Bytes": if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to decimal", expr, ) num = byte_array_to_num(in_arg, expr, "int128") return LLLnode.from_list(["mul", num, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: if input_type == "uint256": if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteral( f"Number out of range: {in_arg.value}", expr, ) else: return LLLnode.from_list(["mul", in_arg, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: return LLLnode.from_list( [ "uclample", ["mul", in_arg, DECIMAL_DIVISOR], ["mload", MemoryPositions.MAXDECIMAL], ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type == "address": return LLLnode.from_list( [ "mul", [ "signextend", 15, ["and", in_arg, (SizeLimits.ADDRSIZE - 1)] ], DECIMAL_DIVISOR, ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type == "bytes32": if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteral( f"Number out of range: {in_arg.value}", expr, ) else: return LLLnode.from_list(["mul", in_arg, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: return LLLnode.from_list( [ "clamp", ["mload", MemoryPositions.MINDECIMAL], ["mul", in_arg, DECIMAL_DIVISOR], ["mload", MemoryPositions.MAXDECIMAL], ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type == "int256": return LLLnode.from_list( [ "seq", int128_clamp(in_arg), ["mul", in_arg, DECIMAL_DIVISOR] ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type in ("int128", "bool"): return LLLnode.from_list(["mul", in_arg, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: raise InvalidLiteral(f"Invalid input for decimal: {in_arg}", expr)
def internal_memory_scope(self): if not self._mock_vars: for i in range(20): self._new_variable(f"#mock{i}", BaseType(self._size), self._size, bool(i % 2)) self._mock_vars = True return super().internal_memory_scope()
def make_call(stmt_expr, context): # ** Internal Call ** # Steps: # (x) push current local variables # (x) push arguments # (x) push jumpdest (callback ptr) # (x) jump to label # (x) pop return values # (x) pop local variables pop_local_vars = [] push_local_vars = [] pop_return_values = [] push_args = [] method_name = stmt_expr.func.attr # TODO check this out from vyper.old_codegen.expr import parse_sequence pre_init, expr_args = parse_sequence(stmt_expr, stmt_expr.args, context) sig = FunctionSignature.lookup_sig( context.sigs, method_name, expr_args, stmt_expr, context, ) if context.is_constant() and sig.mutability not in ("view", "pure"): raise StateAccessViolation( f"May not call state modifying function " f"'{method_name}' within {context.pp_constancy()}.", getpos(stmt_expr), ) if not sig.internal: raise StructureException("Cannot call external functions via 'self'", stmt_expr) # Push local variables. var_slots = [(v.pos, v.size) for name, v in context.vars.items() if v.location == "memory"] if var_slots: var_slots.sort(key=lambda x: x[0]) if len(var_slots) > 10: # if memory is large enough, push and pop it via iteration mem_from, mem_to = var_slots[0][ 0], var_slots[-1][0] + var_slots[-1][1] * 32 i_placeholder = context.new_internal_variable(BaseType("uint256")) local_save_ident = f"_{stmt_expr.lineno}_{stmt_expr.col_offset}" push_loop_label = "save_locals_start" + local_save_ident pop_loop_label = "restore_locals_start" + local_save_ident push_local_vars = [ ["mstore", i_placeholder, mem_from], ["label", push_loop_label], ["mload", ["mload", i_placeholder]], [ "mstore", i_placeholder, ["add", ["mload", i_placeholder], 32] ], [ "if", ["lt", ["mload", i_placeholder], mem_to], ["goto", push_loop_label] ], ] pop_local_vars = [ ["mstore", i_placeholder, mem_to - 32], ["label", pop_loop_label], ["mstore", ["mload", i_placeholder], "pass"], [ "mstore", i_placeholder, ["sub", ["mload", i_placeholder], 32] ], [ "if", ["ge", ["mload", i_placeholder], mem_from], ["goto", pop_loop_label] ], ] else: # for smaller memory, hardcode the mload/mstore locations push_mem_slots = [] for pos, size in var_slots: push_mem_slots.extend([pos + i * 32 for i in range(size)]) push_local_vars = [["mload", pos] for pos in push_mem_slots] pop_local_vars = [["mstore", pos, "pass"] for pos in push_mem_slots[::-1]] # Push Arguments if expr_args: inargs, inargsize, arg_pos = pack_arguments(sig, expr_args, context, stmt_expr, is_external_call=False) push_args += [ inargs ] # copy arguments first, to not mess up the push/pop sequencing. static_arg_size = 32 * sum( [get_static_size_of_type(arg.typ) for arg in expr_args]) static_pos = int(arg_pos + static_arg_size) needs_dyn_section = any( [has_dynamic_data(arg.typ) for arg in expr_args]) if needs_dyn_section: ident = f"push_args_{sig.method_id}_{stmt_expr.lineno}_{stmt_expr.col_offset}" start_label = ident + "_start" end_label = ident + "_end" i_placeholder = context.new_internal_variable(BaseType("uint256")) # Calculate copy start position. # Given | static | dynamic | section in memory, # copy backwards so the values are in order on the stack. # We calculate i, the end of the whole encoded part # (i.e. the starting index for copy) # by taking ceil32(len<arg>) + offset<arg> + arg_pos # for the last dynamic argument and arg_pos is the start # the whole argument section. idx = 0 for arg in expr_args: if isinstance(arg.typ, ByteArrayLike): last_idx = idx idx += get_static_size_of_type(arg.typ) push_args += [[ "with", "offset", ["mload", arg_pos + last_idx * 32], [ "with", "len_pos", ["add", arg_pos, "offset"], [ "with", "len_value", ["mload", "len_pos"], [ "mstore", i_placeholder, ["add", "len_pos", ["ceil32", "len_value"]] ], ], ], ]] # loop from end of dynamic section to start of dynamic section, # pushing each element onto the stack. push_args += [ ["label", start_label], [ "if", ["lt", ["mload", i_placeholder], static_pos], ["goto", end_label] ], ["mload", ["mload", i_placeholder]], [ "mstore", i_placeholder, ["sub", ["mload", i_placeholder], 32] ], # decrease i ["goto", start_label], ["label", end_label], ] # push static section push_args += [["mload", pos] for pos in reversed(range(arg_pos, static_pos, 32))] elif sig.args: raise StructureException( f"Wrong number of args for: {sig.name} (0 args given, expected {len(sig.args)})", stmt_expr, ) # Jump to function label. jump_to_func = [ ["add", ["pc"], 6], # set callback pointer. ["goto", f"priv_{sig.method_id}"], ["jumpdest"], ] # Pop return values. returner = [0] if sig.output_type: output_placeholder, returner, output_size = _call_make_placeholder( stmt_expr, context, sig) if output_size > 0: dynamic_offsets = [] if isinstance(sig.output_type, (BaseType, ListType)): pop_return_values = [[ "mstore", ["add", output_placeholder, pos], "pass" ] for pos in range(0, output_size, 32)] elif isinstance(sig.output_type, ByteArrayLike): dynamic_offsets = [(0, sig.output_type)] pop_return_values = [ ["pop", "pass"], ] elif isinstance(sig.output_type, TupleLike): static_offset = 0 pop_return_values = [] for name, typ in sig.output_type.tuple_items(): if isinstance(typ, ByteArrayLike): pop_return_values.append([ "mstore", ["add", output_placeholder, static_offset], "pass" ]) dynamic_offsets.append(([ "mload", ["add", output_placeholder, static_offset] ], name)) static_offset += 32 else: member_output_size = get_size_of_type(typ) * 32 pop_return_values.extend([[ "mstore", ["add", output_placeholder, pos], "pass" ] for pos in range(static_offset, static_offset + member_output_size, 32)]) static_offset += member_output_size # append dynamic unpacker. dyn_idx = 0 for in_memory_offset, _out_type in dynamic_offsets: ident = f"{stmt_expr.lineno}_{stmt_expr.col_offset}_arg_{dyn_idx}" dyn_idx += 1 start_label = "dyn_unpack_start_" + ident end_label = "dyn_unpack_end_" + ident i_placeholder = context.new_internal_variable( typ=BaseType("uint256")) begin_pos = ["add", output_placeholder, in_memory_offset] # loop until length. o = LLLnode.from_list( [ "seq_unchecked", ["mstore", begin_pos, "pass"], # get len ["mstore", i_placeholder, 0], ["label", start_label], [ # break "if", [ "ge", ["mload", i_placeholder], ["ceil32", ["mload", begin_pos]] ], ["goto", end_label], ], [ # pop into correct memory slot. "mstore", [ "add", ["add", begin_pos, 32], ["mload", i_placeholder] ], "pass", ], # increment i [ "mstore", i_placeholder, ["add", 32, ["mload", i_placeholder]] ], ["goto", start_label], ["label", end_label], ], typ=None, annotation="dynamic unpacker", pos=getpos(stmt_expr), ) pop_return_values.append(o) call_body = list( itertools.chain( ["seq_unchecked"], pre_init, push_local_vars, push_args, jump_to_func, pop_return_values, pop_local_vars, [returner], )) # If we have no return, we need to pop off pop_returner_call_body = ["pop", call_body ] if sig.output_type is None else call_body o = LLLnode.from_list( pop_returner_call_body, typ=sig.output_type, location="memory", pos=getpos(stmt_expr), annotation=f"Internal Call: {method_name}", add_gas_estimate=sig.gas, ) o.gas += sig.gas return o
def get_length(arg): if arg.location == "memory": return LLLnode.from_list(["mload", arg], typ=BaseType("uint256")) elif arg.location == "storage": return LLLnode.from_list(["sload", arg], typ=BaseType("uint256"))
def make_return_stmt(stmt, context, begin_pos, _size, loop_memory_position=None): # TODO check this out func_type = stmt.get_ancestor(vy_ast.FunctionDef)._metadata["type"] _, nonreentrant_post = get_nonreentrant_lock(func_type) if context.is_internal: if loop_memory_position is None: loop_memory_position = context.new_internal_variable( BaseType("uint256")) # Make label for stack push loop. label_id = "_".join([ str(x) for x in (context.method_id, stmt.lineno, stmt.col_offset) ]) exit_label = f"make_return_loop_exit_{label_id}" start_label = f"make_return_loop_start_{label_id}" # Push prepared data onto the stack, # in reverse order so it can be popped of in order. if isinstance(begin_pos, int) and isinstance(_size, int): # static values, unroll the mloads instead. mloads = [["mload", pos] for pos in range(begin_pos, _size, 32)] else: mloads = [ "seq_unchecked", ["mstore", loop_memory_position, _size], ["label", start_label], [ # maybe exit loop / break. "if", ["le", ["mload", loop_memory_position], 0], ["goto", exit_label], ], [ # push onto stack "mload", [ "add", begin_pos, ["sub", ["mload", loop_memory_position], 32] ], ], [ # decrement i by 32. "mstore", loop_memory_position, ["sub", ["mload", loop_memory_position], 32], ], ["goto", start_label], ["label", exit_label], ] # if we are in a for loop, we have to exit prior to returning exit_repeater = ["exit_repeater"] if context.forvars else [] return (["seq_unchecked"] + exit_repeater + mloads + nonreentrant_post + [["jump", ["mload", context.callback_ptr]]]) else: return ["seq_unchecked" ] + nonreentrant_post + [["return", begin_pos, _size]]