def concat(expr, context): from viper.parser.parser import parse_expr, unwrap_location args = [parse_expr(arg, context) 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('num')) 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('num')) 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) )
def avo(arg, ind): return unwrap_location(add_variable_offset(arg, LLLnode.from_list(ind, 'num')))