Ejemplo n.º 1
0
Archivo: stmt.py Proyecto: zutobg/vyper
    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
Ejemplo n.º 2
0
def constancy_checks(node, context, stmt):
    if node.location == 'storage' and context.is_constant():
        raise ConstancyViolationException(
            f"Cannot modify storage inside {context.pp_constancy()}: {node.annotation}",
            stmt,
        )
    if not node.mutable:
        raise ConstancyViolationException(
            f"Cannot modify function argument: {node.annotation}",
            stmt,
        )
Ejemplo n.º 3
0
def selfdestruct(expr, args, kwargs, context):
    if context.is_constant():
        raise ConstancyViolationException(
            "Cannot %s inside %s!" % (expr.func.id, context.pp_constancy()),
            expr.func,
        )
    return LLLnode.from_list(['selfdestruct', args[0]], typ=None, pos=getpos(expr))
Ejemplo n.º 4
0
def create_forwarder_to(expr, args, kwargs, context):

    value = kwargs['value']
    if value != zero_value:
        enforce_units(value.typ, get_keyword(expr, 'value'),
                      BaseType('uint256', {'wei': 1}))
    if context.is_constant():
        raise ConstancyViolationException(
            "Cannot make calls from %s" % context.pp_constancy(),
            expr,
        )
    placeholder = context.new_placeholder(ByteArrayType(96))

    kode = get_create_forwarder_to_bytecode()
    high = bytes_to_int(kode[:32])
    low = bytes_to_int((kode + b'\x00' * 32)[47:79])

    return LLLnode.from_list(
        [
            'seq',
            ['mstore', placeholder, high],
            ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]],
            ['mstore', ['add', placeholder, 47], low],
            ['clamp_nonzero', ['create', value, placeholder, 96]],
        ],
        typ=BaseType('address'),
        pos=getpos(expr),
        add_gas_estimate=11000,
    )
Ejemplo n.º 5
0
def raw_call(expr, args, kwargs, context):
    to, data = args
    gas, value, outsize = kwargs['gas'], kwargs['value'], kwargs['outsize']
    if context.is_constant:
        raise ConstancyViolationException(
            "Cannot make calls from a constant function", expr)
    if value != zero_value:
        enforce_units(value.typ, get_keyword(expr, 'value'),
                      BaseType('int128', {'wei': 1}))
    placeholder = context.new_placeholder(data.typ)
    placeholder_node = LLLnode.from_list(placeholder,
                                         typ=data.typ,
                                         location='memory')
    copier = make_byte_array_copier(placeholder_node, data)
    output_placeholder = context.new_placeholder(ByteArrayType(outsize))
    output_node = LLLnode.from_list(output_placeholder,
                                    typ=ByteArrayType(outsize),
                                    location='memory')
    z = LLLnode.from_list([
        'seq', copier,
        [
            'assert',
            [
                'call', gas, to, value, ['add', placeholder_node, 32],
                ['mload', placeholder_node], ['add', output_node, 32], outsize
            ]
        ], ['mstore', output_node, outsize], output_node
    ],
                          typ=ByteArrayType(outsize),
                          location='memory',
                          pos=getpos(expr))
    return z
Ejemplo n.º 6
0
def send(expr, args, kwargs, context):
    to, value = args
    if context.is_constant:
        raise ConstancyViolationException(
            "Cannot send ether inside a constant function!", expr)
    enforce_units(value.typ, expr.args[1], BaseType('int128', {'wei': 1}))
    return LLLnode.from_list(['assert', ['call', 0, to, value, 0, 0, 0, 0]],
                             typ=None,
                             pos=getpos(expr))
