def struct_literals(expr, name, context): member_subs = {} member_typs = {} for key, value in zip(expr.keys, expr.values): if not isinstance(key, vy_ast.Name): raise TypeMismatch( f"Invalid member variable for struct: {getattr(key, 'id', '')}", key, ) check_valid_varname( key.id, context.structs, context.constants, "Invalid member variable for struct", ) if key.id in member_subs: raise TypeMismatch("Member variable duplicated: " + key.id, key) sub = Expr(value, context).lll_node member_subs[key.id] = sub member_typs[key.id] = sub.typ return LLLnode.from_list( ["multi"] + [member_subs[key] for key in member_subs.keys()], typ=StructType(member_typs, name, is_literal=True), pos=getpos(expr), )
def make_byte_array_copier(dst, src): assert isinstance(src.typ, ByteArrayLike) assert isinstance(dst.typ, ByteArrayLike) if src.typ.maxlen > dst.typ.maxlen: raise TypeMismatch(f"Cannot cast from {src.typ} to {dst.typ}") # stricter check for zeroing a byte array. if src.value == "~empty" and src.typ.maxlen != dst.typ.maxlen: raise TypeMismatch( f"Bad type for clearing bytes: expected {dst.typ} but got {src.typ}" ) # pragma: notest if src.value == "~empty": # set length word to 0. return STORE(dst, 0) with src.cache_when_complex("src") as (b1, src): with get_bytearray_length(src).cache_when_complex("len") as (b2, len_): max_bytes = src.typ.maxlen ret = ["seq"] # store length ret.append(STORE(dst, len_)) dst = bytes_data_ptr(dst) src = bytes_data_ptr(src) ret.append(copy_bytes(dst, src, len_, max_bytes)) return b1.resolve(b2.resolve(ret))
def unary_operations(self): operand = Expr.parse_value_expr(self.expr.operand, self.context) if isinstance(self.expr.op, vy_ast.Not): if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool': return LLLnode.from_list(["iszero", operand], typ='bool', pos=getpos(self.expr)) else: raise TypeMismatch( f"Only bool is supported for not operation, {operand.typ} supplied.", self.expr, ) elif isinstance(self.expr.op, vy_ast.USub): if not is_numeric_type(operand.typ): raise TypeMismatch( f"Unsupported type for negation: {operand.typ}", self.expr, ) # Clamp on minimum integer value as we cannot negate that value # (all other integer values are fine) min_int_val = get_min_val_for_type(operand.typ.typ) return LLLnode.from_list( ["sub", 0, ["clampgt", operand, min_int_val]], typ=operand.typ, pos=getpos(self.expr)) else: raise StructureException( "Only the 'not' or 'neg' unary operators are supported")
def _check_assign_bytes(left, right): if right.typ.maxlen > left.typ.maxlen: raise TypeMismatch( f"Cannot cast from {right.typ} to {left.typ}") # pragma: notest # stricter check for zeroing a byte array. if right.value == "~empty" and right.typ.maxlen != left.typ.maxlen: raise TypeMismatch(f"Cannot cast from empty({right.typ}) to {left.typ}" ) # pragma: notest
def base_type_conversion(orig, frm, to, pos, in_function_call=False): orig = unwrap_location(orig) is_valid_int128_to_decimal = (is_base_type(frm, 'int128') and is_base_type( to, 'decimal')) and are_units_compatible(frm, to) if getattr(frm, 'is_literal', False): if frm.typ in ('int128', 'uint256'): if not SizeLimits.in_bounds(frm.typ, orig.value): raise InvalidLiteral(f"Number out of range: {orig.value}", pos) # Special Case: Literals in function calls should always convey unit type as well. if in_function_call and not (frm.unit == to.unit and frm.positional == to.positional): raise InvalidLiteral( f"Function calls require explicit unit definitions on calls, expected {to}", pos) if to.typ in ('int128', 'uint256'): if not SizeLimits.in_bounds(to.typ, orig.value): raise InvalidLiteral(f"Number out of range: {orig.value}", pos) if not isinstance(frm, (BaseType, NullType)) or not isinstance(to, BaseType): raise TypeMismatch( f"Base type conversion from or to non-base type: {frm} {to}", pos) elif is_base_type(frm, to.typ) and are_units_compatible(frm, to): return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate) elif isinstance(frm, ContractType) and to == BaseType('address'): return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate) elif is_valid_int128_to_decimal: return LLLnode.from_list( ['mul', orig, DECIMAL_DIVISOR], typ=BaseType('decimal', to.unit, to.positional), ) elif isinstance(frm, NullType): if to.typ not in ('int128', 'bool', 'uint256', 'address', 'bytes32', 'decimal'): # This is only to future proof the use of base_type_conversion. raise TypeMismatch( # pragma: no cover f"Cannot convert null-type object to type {to}", pos) return LLLnode.from_list(0, typ=to) # Integer literal conversion. elif (frm.typ, to.typ, frm.is_literal) == ('int128', 'uint256', True): return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate) else: raise TypeMismatch( f"Typecasting from base type {frm} to {to} unavailable", pos)
def call(self): from vyper.functions import ( DISPATCH_TABLE, ) if isinstance(self.expr.func, vy_ast.Name): function_name = self.expr.func.id if function_name in DISPATCH_TABLE: return DISPATCH_TABLE[function_name].build_LLL( self.expr, self.context) # Struct constructors do not need `self` prefix. elif function_name in self.context.structs: args = self.expr.args if len(args) != 1: raise StructureException( "Struct constructor is called with one argument only", self.expr, ) arg = args[0] if not isinstance(arg, vy_ast.Dict): raise TypeMismatch( "Struct can only be constructed with a dict", self.expr, ) return Expr.struct_literals(arg, function_name, self.context) # Contract assignment. Bar(<address>). elif function_name in self.context.sigs: ret, arg_lll = self._is_valid_contract_assign() if ret is True: arg_lll.typ = ContractType( function_name) # Cast to Correct contract type. return arg_lll else: raise TypeMismatch( "ContractType definition expects one address argument.", self.expr, ) else: err_msg = f"Not a top-level function: {function_name}" if function_name in [ x.split('(')[0] for x, _ in self.context.sigs['self'].items() ]: err_msg += f". Did you mean self.{function_name}?" raise StructureException(err_msg, self.expr) elif isinstance(self.expr.func, vy_ast.Attribute) and isinstance( self.expr.func.value, vy_ast.Name ) and self.expr.func.value.id == "self": # noqa: E501 return self_call.make_call(self.expr, self.context) else: return external_call.make_external_call(self.expr, self.context)
def to_bytes32(expr, args, kwargs, context): in_arg = args[0] input_type, _len = get_type(in_arg) if input_type == "Bytes": if _len > 32: raise TypeMismatch( f"Unable to convert bytes[{_len}] to bytes32, max length is too " "large.") with in_arg.cache_when_complex("bytes") as (b1, in_arg): op = load_op(in_arg.location) ofst = wordsize(in_arg.location) * DYNAMIC_ARRAY_OVERHEAD bytes_val = [op, ["add", in_arg, ofst]] # zero out any dirty bytes (which can happen in the last # word of a bytearray) len_ = get_bytearray_length(in_arg) num_zero_bits = LLLnode.from_list(["mul", ["sub", 32, len_], 8]) with num_zero_bits.cache_when_complex("bits") as (b2, num_zero_bits): ret = shl(num_zero_bits, shr(num_zero_bits, bytes_val)) ret = b1.resolve(b2.resolve(ret)) else: # literal ret = in_arg return LLLnode.from_list(ret, typ="bytes32", pos=getpos(expr))
def parse_List(self): if not len(self.expr.elements): return def get_out_type(lll_node): if isinstance(lll_node, ListType): return get_out_type(lll_node.subtype) return lll_node.typ lll_node = [] previous_type = None out_type = None for elt in self.expr.elements: current_lll_node = Expr(elt, self.context).lll_node if not out_type: out_type = current_lll_node.typ current_type = get_out_type(current_lll_node) if len(lll_node) > 0 and previous_type != current_type: raise TypeMismatch("Lists may only contain one type", self.expr) else: lll_node.append(current_lll_node) previous_type = current_type return LLLnode.from_list( ["multi"] + lll_node, typ=ListType(out_type, len(lll_node)), pos=getpos(self.expr), )
def validate_expected_type(node, expected_type): """ Validate that the given node matches the expected type(s) Raises if the node does not match one of the expected types. Arguments --------- node : VyperNode Vyper ast node. expected_type : Tuple | BaseType A type object, or tuple of type objects Returns ------- None """ given_types = _ExprTypeChecker().get_possible_types_from_node(node) if not isinstance(expected_type, tuple): expected_type = (expected_type, ) if isinstance(node, (vy_ast.List, vy_ast.Tuple)): # special case - for literal arrays or tuples we individually validate each item for expected in (i for i in expected_type if isinstance(i, ArrayDefinition)): if _validate_literal_array(node, expected): return else: for given, expected in itertools.product(given_types, expected_type): if expected.compare_type(given): return # validation failed, prepare a meaningful error message if len(expected_type) > 1: expected_str = f"one of {', '.join(str(i) for i in expected_type)}" else: expected_str = expected_type[0] if len(given_types) == 1 and getattr(given_types[0], "_is_callable", False): raise StructureException( f"{given_types[0]} cannot be referenced directly, it must be called", node) if not isinstance(node, (vy_ast.List, vy_ast.Tuple)) and node.get_descendants( vy_ast.Name, include_self=True): given = given_types[0] raise TypeMismatch( f"Given reference has type {given}, expected {expected_str}", node) else: if len(given_types) == 1: given_str = str(given_types[0]) else: types_str = sorted(str(i) for i in given_types) given_str = f"{', '.join(types_str[:1])} or {types_str[-1]}" raise InvalidType( f"Expected {expected_str} but literal can only be cast as {given_str}", node)
def types_from_Compare(self, node): # comparison: `x < y` if isinstance(node.op, vy_ast.In): # x in y left = self.get_possible_types_from_node(node.left) right = self.get_possible_types_from_node(node.right) if next((i for i in left if isinstance(i, ArrayDefinition)), False): raise InvalidOperation( "Left operand in membership comparison cannot be Array type", node.left, ) if next((i for i in right if not isinstance(i, ArrayDefinition)), False): raise InvalidOperation( "Right operand must be Array for membership comparison", node.right) types_list = [ i for i in left if _is_type_in_list(i, [i.value_type for i in right]) ] if not types_list: raise TypeMismatch( "Cannot perform membership comparison between dislike types", node) else: types_list = get_common_types(node.left, node.right) _validate_op(node, types_list, "validate_comparator") return [BoolDefinition()]
def list_literals(self): if not len(self.expr.elts): raise StructureException("List must have elements", self.expr) def get_out_type(lll_node): if isinstance(lll_node, ListType): return get_out_type(lll_node.subtype) return lll_node.typ o = [] previous_type = None out_type = None for elt in self.expr.elts: current_lll_node = Expr(elt, self.context).lll_node if not out_type: out_type = current_lll_node.typ current_type = get_out_type(current_lll_node) if len(o) > 0 and previous_type != current_type: raise TypeMismatch("Lists may only contain one type", self.expr) else: o.append(current_lll_node) previous_type = current_type return LLLnode.from_list( ["multi"] + o, typ=ListType(out_type, len(o)), pos=getpos(self.expr), )
def raw_log(expr, args, kwargs, context): if not isinstance(args[0], vy_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 TypeMismatch("Expecting a bytes32 argument as topic", elt) topics.append(arg) if args[1].typ == BaseType('bytes32'): placeholder = context.new_placeholder(BaseType('bytes32')) return LLLnode.from_list([ 'seq', ['mstore', placeholder, unwrap_location(args[1])], [ "log" + str(len(topics)), placeholder, 32, ] + topics ], typ=None, pos=getpos(expr)) 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), )
def get_min_val_for_type(typ: str) -> int: key = 'MIN_' + typ.upper() try: min_val, _ = BUILTIN_CONSTANTS[key] except KeyError as e: raise TypeMismatch(f"Not a signed type: {typ}") from e return min_val
def evaluate(self) -> VyperNode: """ Attempt to evaluate the comparison. Returns ------- NameConstant Node representing the result of the evaluation. """ left, right = self.left, self.right if not isinstance(left, Constant): raise UnfoldableNode("Node contains invalid field(s) for evaluation") if isinstance(self.op, In): if not isinstance(right, List): raise UnfoldableNode("Node contains invalid field(s) for evaluation") if next((i for i in right.elements if not isinstance(i, Constant)), None): raise UnfoldableNode("Node contains invalid field(s) for evaluation") if len(set([type(i) for i in right.elements])) > 1: raise UnfoldableNode("List contains multiple literal types") value = self.op._op(left.value, [i.value for i in right.elements]) return NameConstant.from_node(self, value=value) if not isinstance(left, type(right)): raise UnfoldableNode("Cannot compare different literal types") if not isinstance(self.op, (Eq, NotEq)) and not isinstance(left, (Int, Decimal)): raise TypeMismatch(f"Invalid literal types for {self.op.description} comparison", self) value = self.op._op(left.value, right.value) return NameConstant.from_node(self, value=value)
def _validate_arg_types(self, node): num_args = len( self._inputs) # the number of args the signature indicates expect_num_args = num_args if self._has_varargs: # note special meaning for -1 in validate_call_args API expect_num_args = (num_args, -1) validate_call_args(node, expect_num_args, self._kwargs) for arg, (_, expected) in zip(node.args, self._inputs): self._validate_single(arg, expected) for kwarg in node.keywords: kwarg_settings = self._kwargs[kwarg.arg] if kwarg_settings.require_literal and not isinstance( kwarg.value, vy_ast.Constant): raise TypeMismatch("Value for kwarg must be a literal", kwarg.value) self._validate_single(kwarg.value, kwarg_settings.typ) # typecheck varargs. we don't have type info from the signature, # so ensure that the types of the args can be inferred exactly. varargs = node.args[num_args:] if len(varargs) > 0: assert self._has_varargs # double check validate_call_args for arg in varargs: # call get_exact_type_from_node for its side effects - # ensures the type can be inferred exactly. get_exact_type_from_node(arg)
def minmax(expr, args, kwargs, context, comparator): def _can_compare_with_uint256(operand): if operand.typ.typ == 'uint256': return True elif operand.typ.typ == 'int128' and operand.typ.is_literal and SizeLimits.in_bounds( 'uint256', operand.value): # noqa: E501 return True return False left, right = args[0], args[1] if left.typ.typ == right.typ.typ: if left.typ.typ != 'uint256': # if comparing like types that are not uint256, use SLT or SGT comparator = f's{comparator}' o = ['if', [comparator, '_l', '_r'], '_r', '_l'] otyp = left.typ otyp.is_literal = False elif _can_compare_with_uint256(left) and _can_compare_with_uint256(right): o = ['if', [comparator, '_l', '_r'], '_r', '_l'] if right.typ.typ == 'uint256': otyp = right.typ else: otyp = left.typ otyp.is_literal = False else: raise TypeMismatch( f"Minmax types incompatible: {left.typ.typ} {right.typ.typ}") return LLLnode.from_list( ['with', '_l', left, ['with', '_r', right, o]], typ=otyp, pos=getpos(expr), )
def _op(self, left, right): if isinstance(left, decimal.Decimal): raise TypeMismatch( "Cannot perform exponentiation on decimal values.", self._parent) if right < 0: raise InvalidOperation("Cannot calculate a negative power", self._parent) return int(left**right)
def _check_implicit_conversion(self, var_id, sub): target_typ = self.context.vars[var_id].typ assign_typ = sub.typ if isinstance(target_typ, BaseType) and isinstance( assign_typ, BaseType): if not assign_typ.is_literal and assign_typ.typ != target_typ.typ: raise TypeMismatch( f'Invalid type {assign_typ.typ}, expected: {target_typ.typ}', self.stmt)
def subscript(self): sub = Expr.parse_variable_location(self.expr.value, self.context) if isinstance(sub.typ, (MappingType, ListType)): if not isinstance(self.expr.slice, vy_ast.Index): raise StructureException( "Array access must access a single element, not a slice", self.expr, ) index = Expr.parse_value_expr(self.expr.slice.value, self.context) elif isinstance(sub.typ, TupleType): if not isinstance(self.expr.slice.value, vy_ast.Int) or self.expr.slice.value.n < 0 or self.expr.slice.value.n >= len(sub.typ.members): # noqa: E501 raise TypeMismatch("Tuple index invalid", self.expr.slice.value) index = self.expr.slice.value.n else: raise TypeMismatch("Bad subscript attempt", self.expr.value) o = add_variable_offset(sub, index, pos=getpos(self.expr)) o.mutable = sub.mutable return o
def raw_call(expr, args, kwargs, context): to, data = args gas, value, outsize, delegate_call = ( kwargs['gas'], kwargs['value'], kwargs['outsize'], kwargs['is_delegate_call'], ) if delegate_call.typ.is_literal is False: raise TypeMismatch( 'The delegate_call parameter has to be a static/literal boolean value.' ) if context.is_constant(): raise ConstancyViolation( f"Cannot make calls from {context.pp_constancy()}", expr, ) 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', ) # build LLL for call or delegatecall common_call_lll = [ ['add', placeholder_node, 32], ['mload', placeholder_node], # if there is no return value, the return offset can be 0 ['add', output_node, 32] if outsize else 0, outsize ] if delegate_call.value == 1: call_lll = ['delegatecall', gas, to] + common_call_lll else: call_lll = ['call', gas, to, value] + common_call_lll # build sequence LLL if outsize: # only copy the return value to memory if outsize > 0 seq = [ 'seq', copier, ['assert', call_lll], ['mstore', output_node, outsize], output_node ] typ = ByteArrayType(outsize) else: seq = ['seq', copier, ['assert', call_lll]] typ = None return LLLnode.from_list(seq, typ=typ, location="memory", pos=getpos(expr))
def assign(self): # Assignment (e.g. x[4] = y) with self.context.assignment_scope(): sub = Expr(self.stmt.value, self.context).lll_node is_valid_rlp_list_assign = ( isinstance(self.stmt.value, vy_ast.Call) ) and getattr(self.stmt.value.func, 'id', '') == 'RLPList' # Determine if it's an RLPList assignment. if is_valid_rlp_list_assign: pos = self.context.new_variable(self.stmt.target.id, sub.typ) variable_loc = LLLnode.from_list( pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.target.id, ) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) else: # Error check when assigning to declared variable if isinstance(self.stmt.target, vy_ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.target.id not in self.context.vars: raise VariableDeclarationException("Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.target.id, sub) is_valid_tuple_assign = ( isinstance(self.stmt.target, vy_ast.Tuple) ) and isinstance(self.stmt.value, vy_ast.Tuple) # Do no allow tuple-to-tuple assignment if is_valid_tuple_assign: raise VariableDeclarationException( "Tuple to tuple assignment not supported", self.stmt, ) # Checks to see if assignment is valid target = self.get_target(self.stmt.target) if isinstance(target.typ, ContractType) and not isinstance(sub.typ, ContractType): raise TypeMismatch( 'Contract assignment expects casted address: ' f'{target.typ}(<address_var>)', self.stmt ) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def base_type_conversion(orig, frm, to, pos, in_function_call=False): orig = unwrap_location(orig) # do the base type check so we can use BaseType attributes if not isinstance(frm, BaseType) or not isinstance(to, BaseType): raise TypeMismatch( f"Base type conversion from or to non-base type: {frm} {to}", pos) if getattr(frm, 'is_literal', False): if frm.typ in ('int128', 'uint256'): if not SizeLimits.in_bounds(frm.typ, orig.value): raise InvalidLiteral(f"Number out of range: {orig.value}", pos) if to.typ in ('int128', 'uint256'): if not SizeLimits.in_bounds(to.typ, orig.value): raise InvalidLiteral(f"Number out of range: {orig.value}", pos) is_decimal_int128_conversion = frm.typ == 'int128' and to.typ == 'decimal' is_same_type = frm.typ == to.typ is_literal_conversion = frm.is_literal and (frm.typ, to.typ) == ('int128', 'uint256') is_address_conversion = isinstance(frm, ContractType) and to.typ == 'address' if not (is_same_type or is_literal_conversion or is_address_conversion or is_decimal_int128_conversion): raise TypeMismatch( f"Typecasting from base type {frm} to {to} unavailable", pos) # handle None value inserted by `empty()` if orig.value is None: return LLLnode.from_list(0, typ=to) if is_decimal_int128_conversion: return LLLnode.from_list( ['mul', orig, DECIMAL_DIVISOR], typ=BaseType('decimal'), ) return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate)
def pack_logging_topics(event_id, args, expected_topics, context, pos): topics = [event_id] code_pos = pos for pos, expected_topic in enumerate(expected_topics): expected_type = expected_topic.typ arg = args[pos] value = Expr(arg, context).lll_node arg_type = value.typ if isinstance(arg_type, ByteArrayLike) and isinstance( expected_type, ByteArrayLike): if arg_type.maxlen > expected_type.maxlen: raise TypeMismatch( f"Topic input bytes are too big: {arg_type} {expected_type}", code_pos) if isinstance(arg, vy_ast.Str): bytez, bytez_length = string_to_bytes(arg.s) if len(bytez) > 32: raise InvalidLiteral( "Can only log a maximum of 32 bytes at a time.", code_pos) topics.append( bytes_to_int(bytez + b'\x00' * (32 - bytez_length))) else: if value.location == "memory": size = ['mload', value] elif value.location == "storage": size = ['sload', ['sha3_32', value]] topics.append(byte_array_to_num(value, arg, 'uint256', size)) else: if arg_type != expected_type: raise TypeMismatch( f"Invalid type for logging topic, got {arg_type} expected {expected_type}", value.pos) value = unwrap_location(value) value = base_type_conversion(value, arg_type, expected_type, pos=code_pos) topics.append(value) return topics
def ann_assign(self): with self.context.assignment_scope(): typ = parse_type( self.stmt.annotation, location='memory', custom_units=self.context.custom_units, custom_structs=self.context.structs, constants=self.context.constants, ) if isinstance(self.stmt.target, vy_ast.Attribute): raise TypeMismatch( f'May not set type for field {self.stmt.target.attr}', self.stmt, ) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) if self.stmt.value is None: raise StructureException( 'New variables must be initialized explicitly', self.stmt) sub = Expr(self.stmt.value, self.context).lll_node # Disallow assignment to None if isinstance(sub.typ, NullType): raise InvalidLiteral( ('Assignment to None is not allowed, use a default ' 'value or built-in `clear()`.'), self.stmt) is_literal_bytes32_assign = (isinstance(sub.typ, ByteArrayType) and sub.typ.maxlen == 32 and isinstance(typ, BaseType) and typ.typ == 'bytes32' and sub.typ.is_literal) # If bytes[32] to bytes32 assignment rewrite sub as bytes32. if is_literal_bytes32_assign: sub = LLLnode( bytes_to_int(self.stmt.value.s), typ=BaseType('bytes32'), pos=getpos(self.stmt), ) self._check_valid_assign(sub) self._check_same_variable_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)) # o.pos = getpos(self.stmt) # TODO: Should this be here like in assign()? return o
def _to_bytelike(expr, args, kwargs, context, bytetype): if bytetype == 'string': ReturnType = StringType elif bytetype == 'bytes': ReturnType = ByteArrayType else: raise TypeMismatch(f'Invalid {bytetype} supplied') in_arg = args[0] if in_arg.typ.maxlen > args[1].slice.value.n: raise TypeMismatch( f'Cannot convert as input {bytetype} are larger than max length', expr, ) return LLLnode(value=in_arg.value, args=in_arg.args, typ=ReturnType(in_arg.typ.maxlen), pos=getpos(expr), location=in_arg.location)
def parse_assert(self): with self.context.assertion_scope(): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if not self.is_bool_expr(test_expr): raise TypeMismatch('Only boolean expressions allowed', self.stmt.test) if self.stmt.msg: return self._assert_reason(test_expr, self.stmt.msg) else: return LLLnode.from_list(['assert', test_expr], typ=None, pos=getpos(self.stmt))
def make_byte_array_copier(dst, src, pos=None): assert isinstance(src.typ, ByteArrayLike) assert isinstance(dst.typ, ByteArrayLike) if src.typ.maxlen > dst.typ.maxlen: raise TypeMismatch(f"Cannot cast from {src.typ} to {dst.typ}") # stricter check for zeroing a byte array. if src.value == "~empty" and src.typ.maxlen != dst.typ.maxlen: raise TypeMismatch( f"Bad type for clearing bytes: expected {dst.typ} but got {src.typ}" ) # pragma: notest if src.value == "~empty": # set length word to 0. return LLLnode.from_list([store_op(dst.location), dst, 0], pos=pos) with src.cache_when_complex("src") as (builder, src): n_bytes = ["add", get_bytearray_length(src), 32] max_bytes = src.typ.memory_bytes_required return builder.resolve( copy_bytes(dst, src, n_bytes, max_bytes, pos=pos))
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, (vy_ast.Add, vy_ast.Sub, vy_ast.Mult, vy_ast.Div, vy_ast.Mod) ): raise StructureException("Unsupported operator for augassign", self.stmt) if not isinstance(target.typ, BaseType): raise TypeMismatch( "Can only use aug-assign operators with simple types!", self.stmt.target ) if target.location == 'storage': o = Expr.parse_value_expr( vy_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, end_lineno=self.stmt.end_lineno, end_col_offset=self.stmt.end_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( vy_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, end_lineno=self.stmt.end_lineno, end_col_offset=self.stmt.end_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))
def boolean_operations(self): # Iterate through values for value in self.expr.values: # Check for calls at assignment if self.context.in_assignment and isinstance(value, vy_ast.Call): raise StructureException( "Boolean operations with calls may not be performed on assignment", self.expr, ) # Check for boolean operations with non-boolean inputs _expr = Expr.parse_value_expr(value, self.context) if not is_base_type(_expr.typ, 'bool'): raise TypeMismatch( "Boolean operations can only be between booleans!", self.expr, ) # TODO: Handle special case of literals and simplify at compile time # Check for valid ops if isinstance(self.expr.op, vy_ast.And): op = 'and' elif isinstance(self.expr.op, vy_ast.Or): op = 'or' else: raise Exception("Unsupported bool op: " + self.expr.op) # Handle different numbers of inputs count = len(self.expr.values) if count < 2: raise StructureException( "Expected at least two arguments for a bool op", self.expr) elif count == 2: left = Expr.parse_value_expr(self.expr.values[0], self.context) right = Expr.parse_value_expr(self.expr.values[1], self.context) return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) else: left = Expr.parse_value_expr(self.expr.values[0], self.context) right = Expr.parse_value_expr(self.expr.values[1], self.context) p = ['seq', [op, left, right]] values = self.expr.values[2:] while len(values) > 0: value = Expr.parse_value_expr(values[0], self.context) p = [op, value, p] values = values[1:] return LLLnode.from_list(p, typ='bool', pos=getpos(self.expr))
def _check_valid_assign(self, sub): if (isinstance(self.stmt.annotation, vy_ast.Name) and self.stmt.annotation.id == 'bytes32'): # CMC 04/07/2020 this check could be much clearer, more like: # if isinstance(ByteArrayLike) and maxlen == 32 # or isinstance(BaseType) and typ.typ == 'bytes32' # then GOOD # else RAISE if isinstance(sub.typ, ByteArrayLike): if sub.typ.maxlen != 32: raise TypeMismatch( 'Invalid type, expected: bytes32. String is incorrect length.', self.stmt) return elif isinstance(sub.typ, BaseType): if sub.typ.typ != 'bytes32': raise TypeMismatch('Invalid type, expected: bytes32', self.stmt) return else: raise TypeMismatch("Invalid type, expected: bytes32", self.stmt) elif isinstance(self.stmt.annotation, vy_ast.Subscript): # check list assign: if not isinstance(sub.typ, (ListType, ByteArrayLike)): raise TypeMismatch( f'Invalid type, expected: {self.stmt.annotation.value.id},' f' got: {sub.typ}', self.stmt) elif sub.typ is None: # Check that the object to be assigned is not of NoneType raise TypeMismatch( f"Invalid type, expected {self.stmt.annotation.id}", self.stmt) elif isinstance(sub.typ, StructType): # This needs to get more sophisticated in the presence of # foreign structs. if not sub.typ.name == self.stmt.annotation.id: raise TypeMismatch( f"Invalid type, expected {self.stmt.annotation.id}", self.stmt) # Check that the integer literal, can be assigned to uint256 if necessary. elif (self.stmt.annotation.id, sub.typ.typ) == ('uint256', 'int128') and sub.typ.is_literal: if not SizeLimits.in_bounds('uint256', sub.value): raise InvalidLiteral( 'Invalid uint256 assignment, value not in uint256 range.', self.stmt) elif self.stmt.annotation.id != sub.typ.typ: raise TypeMismatch( f'Invalid type {sub.typ.typ}, expected: {self.stmt.annotation.id}', self.stmt, ) else: return True