Beispiel #1
0
    def get_target(self, target):
        if isinstance(target, ast.Subscript) and self.context.in_for_loop:  # Check if we are doing assignment of an iteration loop.
            raise_exception = False
            if isinstance(target.value, ast.Attribute):
                list_name = "%s.%s" % (target.value.value.id, target.value.attr)
                if list_name in self.context.in_for_loop:
                    raise_exception = True

            if isinstance(target.value, ast.Name) and \
               target.value.id in self.context.in_for_loop:
                list_name = target.value.id
                raise_exception = True

            if raise_exception:
                raise StructureException("Altering list '%s' which is being iterated!" % list_name, self.stmt)

        if isinstance(target, ast.Name) and target.id in self.context.forvars:
            raise StructureException("Altering iterator '%s' which is in use!" % target.id, self.stmt)
        if isinstance(target, ast.Tuple):
            return Expr(target, self.context).lll_node
        target = Expr.parse_variable_location(target, self.context)
        if target.location == 'storage' and self.context.is_constant:
            raise ConstancyViolationException("Cannot modify storage inside a constant function: %s" % target.annotation)
        if not target.mutable:
            raise ConstancyViolationException("Cannot modify function argument: %s" % target.annotation)
        return target
Beispiel #2
0
def process_arg(index, arg, expected_arg_typelist, function_name, context):
    if isinstance(expected_arg_typelist, Optional):
        expected_arg_typelist = expected_arg_typelist.typ
    if not isinstance(expected_arg_typelist, tuple):
        expected_arg_typelist = (expected_arg_typelist, )
    vsub = None
    for expected_arg in expected_arg_typelist:
        if expected_arg == 'num_literal':
            if isinstance(arg, ast.Num) and get_original_if_0_prefixed(
                    arg, context) is None:
                return arg.n
        elif expected_arg == 'str_literal':
            if isinstance(arg, ast.Str) and get_original_if_0_prefixed(
                    arg, context) is None:
                bytez = b''
                for c in arg.s:
                    if ord(c) >= 256:
                        raise InvalidLiteralException(
                            "Cannot insert special character %r into byte array"
                            % c, arg)
                    bytez += bytes([ord(c)])
                return bytez
        elif expected_arg == 'name_literal':
            if isinstance(arg, ast.Name):
                return arg.id
            elif isinstance(arg, ast.Subscript) and arg.value.id == 'bytes':
                return 'bytes[%s]' % arg.slice.value.n
        elif expected_arg == '*':
            return arg
        elif expected_arg == 'bytes':
            sub = Expr(arg, context).lll_node
            if isinstance(sub.typ, ByteArrayType):
                return sub
        else:
            # Does not work for unit-endowed types inside compound types, e.g. timestamp[2]
            parsed_expected_type = parse_type(
                ast.parse(expected_arg).body[0].value, 'memory')
            if isinstance(parsed_expected_type, BaseType):
                vsub = vsub or Expr.parse_value_expr(arg, context)
                if is_base_type(vsub.typ, expected_arg):
                    return vsub
                elif expected_arg in ('int128', 'uint256') and isinstance(vsub.typ, BaseType) and \
                     vsub.typ.is_literal and SizeLimits.in_bounds(expected_arg, vsub.value):
                    return vsub
            else:
                vsub = vsub or Expr(arg, context).lll_node
                if vsub.typ == parsed_expected_type:
                    return Expr(arg, context).lll_node
    if len(expected_arg_typelist) == 1:
        raise TypeMismatchException(
            "Expecting %s for argument %r of %s" %
            (expected_arg, index, function_name), arg)
    else:
        raise TypeMismatchException(
            "Expecting one of %r for argument %r of %s" %
            (expected_arg_typelist, index, function_name), arg)
        return arg.id
Beispiel #3
0
 def aug_assign(self):
     target = self.get_target(self.stmt.target)
     sub = Expr.parse_value_expr(self.stmt.value, self.context)
     if not isinstance(self.stmt.op, (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod)):
         raise Exception("Unsupported operator for augassign")
     if not isinstance(target.typ, BaseType):
         raise TypeMismatchException("Can only use aug-assign operators with simple types!", self.stmt.target)
     if target.location == 'storage':
         o = Expr.parse_value_expr(ast.BinOp(left=LLLnode.from_list(['sload', '_stloc'], typ=target.typ, pos=target.pos),
                                 right=sub, op=self.stmt.op, lineno=self.stmt.lineno, col_offset=self.stmt.col_offset), self.context)
         return LLLnode.from_list(['with', '_stloc', target, ['sstore', '_stloc', base_type_conversion(o, o.typ, target.typ, pos=getpos(self.stmt))]], typ=None, pos=getpos(self.stmt))
     elif target.location == 'memory':
         o = Expr.parse_value_expr(ast.BinOp(left=LLLnode.from_list(['mload', '_mloc'], typ=target.typ, pos=target.pos),
                                 right=sub, op=self.stmt.op, lineno=self.stmt.lineno, col_offset=self.stmt.col_offset), self.context)
         return LLLnode.from_list(['with', '_mloc', target, ['mstore', '_mloc', base_type_conversion(o, o.typ, target.typ, pos=getpos(self.stmt))]], typ=None, pos=getpos(self.stmt))
Beispiel #4
0
def call_lookup_specs(stmt_expr, context):
    from ophydia.parser.expr import Expr
    method_name = stmt_expr.func.attr
    expr_args = [Expr(arg, context).lll_node for arg in stmt_expr.args]
    sig = FunctionSignature.lookup_sig(context.sigs, method_name, expr_args,
                                       stmt_expr, context)
    return method_name, expr_args, sig