Ejemplo n.º 7
0
def raw_call(expr, args, kwargs, context):
    to, data = args
    gas, value, outsize, delegate_call = kwargs['gas'], kwargs[
        'value'], kwargs['outsize'], kwargs['delegate_call']
    if delegate_call.typ.is_literal is False:
        raise TypeMismatchException(
            'The delegate_call parameter has to be a static/literal boolean value.'
        )
    if context.is_constant:
        raise ConstancyViolationException(
            "Cannot make calls from a constant function", expr)
    if value != zero_value:
        enforce_units(value.typ, get_keyword(expr, 'value'),
                      BaseType('uint256', {'wei': 1}))
    placeholder = context.new_placeholder(data.typ)
    placeholder_node = LLLnode.from_list(placeholder,
                                         typ=data.typ,
                                         location='memory')
    copier = make_byte_array_copier(placeholder_node, data, pos=getpos(expr))
    output_placeholder = context.new_placeholder(ByteArrayType(outsize))
    output_node = LLLnode.from_list(output_placeholder,
                                    typ=ByteArrayType(outsize),
                                    location='memory')

    if delegate_call.value == 1:
        z = LLLnode.from_list([
            'seq', copier,
            [
                'assert',
                [
                    'delegatecall', gas, to, ['add', placeholder_node, 32],
                    ['mload', placeholder_node], ['add', output_node, 32],
                    outsize
                ]
            ], ['mstore', output_node, outsize], output_node
        ],
                              typ=ByteArrayType(outsize),
                              location='memory',
                              pos=getpos(expr))
    else:
        z = LLLnode.from_list([
            'seq', copier,
            [
                'assert',
                [
                    'call', gas, to, value, ['add', placeholder_node, 32],
                    ['mload', placeholder_node], ['add', output_node, 32],
                    outsize
                ]
            ], ['mstore', output_node, outsize], output_node
        ],
                              typ=ByteArrayType(outsize),
                              location='memory',
                              pos=getpos(expr))
    return z
Ejemplo n.º 8
0
def make_call(stmt_expr, context):
    method_name, _, sig = call_lookup_specs(stmt_expr, context)

    if context.is_constant() and not sig.const:
        raise ConstancyViolationException(
            "May not call non-constant function '%s' within %s." %
            (method_name, context.pp_constancy()), getpos(stmt_expr))

    if sig.private:
        return call_self_private(stmt_expr, context, sig)
    else:
        return call_self_public(stmt_expr, context, sig)
Ejemplo n.º 9
0
Archivo: stmt.py Proyecto: agroce/vyper
    def get_target(self, target):
        # Check if we are doing assignment of an iteration loop.
        if isinstance(target, ast.Subscript) and self.context.in_for_loop:
            raise_exception = False
            if isinstance(target.value, ast.Attribute):
                list_name = f"{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(
                    f"Altering list '{list_name}' which is being iterated!",
                    self.stmt,
                )

        if isinstance(target, ast.Name) and target.id in self.context.forvars:
            raise StructureException(
                f"Altering iterator '{target.id}' which is in use!",
                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(
                f"Cannot modify storage inside {self.context.pp_constancy()}: {target.annotation}",
                self.stmt,
            )
        if not target.mutable:
            raise ConstancyViolationException(
                f"Cannot modify function argument: {target.annotation}",
                self.stmt,
            )
        return target
Ejemplo n.º 10
0
def make_call(stmt_expr, context):
    method_name, _, sig = call_lookup_specs(stmt_expr, context)

    if context.is_constant() and not sig.const:
        raise ConstancyViolationException(
            f"May not call non-constant function '{method_name}' within {context.pp_constancy()}.",
            getpos(stmt_expr)
        )

    if not sig.private:
        raise StructureException("Cannot call public functions via 'self'", stmt_expr)

    return call_self_private(stmt_expr, context, sig)
Ejemplo n.º 11
0
def create_with_code_of(expr, args, kwargs, context):
    value = kwargs['value']
    if value != zero_value:
        enforce_units(value.typ, get_keyword(expr, 'value'),
                      BaseType('int128', {'wei': 1}))
    if context.is_constant:
        raise ConstancyViolationException("Cannot make calls from a constant function", expr)
    placeholder = context.new_placeholder(ByteArrayType(96))
    kode = b'`.`\x0c`\x009`.`\x00\xf36`\x00`\x007a\x10\x00`\x006`\x00s\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\xf4\x15XWa\x10\x00`\x00\xf3'
    assert len(kode) <= 64
    high = bytes_to_int(kode[:32])
    low = bytes_to_int((kode + b'\x00' * 32)[47:79])
    return LLLnode.from_list(['seq',
                                ['mstore', placeholder, high],
                                ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]],
                                ['mstore', ['add', placeholder, 47], low],
                                ['clamp_nonzero', ['create', value, placeholder, 64]]], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=10000)
