Example #1
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)
    # 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))
Example #2
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') and not is_base_type(arg.typ, 'method_id'):
            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),
                                # Change the position to start at the correct
                                # place to paste the next value
                                ['set', '_poz', ['add', '_poz', length]]]])
        elif isinstance(arg.typ, BaseType) and arg.typ.typ == "method_id":
            seq.append(['seq',
                            ['mstore', ['add', placeholder_node, 32], arg.value * 2**224],
                            ['set', '_poz', ['add', '_poz', 4]]])
        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'
    )
Example #3
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", expr)
    # 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", expr)

    if is_base_type(sub.typ, 'bytes32'):
        if (start.typ.is_literal and length.typ.is_literal) and \
           not (0 <= start.value + length.value <= 32):
            raise InvalidLiteralException(
                'Invalid start / length values needs to be between 0 and 32.',
                expr,
            )
        sub_typ_maxlen = 32
    else:
        sub_typ_maxlen = sub.typ.maxlen

    # Get returntype string or bytes
    if isinstance(args[0].typ, ByteArrayType) or is_base_type(sub.typ, 'bytes32'):
        ReturnType = ByteArrayType
    else:
        ReturnType = StringType

    # Node representing the position of the output in memory
    np = context.new_placeholder(ReturnType(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,
        )

    if is_base_type(sub.typ, 'bytes32'):
        adj_sub = LLLnode.from_list(
            sub.args[0], typ=sub.typ, location="memory"
        )

    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
    if is_base_type(sub.typ, 'bytes32'):
        maxlen = 32
    else:
        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=ReturnType(newmaxlen), location='memory', pos=getpos(expr))
Example #4
0
    def build_LLL(self, 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)

        prev_type = ''
        for _, (expr_arg, arg) in enumerate(zip(expr.args, args)):
            if not isinstance(arg.typ, ByteArrayLike) and not is_base_type(
                    arg.typ, 'bytes32'):
                raise TypeMismatch(
                    "Concat expects string, bytes or bytes32 objects",
                    expr_arg)

            current_type = ('bytes' if isinstance(arg.typ, ByteArrayType)
                            or is_base_type(arg.typ, 'bytes32') else 'string')
            if prev_type and current_type != prev_type:
                raise TypeMismatch(
                    ("Concat expects consistant use of string or byte types, "
                     "user either bytes or string."),
                    expr_arg,
                )
            prev_type = current_type

        if current_type == 'string':
            ReturnType = StringType
        else:
            ReturnType = ByteArrayType

        # Maximum length of the output
        total_maxlen = sum([
            arg.typ.maxlen if isinstance(arg.typ, ByteArrayLike) else 32
            for arg in args
        ])
        # Node representing the position of the output in memory
        placeholder = context.new_placeholder(ReturnType(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=ReturnType(total_maxlen),
                location='memory',
            )
            placeholder_node_plus_32 = LLLnode.from_list(
                ['add', ['add', placeholder, '_poz'], 32],
                typ=ReturnType(total_maxlen),
                location='memory',
            )
            if isinstance(arg.typ, ReturnType):
                # 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 argument
                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=ReturnType(total_maxlen),
            location='memory',
            pos=getpos(expr),
            annotation='concat',
        )