Beispiel #5
0
    def call(self):
        from .parser import (
            pack_logging_data,
            pack_logging_topics,
            external_contract_call,
        )
        if isinstance(self.stmt.func, ast.Name):
            if self.stmt.func.id in stmt_dispatch_table:
                return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context)
            elif self.stmt.func.id in dispatch_table:
                raise StructureException("Function {} can not be called without being used.".format(self.stmt.func.id), self.stmt)
            else:
                raise StructureException("Unknown function: '{}'.".format(self.stmt.func.id), self.stmt)
        elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self":
            return self_call.make_call(self.stmt, self.context)
        elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Call):
            contract_name = self.stmt.func.value.func.id
            contract_address = Expr.parse_value_expr(self.stmt.func.value.args[0], self.context)
            return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt))
        elif isinstance(self.stmt.func.value, ast.Attribute) and self.stmt.func.value.attr in self.context.sigs:
            contract_name = self.stmt.func.value.attr
            var = self.context.globals[self.stmt.func.value.attr]
            contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr))
            return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt))
        elif isinstance(self.stmt.func.value, ast.Attribute) and self.stmt.func.value.attr in self.context.globals:
            contract_name = self.context.globals[self.stmt.func.value.attr].typ.unit
            var = self.context.globals[self.stmt.func.value.attr]
            contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr))
            return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt))
        elif isinstance(self.stmt.func, ast.Attribute) and self.stmt.func.value.id == 'log':
            if self.stmt.func.attr not in self.context.sigs['self']:
                raise EventDeclarationException("Event not declared yet: %s" % self.stmt.func.attr)
            event = self.context.sigs['self'][self.stmt.func.attr]
            if len(event.indexed_list) != len(self.stmt.args):
                raise EventDeclarationException("%s received %s arguments but expected %s" % (event.name, len(self.stmt.args), len(event.indexed_list)))
            expected_topics, topics = [], []
            expected_data, data = [], []
            for pos, is_indexed in enumerate(event.indexed_list):
                if is_indexed:
                    expected_topics.append(event.args[pos])
                    topics.append(self.stmt.args[pos])
                else:
                    expected_data.append(event.args[pos])
                    data.append(self.stmt.args[pos])
            topics = pack_logging_topics(event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt))
            inargs, inargsize, inargsize_node, inarg_start = pack_logging_data(expected_data, data, self.context, pos=getpos(self.stmt))

            if inargsize_node is None:
                sz = inargsize
            else:
                sz = ['mload', inargsize_node]

            return LLLnode.from_list(['seq', inargs,
                LLLnode.from_list(["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10)], typ=None, pos=getpos(self.stmt))
        else:
            raise StructureException("Unsupported operator: %r" % ast.dump(self.stmt), self.stmt)
Beispiel #6
0
def _slice(expr, args, kwargs, context):
    sub, start, length = args[0], kwargs['start'], kwargs['len']
    if not are_units_compatible(start.typ, BaseType('int128')):
        raise TypeMismatchException(
            "Type for slice start index must be a unitless number")
    # Expression representing the length of the slice
    if not are_units_compatible(length.typ, BaseType('int128')):
        raise TypeMismatchException(
            "Type for slice length must be a unitless number")
    # Node representing the position of the output in memory
    np = context.new_placeholder(ByteArrayType(maxlen=sub.typ.maxlen + 32))
    placeholder_node = LLLnode.from_list(np, typ=sub.typ, location='memory')
    placeholder_plus_32_node = LLLnode.from_list(np + 32,
                                                 typ=sub.typ,
                                                 location='memory')
    # Copies over bytearray data
    if sub.location == 'storage':
        adj_sub = LLLnode.from_list(
            ['add', ['sha3_32', sub], ['add', ['div', '_start', 32], 1]],
            typ=sub.typ,
            location=sub.location)
    else:
        adj_sub = LLLnode.from_list([
            'add', sub, ['add', ['sub', '_start', ['mod', '_start', 32]], 32]
        ],
                                    typ=sub.typ,
                                    location=sub.location)
    copier = make_byte_slice_copier(placeholder_plus_32_node,
                                    adj_sub, ['add', '_length', 32],
                                    sub.typ.maxlen,
                                    pos=getpos(expr))
    # New maximum length in the type of the result
    newmaxlen = length.value if not len(length.args) else sub.typ.maxlen
    maxlen = ['mload',
              Expr(sub,
                   context=context).lll_node]  # Retrieve length of the bytes.
    out = [
        'with', '_start', start,
        [
            'with', '_length', length,
            [
                'with', '_opos',
                ['add', placeholder_node, ['mod', '_start', 32]],
                [
                    'seq',
                    ['assert', ['le', ['add', '_start', '_length'], maxlen]],
                    copier, ['mstore', '_opos', '_length'], '_opos'
                ]
            ]
        ]
    ]
    return LLLnode.from_list(out,
                             typ=ByteArrayType(newmaxlen),
                             location='memory',
                             pos=getpos(expr))
Beispiel #7
0
 def parse_assert(self):
     test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
     if not self.is_bool_expr(test_expr):
         raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test)
     if self.stmt.msg:
         if len(self.stmt.msg.s.strip()) == 0:
             raise StructureException('Empty reason string not allowed.', self.stmt)
         reason_str = self.stmt.msg.s.strip()
         sig_placeholder = self.context.new_placeholder(BaseType(32))
         arg_placeholder = self.context.new_placeholder(BaseType(32))
         reason_str_type = ByteArrayType(len(reason_str))
         placeholder_bytes = Expr(self.stmt.msg, self.context).lll_node
         method_id = fourbytes_to_int(sha3(b"Error(string)")[:4])
         assert_reason = \
             ['seq',
                 ['mstore', sig_placeholder, method_id],
                 ['mstore', arg_placeholder, 32],
                 placeholder_bytes,
                 ['assert_reason', test_expr, int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32)]]
         return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
     else:
         return LLLnode.from_list(['assert', test_expr], typ=None, pos=getpos(self.stmt))
Beispiel #8
0
    def parse_delete(self):
        from .parser import (
            make_setter,
        )
        if len(self.stmt.targets) != 1:
            raise StructureException("Can delete one variable at a time", self.stmt)
        target = self.stmt.targets[0]
        target_lll = Expr(self.stmt.targets[0], self.context).lll_node

        if isinstance(target, ast.Subscript):
            if target_lll.location == "storage":
                return make_setter(target_lll, LLLnode.from_list(None, typ=NullType()), "storage", pos=getpos(self.stmt))

        raise StructureException("Deleting type not supported.", self.stmt)
Beispiel #9
0
 def ann_assign(self):
     self.context.set_in_assignment(True)
     typ = parse_type(self.stmt.annotation, location='memory', custom_units=self.context.custom_units)
     if isinstance(self.stmt.target, ast.Attribute) and self.stmt.target.value.id == 'self':
         raise TypeMismatchException('May not redefine storage variables.', self.stmt)
     varname = self.stmt.target.id
     pos = self.context.new_variable(varname, typ)
     o = LLLnode.from_list('pass', typ=None, pos=pos)
     if self.stmt.value is not None:
         sub = Expr(self.stmt.value, self.context).lll_node
         self._check_valid_assign(sub)
         variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt))
         o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt))
     self.context.set_in_assignment(False)
     return o