Ejemplo n.º 12
0
    def call(self):
        from .parser import (
            external_contract_call,
            pack_arguments,
        )
        from vyper.functions import (
            dispatch_table, )
        if isinstance(self.expr.func, ast.Name):
            function_name = self.expr.func.id
            if function_name in dispatch_table:
                return dispatch_table[function_name](self.expr, self.context)
            else:
                err_msg = "Not a top-level function: {}".format(function_name)
                if function_name in self.context.sigs['self']:
                    err_msg += ". Did you mean self.{}?".format(function_name)
                raise StructureException(err_msg, self.expr)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(
                self.expr.func.value,
                ast.Name) and self.expr.func.value.id == "self":
            method_name = self.expr.func.attr
            if method_name not in self.context.sigs['self']:
                raise VariableDeclarationException(
                    "Function not declared yet (reminder: functions cannot "
                    "call functions later in code than themselves): %s" %
                    self.expr.func.attr)

            sig = self.context.sigs['self'][method_name]
            if self.context.is_constant and not sig.const:
                raise ConstancyViolationException(
                    "May not call non-constant function '%s' within a constant function."
                    % (method_name), getpos(self.expr))
            add_gas = self.context.sigs['self'][method_name].gas  # gas of call
            inargs, inargsize = pack_arguments(
                sig,
                [Expr(arg, self.context).lll_node for arg in self.expr.args],
                self.context,
                pos=getpos(self.expr))
            output_placeholder = self.context.new_placeholder(
                typ=sig.output_type)
            multi_arg = []
            if isinstance(sig.output_type, BaseType):
                returner = output_placeholder
            elif isinstance(sig.output_type, ByteArrayType):
                returner = output_placeholder + 32
            elif self.context.in_assignment and isinstance(
                    sig.output_type, TupleType):
                returner = output_placeholder
            else:
                raise TypeMismatchException(
                    "Invalid output type: %r" % sig.output_type, self.expr)
            o = LLLnode.from_list(multi_arg + [
                'seq',
                [
                    'assert',
                    [
                        'call', ['gas'], ['address'], 0, inargs, inargsize,
                        output_placeholder,
                        get_size_of_type(sig.output_type) * 32
                    ]
                ], returner
            ],
                                  typ=sig.output_type,
                                  location='memory',
                                  pos=getpos(self.expr),
                                  add_gas_estimate=add_gas,
                                  annotation='Internal Call: %s' % method_name)
            o.gas += sig.gas
            return o
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(
                self.expr.func.value, ast.Call):
            contract_name = self.expr.func.value.func.id
            contract_address = Expr.parse_value_expr(
                self.expr.func.value.args[0], self.context)
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.expr),
                                          value=value,
                                          gas=gas)
        elif isinstance(self.expr.func.value, ast.Attribute
                        ) and self.expr.func.value.attr in self.context.sigs:
            contract_name = self.expr.func.value.attr
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.expr),
                                  annotation='self.' +
                                  self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.expr),
                                          value=value,
                                          gas=gas)
        elif isinstance(
                self.expr.func.value, ast.Attribute
        ) and self.expr.func.value.attr in self.context.globals:
            contract_name = self.context.globals[
                self.expr.func.value.attr].typ.unit
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.expr),
                                  annotation='self.' +
                                  self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.expr),
                                          value=value,
                                          gas=gas)
        else:
            raise StructureException(
                "Unsupported operator: %r" % ast.dump(self.expr), self.expr)