Beispiel #10
0
    def parse_for(self):
        from .parser import (
            parse_body,
        )
        # Type 0 for, e.g. for i in list(): ...
        if self._is_list_iter():
            return self.parse_for_list()

        if not isinstance(self.stmt.iter, ast.Call) or \
            not isinstance(self.stmt.iter.func, ast.Name) or \
                not isinstance(self.stmt.target, ast.Name) or \
                    self.stmt.iter.func.id != "range" or \
                        len(self.stmt.iter.args) not in (1, 2):
            raise StructureException("For statements must be of the form `for i in range(rounds): ..` or `for i in range(start, start + rounds): ..`", self.stmt.iter)  # noqa

        block_scope_id = id(self.stmt.orelse)
        self.context.start_blockscope(block_scope_id)
        # Type 1 for, e.g. for i in range(10): ...
        if len(self.stmt.iter.args) == 1:
            if not isinstance(self.stmt.iter.args[0], ast.Num):
                raise StructureException("Range only accepts literal values", self.stmt.iter)
            start = LLLnode.from_list(0, typ='int128', pos=getpos(self.stmt))
            rounds = self.stmt.iter.args[0].n
        elif isinstance(self.stmt.iter.args[0], ast.Num) and isinstance(self.stmt.iter.args[1], ast.Num):
            # Type 2 for, e.g. for i in range(100, 110): ...
            start = LLLnode.from_list(self.stmt.iter.args[0].n, typ='int128', pos=getpos(self.stmt))
            rounds = LLLnode.from_list(self.stmt.iter.args[1].n - self.stmt.iter.args[0].n, typ='int128', pos=getpos(self.stmt))
        else:
            # Type 3 for, e.g. for i in range(x, x + 10): ...
            if not isinstance(self.stmt.iter.args[1], ast.BinOp) or not isinstance(self.stmt.iter.args[1].op, ast.Add):
                raise StructureException("Two-arg for statements must be of the form `for i in range(start, start + rounds): ...`",
                                            self.stmt.iter.args[1])
            if ast.dump(self.stmt.iter.args[0]) != ast.dump(self.stmt.iter.args[1].left):
                raise StructureException("Two-arg for statements of the form `for i in range(x, x + y): ...` must have x identical in both places: %r %r" % (ast.dump(self.stmt.iter.args[0]), ast.dump(self.stmt.iter.args[1].left)), self.stmt.iter)
            if not isinstance(self.stmt.iter.args[1].right, ast.Num):
                raise StructureException("Range only accepts literal values", self.stmt.iter.args[1])
            start = Expr.parse_value_expr(self.stmt.iter.args[0], self.context)
            rounds = self.stmt.iter.args[1].right.n
        varname = self.stmt.target.id
        pos = self.context.new_variable(varname, BaseType('int128'))
        self.context.forvars[varname] = True
        o = LLLnode.from_list(['repeat', pos, start, rounds, parse_body(self.stmt.body, self.context)], typ=None, pos=getpos(self.stmt))
        del self.context.vars[varname]
        del self.context.forvars[varname]
        self.context.end_blockscope(block_scope_id)
        return o
Beispiel #11
0
def raw_log(expr, args, kwargs, context):
    if not isinstance(args[0], ast.List) or len(args[0].elts) > 4:
        raise StructureException(
            "Expecting a list of 0-4 topics as first argument", args[0])
    topics = []
    for elt in args[0].elts:
        arg = Expr.parse_value_expr(elt, context)
        if not is_base_type(arg.typ, 'bytes32'):
            raise TypeMismatchException(
                "Expecting a bytes32 argument as topic", elt)
        topics.append(arg)
    if args[1].location == "memory":
        return LLLnode.from_list([
            "with", "_arr", args[1],
            ["log" + str(len(topics)), ["add", "_arr", 32], ["mload", "_arr"]]
            + topics
        ],
                                 typ=None,
                                 pos=getpos(expr))
    placeholder = context.new_placeholder(args[1].typ)
    placeholder_node = LLLnode.from_list(placeholder,
                                         typ=args[1].typ,
                                         location='memory')
    copier = make_byte_array_copier(placeholder_node,
                                    LLLnode.from_list(
                                        '_sub',
                                        typ=args[1].typ,
                                        location=args[1].location),
                                    pos=getpos(expr))
    return LLLnode.from_list([
        "with", "_sub", args[1],
        [
            "seq", copier,
            [
                "log" + str(len(topics)), ["add", placeholder_node, 32],
                ["mload", placeholder_node]
            ] + topics
        ]
    ],
                             typ=None,
                             pos=getpos(expr))
Beispiel #12
0
    def unroll_constant(self, const):
        # const = self.context.constants[self.expr.id]
        expr = Expr.parse_value_expr(const.value, Context(vars=None, global_ctx=self, origcode=const.source_code))
        annotation_type = parse_type(const.annotation.args[0], None, custom_units=self._custom_units)

        fail = False

        if self.is_instances([expr.typ, annotation_type], ByteArrayType):
            if expr.typ.maxlen < annotation_type.maxlen:
                return const
            fail = True

        elif expr.typ != annotation_type:
            fail = True
            # TODO: 
            # special case for literals, which can be uint256 types as well.
            # if self.is_instances([expr.typ, annotation_type], BaseType) and \
            #    [annotation_type.typ, expr.typ.typ] == ['uint256', 'int128'] and \
            #    SizeLimits.in_bounds('uint256', expr.value):
            #     fail = False

            # elif self.is_instances([expr.typ, annotation_type], BaseType) and \
            #    [annotation_type.typ, expr.typ.typ] == ['int128', 'int128'] and \
            #    SizeLimits.in_bounds('int128', expr.value):
            #     fail = False
            if self.is_instances([expr.typ, annotation_type], BaseType) and \
               [annotation_type.typ, expr.typ.typ] == ['integer', 'integer'] and \
               SizeLimits.in_bounds('integer', expr.value):
                fail = False

            elif self.is_instances([expr.typ, annotation_type], BaseType) and \
               [annotation_type.typ, expr.typ.typ] == ['amount', 'amount'] and \
               SizeLimits.in_bounds('amount', expr.value):
                fail = False

        if fail:
            raise TypeMismatchException('Invalid value for constant type, expected %r' % annotation_type, const.value)
        else:
            expr.typ = annotation_type
        return expr
Beispiel #13
0
 def assign(self):
     # Assignment (e.g. x[4] = y)
     if len(self.stmt.targets) != 1:
         raise StructureException("Assignment statement must have one target", self.stmt)
     self.context.set_in_assignment(True)
     sub = Expr(self.stmt.value, self.context).lll_node
     # Determine if it's an RLPList assignment.
     if isinstance(self.stmt.value, ast.Call) and getattr(self.stmt.value.func, 'id', '') is 'RLPList':
         pos = self.context.new_variable(self.stmt.targets[0].id, sub.typ)
         variable_loc = LLLnode.from_list(pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.targets[0].id)
         o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt))
     # All other assignments are forbidden.
     elif isinstance(self.stmt.targets[0], ast.Name) and self.stmt.targets[0].id not in self.context.vars:
         raise VariableDeclarationException("Variable type not defined", self.stmt)
     elif isinstance(self.stmt.targets[0], ast.Tuple) and isinstance(self.stmt.value, ast.Tuple):
         raise VariableDeclarationException("Tuple to tuple assignment not supported", self.stmt)
     else:
         # Checks to see if assignment is valid
         target = self.get_target(self.stmt.targets[0])
         o = make_setter(target, sub, target.location, pos=getpos(self.stmt))
     o.pos = getpos(self.stmt)
     self.context.set_in_assignment(False)
     return o
Beispiel #14
0
    def parse_if(self):
        from .parser import (
            parse_body,
        )
        if self.stmt.orelse:
            block_scope_id = id(self.stmt.orelse)
            self.context.start_blockscope(block_scope_id)
            add_on = [parse_body(self.stmt.orelse, self.context)]
            self.context.end_blockscope(block_scope_id)
        else:
            add_on = []
        block_scope_id = id(self.stmt)
        self.context.start_blockscope(block_scope_id)
        test_expr = Expr.parse_value_expr(self.stmt.test, self.context)

        if not self.is_bool_expr(test_expr):
            raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test)

        o = LLLnode.from_list(
            ['if', test_expr, parse_body(self.stmt.body, self.context)] + add_on,
            typ=None, pos=getpos(self.stmt)
        )
        self.context.end_blockscope(block_scope_id)
        return o
Beispiel #15
0
def concat(expr, context):
    args = [Expr(arg, context).lll_node for arg in expr.args]
    if len(args) < 2:
        raise StructureException("Concat expects at least two arguments", expr)
    for expr_arg, arg in zip(expr.args, args):
        if not isinstance(arg.typ, ByteArrayType) and not is_base_type(
                arg.typ, 'bytes32'):
            raise TypeMismatchException(
                "Concat expects byte arrays or bytes32 objects", expr_arg)
    # Maximum length of the output
    total_maxlen = sum([
        arg.typ.maxlen if isinstance(arg.typ, ByteArrayType) else 32
        for arg in args
    ])
    # Node representing the position of the output in memory
    placeholder = context.new_placeholder(ByteArrayType(total_maxlen))
    # Object representing the output
    seq = []
    # For each argument we are concatenating...
    for arg in args:
        # Start pasting into a position the starts at zero, and keeps
        # incrementing as we concatenate arguments
        placeholder_node = LLLnode.from_list(['add', placeholder, '_poz'],
                                             typ=ByteArrayType(total_maxlen),
                                             location='memory')
        placeholder_node_plus_32 = LLLnode.from_list(
            ['add', ['add', placeholder, '_poz'], 32],
            typ=ByteArrayType(total_maxlen),
            location='memory')
        if isinstance(arg.typ, ByteArrayType):
            # Ignore empty strings
            if arg.typ.maxlen == 0:
                continue
            # Get the length of the current argument
            if arg.location == "memory":
                length = LLLnode.from_list(['mload', '_arg'],
                                           typ=BaseType('int128'))
                argstart = LLLnode.from_list(['add', '_arg', 32],
                                             typ=arg.typ,
                                             location=arg.location)
            elif arg.location == "storage":
                length = LLLnode.from_list(['sload', ['sha3_32', '_arg']],
                                           typ=BaseType('int128'))
                argstart = LLLnode.from_list(['add', ['sha3_32', '_arg'], 1],
                                             typ=arg.typ,
                                             location=arg.location)
            # Make a copier to copy over data from that argyument
            seq.append([
                'with',
                '_arg',
                arg,
                [
                    'seq',
                    make_byte_slice_copier(placeholder_node_plus_32,
                                           argstart,
                                           length,
                                           arg.typ.maxlen,
                                           pos=getpos(expr)),
                    # Change the position to start at the correct
                    # place to paste the next value
                    ['set', '_poz', ['add', '_poz', length]]
                ]
            ])
        else:
            seq.append([
                'seq',
                [
                    'mstore', ['add', placeholder_node, 32],
                    unwrap_location(arg)
                ], ['set', '_poz', ['add', '_poz', 32]]
            ])
    # The position, after all arguments are processing, equals the total
    # length. Paste this in to make the output a proper bytearray
    seq.append(['mstore', placeholder, '_poz'])
    # Memory location of the output
    seq.append(placeholder)
    return LLLnode.from_list(['with', '_poz', 0, ['seq'] + seq],
                             typ=ByteArrayType(total_maxlen),
                             location='memory',
                             pos=getpos(expr),
                             annotation='concat')