Ejemplo n.º 13
0
    def call(self):
        from .parser import (
            pack_arguments,
            pack_logging_data,
            pack_logging_topics,
            external_contract_call,
        )
        if isinstance(self.stmt.func,
                      ast.Name) and self.stmt.func.id in stmt_dispatch_table:
            return stmt_dispatch_table[self.stmt.func.id](self.stmt,
                                                          self.context)
        elif isinstance(self.stmt.func, ast.Attribute) and isinstance(
                self.stmt.func.value,
                ast.Name) and self.stmt.func.value.id == "self":
            method_name = self.stmt.func.attr
            if method_name not in self.context.sigs['self']:
                raise VariableDeclarationException(
                    "Function not declared yet (reminder: functions cannot "
                    "call functions later in code than themselves): %s" %
                    self.stmt.func.attr)
            sig = self.context.sigs['self'][method_name]
            if self.context.is_constant and not sig.const:
                raise ConstancyViolationException(
                    "May not call non-constant function '%s' within a constant function."
                    % (method_name))
            add_gas = self.context.sigs['self'][method_name].gas
            inargs, inargsize = pack_arguments(
                sig,
                [Expr(arg, self.context).lll_node for arg in self.stmt.args],
                self.context,
                pos=getpos(self.stmt))
            return LLLnode.from_list([
                'assert',
                ['call', ['gas'], ['address'], 0, inargs, inargsize, 0, 0]
            ],
                                     typ=None,
                                     pos=getpos(self.stmt),
                                     add_gas_estimate=add_gas,
                                     annotation='Internal Call: %s' %
                                     method_name)
        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 VariableDeclarationException(
                    "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 VariableDeclarationException(
                    "%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)
Ejemplo n.º 14
0
def external_contract_call(node,
                           context,
                           contract_name,
                           contract_address,
                           pos,
                           value=None,
                           gas=None):
    from vyper.parser.expr import (
        Expr, )
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if not contract_name:
        raise StructureException(
            f'Invalid external contract call "{node.func.attr}".', node)
    if contract_name not in context.sigs:
        raise VariableDeclarationException(
            f'Contract "{contract_name}" not declared yet', node)
    if contract_address.value == "address":
        raise StructureException(f"External calls to self are not permitted.",
                                 node)
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException((
            f"Function not declared yet: {method_name} (reminder: "
            "function must be declared in the correct contract)"
            f"The available methods are: {','.join(context.sigs[contract_name].keys())}"
        ), node.func)
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [Expr(arg, context).lll_node for arg in node.args],
        context,
        node.func,
    )
    output_placeholder, output_size, returner = get_external_contract_call_output(
        sig, context)
    sub = [
        'seq',
        ['assert', ['extcodesize', contract_address]],
        ['assert', ['ne', 'address', contract_address]],
    ]
    if context.is_constant() and not sig.const:
        raise ConstancyViolationException(
            f"May not call non-constant function '{method_name}' within {context.pp_constancy()}."
            " For asserting the result of modifiable contract calls, try assert_modifiable.",
            node)

    if context.is_constant() or sig.const:
        sub.append([
            'assert',
            [
                'staticcall',
                gas,
                contract_address,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ]
        ])
    else:
        sub.append([
            'assert',
            [
                'call',
                gas,
                contract_address,
                value,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ]
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(node))
    return o
Ejemplo n.º 15
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(
            right,
            right.typ,
            left.typ,
            pos,
            in_function_call=in_function_call,
        )
        # TODO this overlaps a type check in parser.stmt.Stmt._check_valid_assign
        # and should be examined during a refactor (@iamdefinitelyahuman)
        if 'int' in left.typ.typ and isinstance(right.value, int):
            if not SizeLimits.in_bounds(left.typ.typ, right.value):
                raise InvalidLiteralException(
                    f"Number out of range for {left.typ}: {right.value}", pos)
        if location == 'storage':
            return LLLnode.from_list(['sstore', left, right], typ=None)
        elif location == 'memory':
            return LLLnode.from_list(['mstore', left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatchException(
            "Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, (ListType, NullType)):
            raise TypeMismatchException(
                f"Setter type mismatch: left side is array, right side is {right.typ}",
                pos)
        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        # Type checks
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, ListType):
                raise TypeMismatchException(
                    "Left side is array, right side is not", pos)
            if left.typ.count != right.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != left.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        # CC 20190619 probably not needed as of #1106
        elif isinstance(right.typ, NullType):
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                LLLnode.from_list(None, typ=NullType()),
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a variable
        else:
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                add_variable_offset(
                                    right_token,
                                    LLLnode.from_list(i, typ='int128'),
                                    pos=pos,
                                    array_bounds_check=False,
                                ),
                                location,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    # Structs
    elif isinstance(left.typ, TupleLike):
        if left.value == "multi" and isinstance(left.typ, StructType):
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, left.typ.__class__):
                raise TypeMismatchException(
                    f"Setter type mismatch: left side is {left.typ}, right side is {right.typ}",
                    pos,
                )
            if isinstance(left.typ, StructType):
                for k in right.args:
                    if k.value is None:
                        raise InvalidLiteralException(
                            'Setting struct value to None is not allowed, use a default value.',
                            pos,
                        )
                for k in left.typ.members:
                    if k not in right.typ.members:
                        raise TypeMismatchException(
                            f"Keys don't match for structs, missing {k}",
                            pos,
                        )
                for k in right.typ.members:
                    if k not in left.typ.members:
                        raise TypeMismatchException(
                            f"Keys don't match for structs, extra {k}",
                            pos,
                        )
                if left.typ.name != right.typ.name:
                    raise TypeMismatchException(
                        f"Expected {left.typ}, got {right.typ}", pos)
            else:
                if len(left.typ.members) != len(right.typ.members):
                    raise TypeMismatchException(
                        "Tuple lengths don't match, "
                        f"{len(left.typ.members)} vs {len(right.typ.members)}",
                        pos,
                    )

        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        keyz = left.typ.tuple_keys()

        # If the left side is a literal
        if left.value == 'multi':
            locations = [arg.location for arg in left.args]
        else:
            locations = [location for _ in keyz]

        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != len(keyz):
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            # get the RHS arguments into a dict because
            # they are not guaranteed to be in the same order
            # the LHS keys.
            right_args = dict(zip(right.typ.tuple_keys(), right.args))
            subs = []
            for (key, loc) in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, key, pos=pos),
                        right_args[key],
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        elif isinstance(right.typ, NullType):
            subs = []
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        LLLnode.from_list(None, typ=NullType()),
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If tuple assign.
        elif isinstance(left.typ, TupleType) and isinstance(
                right.typ, TupleType):
            subs = []
            static_offset_counter = 0
            zipped_components = zip(left.args, right.typ.members, locations)
            for var_arg in left.args:
                if var_arg.location == 'calldata':
                    raise ConstancyViolationException(
                        f"Cannot modify function argument: {var_arg.annotation}",
                        pos)
            for left_arg, right_arg, loc in zipped_components:
                if isinstance(right_arg, ByteArrayLike):
                    RType = ByteArrayType if isinstance(
                        right_arg, ByteArrayType) else StringType
                    offset = LLLnode.from_list([
                        'add', '_R',
                        ['mload', ['add', '_R', static_offset_counter]]
                    ],
                                               typ=RType(right_arg.maxlen),
                                               location='memory',
                                               pos=pos)
                    static_offset_counter += 32
                else:
                    offset = LLLnode.from_list(
                        ['mload', ['add', '_R', static_offset_counter]],
                        typ=right_arg.typ,
                        pos=pos,
                    )
                    static_offset_counter += get_size_of_type(right_arg) * 32
                subs.append(make_setter(left_arg, offset, loc, pos=pos))
            return LLLnode.from_list(
                ['with', '_R', right, ['seq'] + subs],
                typ=None,
                annotation='Tuple assignment',
            )
        # If the right side is a variable
        else:
            subs = []
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(add_variable_offset(left_token, typ, pos=pos),
                                add_variable_offset(right_token, typ, pos=pos),
                                loc,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None,
            )
    else:
        raise Exception("Invalid type for setters")
Ejemplo n.º 16
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(
            right,
            right.typ,
            left.typ,
            pos,
            in_function_call=in_function_call,
        )
        if location == 'storage':
            return LLLnode.from_list(['sstore', left, right], typ=None)
        elif location == 'memory':
            return LLLnode.from_list(['mstore', left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatchException(
            "Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, (ListType, NullType)):
            raise TypeMismatchException(
                "Setter type mismatch: left side is array, right side is %r" %
                right.typ, pos)
        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        # Type checks
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, ListType):
                raise TypeMismatchException(
                    "Left side is array, right side is not", pos)
            if left.typ.count != right.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != left.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        # CC 20190619 probably not needed as of #1106
        elif isinstance(right.typ, NullType):
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                LLLnode.from_list(None, typ=NullType()),
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a variable
        else:
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                add_variable_offset(
                                    right_token,
                                    LLLnode.from_list(i, typ='int128'),
                                    pos=pos,
                                    array_bounds_check=False,
                                ),
                                location,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    # Structs
    elif isinstance(left.typ, (StructType, TupleType)):
        if left.value == "multi" and isinstance(left.typ, StructType):
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, left.typ.__class__):
                raise TypeMismatchException(
                    "Setter type mismatch: left side is %r, right side is %r" %
                    (
                        left.typ,
                        right.typ,
                    ),
                    pos,
                )
            if isinstance(left.typ, StructType):
                for k in right.args:
                    if k.value is None:
                        raise InvalidLiteralException(
                            'Setting struct value to None is not allowed, use a default value.',
                            pos,
                        )
                for k in left.typ.members:
                    if k not in right.typ.members:
                        raise TypeMismatchException(
                            "Keys don't match for structs, missing %s" % k,
                            pos,
                        )
                for k in right.typ.members:
                    if k not in left.typ.members:
                        raise TypeMismatchException(
                            "Keys don't match for structs, extra %s" % k,
                            pos,
                        )
                if left.typ.name != right.typ.name:
                    raise TypeMismatchException(
                        "Expected %r, got %r" % (left.typ, right.typ), pos)
            else:
                if len(left.typ.members) != len(right.typ.members):
                    raise TypeMismatchException(
                        "Tuple lengths don't match, %d vs %d" % (
                            len(left.typ.members),
                            len(right.typ.members),
                        ),
                        pos,
                    )

        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        if isinstance(left.typ, StructType):
            keyz = list(left.typ.members.keys())
        else:
            keyz = list(range(len(left.typ.members)))

        # If the left side is a literal
        if left.value == 'multi':
            locations = [arg.location for arg in left.args]
        else:
            locations = [location for _ in keyz]

        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != len(keyz):
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i, (typ, loc) in enumerate(zip(keyz, locations)):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        right.args[i],
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        elif isinstance(right.typ, NullType):
            subs = []
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        LLLnode.from_list(None, typ=NullType()),
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If tuple assign.
        elif isinstance(left.typ, TupleType) and isinstance(
                right.typ, TupleType):
            subs = []
            static_offset_counter = 0
            zipped_components = zip(left.args, right.typ.members, locations)
            for var_arg in left.args:
                if var_arg.location == 'calldata':
                    raise ConstancyViolationException(
                        f"Cannot modify function argument: {var_arg.annotation}",
                        pos)
            for left_arg, right_arg, loc in zipped_components:
                if isinstance(right_arg, ByteArrayLike):
                    RType = ByteArrayType if isinstance(
                        right_arg, ByteArrayType) else StringType
                    offset = LLLnode.from_list([
                        'add', '_R',
                        ['mload', ['add', '_R', static_offset_counter]]
                    ],
                                               typ=RType(right_arg.maxlen),
                                               location='memory',
                                               pos=pos)
                    static_offset_counter += 32
                else:
                    offset = LLLnode.from_list(
                        ['mload', ['add', '_R', static_offset_counter]],
                        typ=right_arg.typ,
                        pos=pos,
                    )
                    static_offset_counter += get_size_of_type(right_arg) * 32
                subs.append(make_setter(left_arg, offset, loc, pos=pos))
            return LLLnode.from_list(
                ['with', '_R', right, ['seq'] + subs],
                typ=None,
                annotation='Tuple assignment',
            )
        # If the right side is a variable
        else:
            subs = []
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(add_variable_offset(left_token, typ, pos=pos),
                                add_variable_offset(right_token, typ, pos=pos),
                                loc,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None,
            )
    else:
        raise Exception("Invalid type for setters")