Beispiel #16
0
def pack_args_by_32(holder,
                    maxlen,
                    arg,
                    typ,
                    context,
                    placeholder,
                    dynamic_offset_counter=None,
                    datamem_start=None,
                    zero_pad_i=None,
                    pos=None):
    """
    Copy necessary variables to pre-allocated memory section.

    :param holder: Complete holder for all args
    :param maxlen: Total length in bytes of the full arg section (static + dynamic).
    :param arg: Current arg to pack
    :param context: Context of arg
    :param placeholder: Static placeholder for static argument part.
    :param dynamic_offset_counter: position counter stored in static args.
    :param dynamic_placeholder: pointer to current position in memory to write dynamic values to.
    :param datamem_start: position where the whole datemem section starts.
    """

    if isinstance(typ, BaseType):
        value = parse_expr(arg, context)
        value = base_type_conversion(value, value.typ, typ, pos)
        holder.append(
            LLLnode.from_list(['mstore', placeholder, value],
                              typ=typ,
                              location='memory'))
    elif isinstance(typ, ByteArrayType):
        bytez = b''

        source_expr = Expr(arg, context)
        if isinstance(arg, ast.Str):
            if len(arg.s) > typ.maxlen:
                raise TypeMismatchException(
                    "Data input bytes are to big: %r %r" % (len(arg.s), typ),
                    pos)
            for c in arg.s:
                if ord(c) >= 256:
                    raise InvalidLiteralException(
                        "Cannot insert special character %r into byte array" %
                        c, pos)
                bytez += bytes([ord(c)])

            holder.append(source_expr.lll_node)

        # Set static offset, in arg slot.
        holder.append(
            LLLnode.from_list(
                ['mstore', placeholder, ['mload', dynamic_offset_counter]]))
        # Get the biginning to write the ByteArray to.
        dest_placeholder = LLLnode.from_list(
            ['add', datamem_start, ['mload', dynamic_offset_counter]],
            typ=typ,
            location='memory',
            annotation="pack_args_by_32:dest_placeholder")
        copier = make_byte_array_copier(dest_placeholder,
                                        source_expr.lll_node,
                                        pos=pos)
        holder.append(copier)
        # Add zero padding.
        new_maxlen = ceil32(source_expr.lll_node.typ.maxlen)

        holder.append([
            'with',
            '_bytearray_loc',
            dest_placeholder,
            [
                'seq',
                [
                    'repeat',
                    zero_pad_i,
                    ['mload', '_bytearray_loc'],
                    new_maxlen,
                    [
                        'seq',
                        [
                            'if', ['ge', ['mload', zero_pad_i], new_maxlen],
                            'break'
                        ],  # stay within allocated bounds
                        [
                            'mstore8',
                            [
                                'add', ['add', '_bytearray_loc', 32],
                                ['mload', zero_pad_i]
                            ], 0
                        ]
                    ]
                ]
            ]
        ])
        # Increment offset counter.
        increment_counter = LLLnode.from_list([
            'mstore', dynamic_offset_counter,
            [
                'add',
                [
                    'add', ['mload', dynamic_offset_counter],
                    ['ceil32', ['mload', dest_placeholder]]
                ], 32
            ]
        ])
        holder.append(increment_counter)
    elif isinstance(typ, ListType):
        maxlen += (typ.count - 1) * 32
        typ = typ.subtype

        def check_list_type_match(provided):  # Check list types match.
            if provided != typ:
                raise TypeMismatchException(
                    "Log list type '%s' does not match provided, expected '%s'"
                    % (provided, typ))

        # List from storage
        if isinstance(arg, ast.Attribute) and arg.value.id == 'self':
            stor_list = context.globals[arg.attr]
            check_list_type_match(stor_list.typ.subtype)
            size = stor_list.typ.count
            for offset in range(0, size):
                arg2 = LLLnode.from_list([
                    'sload',
                    ['add', ['sha3_32', Expr(arg, context).lll_node], offset]
                ],
                                         typ=typ)
                p_holder = context.new_placeholder(
                    BaseType(32)) if offset > 0 else placeholder
                holder, maxlen = pack_args_by_32(holder,
                                                 maxlen,
                                                 arg2,
                                                 typ,
                                                 context,
                                                 p_holder,
                                                 pos=pos)
        # List from variable.
        elif isinstance(arg, ast.Name):
            size = context.vars[arg.id].size
            pos = context.vars[arg.id].pos
            check_list_type_match(context.vars[arg.id].typ.subtype)
            for i in range(0, size):
                offset = 32 * i
                arg2 = LLLnode.from_list(pos + offset,
                                         typ=typ,
                                         location='memory')
                p_holder = context.new_placeholder(
                    BaseType(32)) if i > 0 else placeholder
                holder, maxlen = pack_args_by_32(holder,
                                                 maxlen,
                                                 arg2,
                                                 typ,
                                                 context,
                                                 p_holder,
                                                 pos=pos)
        # is list literal.
        else:
            holder, maxlen = pack_args_by_32(holder,
                                             maxlen,
                                             arg.elts[0],
                                             typ,
                                             context,
                                             placeholder,
                                             pos=pos)
            for j, arg2 in enumerate(arg.elts[1:]):
                holder, maxlen = pack_args_by_32(holder,
                                                 maxlen,
                                                 arg2,
                                                 typ,
                                                 context,
                                                 context.new_placeholder(
                                                     BaseType(32)),
                                                 pos=pos)

    return holder, maxlen
Beispiel #17
0
def parse_expr(expr, context):
    return Expr(expr, context).lll_node
Beispiel #18
0
def parse_func(code, sigs, origcode, global_ctx, _vars=None):
    if _vars is None:
        _vars = {}
    sig = FunctionSignature.from_definition(
        code, sigs=sigs, custom_units=global_ctx._custom_units)
    # Get base args for function.
    total_default_args = len(code.args.defaults)
    base_args = sig.args[:
                         -total_default_args] if total_default_args > 0 else sig.args
    default_args = code.args.args[-total_default_args:]
    default_values = dict(
        zip([arg.arg for arg in default_args], code.args.defaults))
    # __init__ function may not have defaults.
    if sig.name == '__init__' and total_default_args > 0:
        raise FunctionDeclarationException(
            "__init__ function may not have default parameters.")
    # Check for duplicate variables with globals
    for arg in sig.args:
        if arg.name in global_ctx._globals:
            raise FunctionDeclarationException(
                "Variable name duplicated between function arguments and globals: "
                + arg.name)

    # Create a local (per function) context.
    context = Context(vars=_vars,
                      global_ctx=global_ctx,
                      sigs=sigs,
                      return_type=sig.output_type,
                      is_constant=sig.const,
                      is_payable=sig.payable,
                      origcode=origcode,
                      is_private=sig.private,
                      method_id=sig.method_id)

    # Copy calldata to memory for fixed-size arguments
    max_copy_size = sum([
        32 if isinstance(arg.typ, ByteArrayType) else
        get_size_of_type(arg.typ) * 32 for arg in sig.args
    ])
    base_copy_size = sum([
        32 if isinstance(arg.typ, ByteArrayType) else
        get_size_of_type(arg.typ) * 32 for arg in base_args
    ])
    context.next_mem += max_copy_size

    clampers = []

    # Create callback_ptr, this stores a destination in the bytecode for a private
    # function to jump to after a function has executed.
    _post_callback_ptr = "{}_{}_post_callback_ptr".format(
        sig.name, sig.method_id)
    if sig.private:
        context.callback_ptr = context.new_placeholder(typ=BaseType('uint256'))
        clampers.append(
            LLLnode.from_list(['mstore', context.callback_ptr, 'pass'],
                              annotation='pop callback pointer'))
        if total_default_args > 0:
            clampers.append(['label', _post_callback_ptr])

    # private functions without return types need to jump back to
    # the calling function, as there is no return statement to handle the
    # jump.
    stop_func = [['stop']]
    if sig.output_type is None and sig.private:
        stop_func = [['jump', ['mload', context.callback_ptr]]]

    if not len(base_args):
        copier = 'pass'
    elif sig.name == '__init__':
        copier = [
            'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen',
            base_copy_size
        ]
    else:
        copier = get_arg_copier(sig=sig,
                                total_size=base_copy_size,
                                memory_dest=MemoryPositions.RESERVED_MEMORY)
    clampers.append(copier)

    # Add asserts for payable and internal
    # private never gets payable check.
    if not sig.payable and not sig.private:
        clampers.append(['assert', ['iszero', 'callvalue']])

    # Fill variable positions
    for i, arg in enumerate(sig.args):
        if i < len(base_args) and not sig.private:
            clampers.append(
                make_clamper(arg.pos, context.next_mem, arg.typ,
                             sig.name == '__init__'))
        if isinstance(arg.typ, ByteArrayType):
            context.vars[arg.name] = VariableRecord(arg.name, context.next_mem,
                                                    arg.typ, False)
            context.next_mem += 32 * get_size_of_type(arg.typ)
        else:
            context.vars[arg.name] = VariableRecord(
                arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ,
                False)

    # Private function copiers. No clamping for private functions.
    dyn_variable_names = [
        a.name for a in base_args if isinstance(a.typ, ByteArrayType)
    ]
    if sig.private and dyn_variable_names:
        i_placeholder = context.new_placeholder(typ=BaseType('uint256'))
        unpackers = []
        for idx, var_name in enumerate(dyn_variable_names):
            var = context.vars[var_name]
            ident = "_load_args_%d_dynarg%d" % (sig.method_id, idx)
            o = make_unpacker(ident=ident,
                              i_placeholder=i_placeholder,
                              begin_pos=var.pos)
            unpackers.append(o)

        if not unpackers:
            unpackers = ['pass']

        clampers.append(
            LLLnode.from_list(
                ['seq_unchecked'] + unpackers + [
                    0
                ],  # [0] to complete full overarching 'seq' statement, see private_label.
                typ=None,
                annotation='dynamic unpacker',
                pos=getpos(code)))

    # Create "clampers" (input well-formedness checkers)
    # Return function body
    if sig.name == '__init__':
        o = LLLnode.from_list(['seq'] + clampers +
                              [parse_body(code.body, context)],
                              pos=getpos(code))
    elif is_default_func(sig):
        if len(sig.args) > 0:
            raise FunctionDeclarationException(
                'Default function may not receive any arguments.', code)
        if sig.private:
            raise FunctionDeclarationException(
                'Default function may only be public.', code)
        o = LLLnode.from_list(['seq'] + clampers +
                              [parse_body(code.body, context)],
                              pos=getpos(code))
    else:

        if total_default_args > 0:  # Function with default parameters.
            function_routine = "{}_{}".format(sig.name, sig.method_id)
            default_sigs = generate_default_arg_sigs(code, sigs,
                                                     global_ctx._custom_units)
            sig_chain = ['seq']

            for default_sig in default_sigs:
                sig_compare, private_label = get_sig_statements(
                    default_sig, getpos(code))

                # Populate unset default variables
                populate_arg_count = len(sig.args) - len(default_sig.args)
                set_defaults = []
                if populate_arg_count > 0:
                    current_sig_arg_names = {x.name for x in default_sig.args}
                    missing_arg_names = [
                        arg.arg for arg in default_args
                        if arg.arg not in current_sig_arg_names
                    ]
                    for arg_name in missing_arg_names:
                        value = Expr(default_values[arg_name],
                                     context).lll_node
                        var = context.vars[arg_name]
                        left = LLLnode.from_list(var.pos,
                                                 typ=var.typ,
                                                 location='memory',
                                                 pos=getpos(code),
                                                 mutable=var.mutable)
                        set_defaults.append(
                            make_setter(left,
                                        value,
                                        'memory',
                                        pos=getpos(code)))

                current_sig_arg_names = {x.name for x in default_sig.args}
                base_arg_names = {arg.name for arg in base_args}
                if sig.private:
                    # Load all variables in default section, if private,
                    # because the stack is a linear pipe.
                    copier_arg_count = len(default_sig.args)
                    copier_arg_names = current_sig_arg_names
                else:
                    copier_arg_count = len(default_sig.args) - len(base_args)
                    copier_arg_names = current_sig_arg_names - base_arg_names
                # Order copier_arg_names, this is very important.
                copier_arg_names = [
                    x.name for x in default_sig.args
                    if x.name in copier_arg_names
                ]

                # Variables to be populated from calldata/stack.
                default_copiers = []
                if copier_arg_count > 0:
                    # Get map of variables in calldata, with thier offsets
                    offset = 4
                    calldata_offset_map = {}
                    for arg in default_sig.args:
                        calldata_offset_map[arg.name] = offset
                        offset += 32 if isinstance(
                            arg.typ,
                            ByteArrayType) else get_size_of_type(arg.typ) * 32
                    # Copy set default parameters from calldata
                    dynamics = []
                    for arg_name in copier_arg_names:
                        var = context.vars[arg_name]
                        calldata_offset = calldata_offset_map[arg_name]
                        if sig.private:
                            _offset = calldata_offset
                            if isinstance(var.typ, ByteArrayType):
                                _size = 32
                                dynamics.append(var.pos)
                            else:
                                _size = var.size * 32
                            default_copiers.append(
                                get_arg_copier(sig=sig,
                                               memory_dest=var.pos,
                                               total_size=_size,
                                               offset=_offset))
                        else:
                            # Add clampers.
                            default_copiers.append(
                                make_clamper(calldata_offset - 4, var.pos,
                                             var.typ))
                            # Add copying code.
                            if isinstance(var.typ, ByteArrayType):
                                _offset = [
                                    'add', 4,
                                    ['calldataload', calldata_offset]
                                ]
                            else:
                                _offset = calldata_offset
                            default_copiers.append(
                                get_arg_copier(sig=sig,
                                               memory_dest=var.pos,
                                               total_size=var.size * 32,
                                               offset=_offset))

                    # Unpack byte array if necessary.
                    if dynamics:
                        i_placeholder = context.new_placeholder(
                            typ=BaseType('uint256'))
                        for idx, var_pos in enumerate(dynamics):
                            ident = 'unpack_default_sig_dyn_%d_arg%d' % (
                                default_sig.method_id, idx)
                            default_copiers.append(
                                make_unpacker(ident=ident,
                                              i_placeholder=i_placeholder,
                                              begin_pos=var_pos))
                    default_copiers.append(0)  # for over arching seq, POP

                sig_chain.append([
                    'if', sig_compare,
                    [
                        'seq', private_label,
                        LLLnode.from_list(
                            ['mstore', context.callback_ptr, 'pass'],
                            annotation='pop callback pointer',
                            pos=getpos(code)) if sig.private else ['pass'],
                        ['seq'] + set_defaults if set_defaults else ['pass'],
                        ['seq_unchecked'] +
                        default_copiers if default_copiers else ['pass'],
                        [
                            'goto', _post_callback_ptr
                            if sig.private else function_routine
                        ]
                    ]
                ])

            # With private functions all variable loading occurs in the default
            # function sub routine.
            if sig.private:
                _clampers = [['label', _post_callback_ptr]]
            else:
                _clampers = clampers

            # Function with default parameters.
            o = LLLnode.from_list(
                [
                    'seq',
                    sig_chain,
                    [
                        'if',
                        0,  # can only be jumped into
                        [
                            'seq', ['label', function_routine]
                            if not sig.private else ['pass'],
                            ['seq'] + _clampers +
                            [parse_body(c, context)
                             for c in code.body] + stop_func
                        ]
                    ]
                ],
                typ=None,
                pos=getpos(code))

        else:
            # Function without default parameters.
            sig_compare, private_label = get_sig_statements(sig, getpos(code))
            o = LLLnode.from_list([
                'if', sig_compare, ['seq'] + [private_label] + clampers +
                [parse_body(c, context) for c in code.body] + stop_func
            ],
                                  typ=None,
                                  pos=getpos(code))

    # Check for at leasts one return statement if necessary.
    if context.return_type and context.function_return_count == 0:
        raise FunctionDeclarationException(
            "Missing return statement in function '%s' " % sig.name, code)

    o.context = context
    o.total_gas = o.gas + calc_mem_gas(o.context.next_mem)
    o.func_name = sig.name
    return o
Beispiel #19
0
    def parse_return(self):
        if self.context.return_type is None:
            if self.stmt.value:
                raise TypeMismatchException("Not expecting to return a value", self.stmt)
            return LLLnode.from_list(self.make_return_stmt(0, 0), typ=None, pos=getpos(self.stmt))
        if not self.stmt.value:
            raise TypeMismatchException("Expecting to return a value", self.stmt)

        def zero_pad(bytez_placeholder, maxlen):
            zero_padder = LLLnode.from_list(['pass'])
            if maxlen > 0:
                zero_pad_i = self.context.new_placeholder(BaseType('uint256'))  # Iterator used to zero pad memory.
                zero_padder = LLLnode.from_list(
                    ['repeat', zero_pad_i, ['mload', bytez_placeholder], maxlen,
                        ['seq',
                            ['if', ['gt', ['mload', zero_pad_i], maxlen], 'break'],  # stay within allocated bounds
                            ['mstore8', ['add', ['add', 32, bytez_placeholder], ['mload', zero_pad_i]], 0]]],
                    annotation="Zero pad"
                )
            return zero_padder

        sub = Expr(self.stmt.value, self.context).lll_node
        self.context.increment_return_counter()
        # Returning a value (most common case)
        if isinstance(sub.typ, BaseType):
            if not isinstance(self.context.return_type, BaseType):
                raise TypeMismatchException("Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value)
            sub = unwrap_location(sub)
            if not are_units_compatible(sub.typ, self.context.return_type):
                raise TypeMismatchException("Return type units mismatch %r %r" % (sub.typ, self.context.return_type), self.stmt.value)
            elif sub.typ.is_literal and (self.context.return_type.typ == sub.typ or
                    'int' in self.context.return_type.typ and
                    'int' in sub.typ.typ):
                if not SizeLimits.in_bounds(self.context.return_type.typ, sub.value):
                    raise InvalidLiteralException("Number out of range: " + str(sub.value), self.stmt)
                else:
                    return LLLnode.from_list(['seq', ['mstore', 0, sub], self.make_return_stmt(0, 32)], typ=None, pos=getpos(self.stmt))
            elif is_base_type(sub.typ, self.context.return_type.typ) or \
                    (is_base_type(sub.typ, 'int128') and is_base_type(self.context.return_type, 'int256')):
                return LLLnode.from_list(['seq', ['mstore', 0, sub], self.make_return_stmt(0, 32)], typ=None, pos=getpos(self.stmt))
            else:
                raise TypeMismatchException("Unsupported type conversion: %r to %r" % (sub.typ, self.context.return_type), self.stmt.value)
        # Returning a byte array
        elif isinstance(sub.typ, ByteArrayType):
            if not isinstance(self.context.return_type, ByteArrayType):
                raise TypeMismatchException("Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value)
            if sub.typ.maxlen > self.context.return_type.maxlen:
                raise TypeMismatchException("Cannot cast from greater max-length %d to shorter max-length %d" %
                                            (sub.typ.maxlen, self.context.return_type.maxlen), self.stmt.value)

            loop_memory_position = self.context.new_placeholder(typ=BaseType('uint256'))  # loop memory has to be allocated first.
            len_placeholder = self.context.new_placeholder(typ=BaseType('uint256'))  # len & bytez placeholder have to be declared after each other at all times.
            bytez_placeholder = self.context.new_placeholder(typ=sub.typ)

            if sub.location in ('storage', 'memory'):
                return LLLnode.from_list([
                    'seq',
                    make_byte_array_copier(
                        LLLnode(bytez_placeholder, location='memory', typ=sub.typ),
                        sub,
                        pos=getpos(self.stmt)
                    ),
                    zero_pad(bytez_placeholder, sub.typ.maxlen),
                    ['mstore', len_placeholder, 32],
                    self.make_return_stmt(len_placeholder, ['ceil32', ['add', ['mload', bytez_placeholder], 64]], loop_memory_position=loop_memory_position)],
                    typ=None, pos=getpos(self.stmt)
                )
            else:
                raise Exception("Invalid location: %s" % sub.location)

        elif isinstance(sub.typ, ListType):
            sub_base_type = re.split(r'\(|\[', str(sub.typ.subtype))[0]
            ret_base_type = re.split(r'\(|\[', str(self.context.return_type.subtype))[0]
            loop_memory_position = self.context.new_placeholder(typ=BaseType('uint256'))
            if sub_base_type != ret_base_type:
                raise TypeMismatchException(
                    "List return type %r does not match specified return type, expecting %r" % (
                        sub_base_type, ret_base_type
                    ),
                    self.stmt
                )
            elif sub.location == "memory" and sub.value != "multi":
                return LLLnode.from_list(self.make_return_stmt(sub, get_size_of_type(self.context.return_type) * 32, loop_memory_position=loop_memory_position),
                                            typ=None, pos=getpos(self.stmt))
            else:
                new_sub = LLLnode.from_list(self.context.new_placeholder(self.context.return_type), typ=self.context.return_type, location='memory')
                setter = make_setter(new_sub, sub, 'memory', pos=getpos(self.stmt))
                return LLLnode.from_list(['seq', setter, self.make_return_stmt(new_sub, get_size_of_type(self.context.return_type) * 32, loop_memory_position=loop_memory_position)],
                                            typ=None, pos=getpos(self.stmt))

        # Returning a tuple.
        elif isinstance(sub.typ, TupleType):
            if not isinstance(self.context.return_type, TupleType):
                raise TypeMismatchException("Trying to return tuple type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value)

            if len(self.context.return_type.members) != len(sub.typ.members):
                raise StructureException("Tuple lengths don't match!", self.stmt)

            # check return type matches, sub type.
            for i, ret_x in enumerate(self.context.return_type.members):
                s_member = sub.typ.members[i]
                sub_type = s_member if isinstance(s_member, NodeType) else s_member.typ
                if type(sub_type) is not type(ret_x):
                    raise StructureException(
                        "Tuple return type does not match annotated return. {} != {}".format(
                            type(sub_type), type(ret_x)
                        ),
                        self.stmt
                    )

            # Is from a call expression.
            if len(sub.args[0].args) > 0 and sub.args[0].args[0].value == 'call':  # self-call to public.
                mem_pos = sub.args[0].args[-1]
                mem_size = get_size_of_type(sub.typ) * 32
                return LLLnode.from_list(['return', mem_pos, mem_size], typ=sub.typ)

            elif (sub.annotation and 'Internal Call' in sub.annotation):
                mem_pos = sub.args[-1].value if sub.value == 'seq_unchecked' else sub.args[0].args[-1]
                mem_size = get_size_of_type(sub.typ) * 32
                # Add zero padder if bytes are present in output.
                zero_padder = ['pass']
                byte_arrays = [(i, x) for i, x in enumerate(sub.typ.members) if isinstance(x, ByteArrayType)]
                if byte_arrays:
                    i, x = byte_arrays[-1]
                    zero_padder = zero_pad(bytez_placeholder=['add', mem_pos, ['mload', mem_pos + i * 32]], maxlen=x.maxlen)
                return LLLnode.from_list(
                    ['seq'] +
                    [sub] +
                    [zero_padder] +
                    [self.make_return_stmt(mem_pos, mem_size)
                ], typ=sub.typ, pos=getpos(self.stmt))

            subs = []
            # Pre-allocate loop_memory_position if required for private function returning.
            loop_memory_position = self.context.new_placeholder(typ=BaseType('uint256')) if self.context.is_private else None
            # Allocate dynamic off set counter, to keep track of the total packed dynamic data size.
            dynamic_offset_counter_placeholder = self.context.new_placeholder(typ=BaseType('uint256'))
            dynamic_offset_counter = LLLnode(
                dynamic_offset_counter_placeholder, typ=None, annotation="dynamic_offset_counter"  # dynamic offset position counter.
            )
            new_sub = LLLnode.from_list(
                self.context.new_placeholder(typ=BaseType('uint256')), typ=self.context.return_type, location='memory', annotation='new_sub'
            )
            keyz = list(range(len(sub.typ.members)))
            dynamic_offset_start = 32 * len(sub.args)  # The static list of args end.
            left_token = LLLnode.from_list('_loc', typ=new_sub.typ, location="memory")

            def get_dynamic_offset_value():
                # Get value of dynamic offset counter.
                return ['mload', dynamic_offset_counter]

            def increment_dynamic_offset(dynamic_spot):
                # Increment dyanmic offset counter in memory.
                return [
                    'mstore', dynamic_offset_counter,
                    ['add',
                        ['add', ['ceil32', ['mload', dynamic_spot]], 32],
                        ['mload', dynamic_offset_counter]]
                ]

            for i, typ in enumerate(keyz):
                arg = sub.args[i]
                variable_offset = LLLnode.from_list(['add', 32 * i, left_token], typ=arg.typ, annotation='variable_offset')
                if isinstance(arg.typ, ByteArrayType):
                    # Store offset pointer value.
                    subs.append(['mstore', variable_offset, get_dynamic_offset_value()])

                    # Store dynamic data, from offset pointer onwards.
                    dynamic_spot = LLLnode.from_list(['add', left_token, get_dynamic_offset_value()], location="memory", typ=arg.typ, annotation='dynamic_spot')
                    subs.append(make_setter(dynamic_spot, arg, location="memory", pos=getpos(self.stmt)))
                    subs.append(increment_dynamic_offset(dynamic_spot))

                elif isinstance(arg.typ, BaseType):
                    subs.append(make_setter(variable_offset, arg, "memory", pos=getpos(self.stmt)))
                else:
                    raise Exception("Can't return type %s as part of tuple", type(arg.typ))

            setter = LLLnode.from_list(
                ['seq',
                    ['mstore', dynamic_offset_counter, dynamic_offset_start],
                    ['with', '_loc', new_sub, ['seq'] + subs]],
                typ=None
            )

            return LLLnode.from_list(
                ['seq',
                    setter,
                    self.make_return_stmt(new_sub, get_dynamic_offset_value(), loop_memory_position)],
                typ=None, pos=getpos(self.stmt)
            )
        else:
            raise TypeMismatchException("Can only return base type!", self.stmt)
Beispiel #20
0
    def parse_for_list(self):
        from .parser import (
            parse_body,
            make_setter
        )

        iter_list_node = Expr(self.stmt.iter, self.context).lll_node
        if not isinstance(iter_list_node.typ.subtype, BaseType):  # Sanity check on list subtype.
            raise StructureException('For loops allowed only on basetype lists.', self.stmt.iter)
        iter_var_type = self.context.vars.get(self.stmt.iter.id).typ if isinstance(self.stmt.iter, ast.Name) else None
        subtype = iter_list_node.typ.subtype.typ
        varname = self.stmt.target.id
        value_pos = self.context.new_variable(varname, BaseType(subtype))
        i_pos = self.context.new_variable('_index_for_' + varname, BaseType(subtype))
        self.context.forvars[varname] = True
        if iter_var_type:  # Is a list that is already allocated to memory.
            self.context.set_in_for_loop(self.stmt.iter.id)  # make sure list cannot be altered whilst iterating.
            iter_var = self.context.vars.get(self.stmt.iter.id)
            body = [
                'seq',
                ['mstore', value_pos, ['mload', ['add', iter_var.pos, ['mul', ['mload', i_pos], 32]]]],
                parse_body(self.stmt.body, self.context)
            ]
            o = LLLnode.from_list(
                ['repeat', i_pos, 0, iter_var.size, body], typ=None, pos=getpos(self.stmt)
            )
            self.context.remove_in_for_loop(self.stmt.iter.id)
        elif isinstance(self.stmt.iter, ast.List):  # List gets defined in the for statement.
            # Allocate list to memory.
            count = iter_list_node.typ.count
            tmp_list = LLLnode.from_list(
                obj=self.context.new_placeholder(ListType(iter_list_node.typ.subtype, count)),
                typ=ListType(iter_list_node.typ.subtype, count),
                location='memory'
            )
            setter = make_setter(tmp_list, iter_list_node, 'memory', pos=getpos(self.stmt))
            body = [
                'seq',
                ['mstore', value_pos, ['mload', ['add', tmp_list, ['mul', ['mload', i_pos], 32]]]],
                parse_body(self.stmt.body, self.context)
            ]
            o = LLLnode.from_list(
                ['seq',
                    setter,
                    ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)
            )
        elif isinstance(self.stmt.iter, ast.Attribute):  # List is contained in storage.
            count = iter_list_node.typ.count
            self.context.set_in_for_loop(iter_list_node.annotation)  # make sure list cannot be altered whilst iterating.
            body = [
                'seq',
                ['mstore', value_pos, ['sload', ['add', ['sha3_32', iter_list_node], ['mload', i_pos]]]],
                parse_body(self.stmt.body, self.context),
            ]
            o = LLLnode.from_list(
                ['seq',
                    ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)
            )
            self.context.remove_in_for_loop(iter_list_node.annotation)
        del self.context.vars[varname]
        del self.context.vars['_index_for_' + varname]
        del self.context.forvars[varname]
        return o