neo_solidity/runtime/execution/helpers/arithmetic/
basic_ops.rs1macro_rules! arithmetic_op {
22 ($fn_name:ident, $op_name:literal, $op_sym:literal, $checked:ident, $wrapping:ident, $error_kind:literal) => {
23 fn $fn_name(&self, a: StackItem, b: StackItem) -> Result<StackItem, RuntimeError> {
24 let use_unsigned =
25 matches!(a, StackItem::UnsignedInteger(_)) || matches!(b, StackItem::UnsignedInteger(_));
26
27 if use_unsigned {
28 let x = self.coerce_item_to_u64(&a).ok_or_else(|| RuntimeError::ExecutionError {
29 message: concat!("Invalid operands for ", $op_name).to_string(),
30 })?;
31 let y = self.coerce_item_to_u64(&b).ok_or_else(|| RuntimeError::ExecutionError {
32 message: concat!("Invalid operands for ", $op_name).to_string(),
33 })?;
34 if self.strict_arithmetic {
35 x.$checked(y)
36 .map(StackItem::UnsignedInteger)
37 .ok_or_else(|| RuntimeError::ExecutionError {
38 message: format!(
39 "Unsigned integer {} in {}: {} {} {}",
40 $error_kind, $op_name, x, $op_sym, y
41 ),
42 })
43 } else {
44 Ok(StackItem::UnsignedInteger(x.$wrapping(y)))
45 }
46 } else {
47 let x = self.coerce_item_to_i64(&a).ok_or_else(|| RuntimeError::ExecutionError {
48 message: concat!("Invalid operands for ", $op_name).to_string(),
49 })?;
50 let y = self.coerce_item_to_i64(&b).ok_or_else(|| RuntimeError::ExecutionError {
51 message: concat!("Invalid operands for ", $op_name).to_string(),
52 })?;
53 if self.strict_arithmetic {
54 x.$checked(y)
55 .map(StackItem::Integer)
56 .ok_or_else(|| RuntimeError::ExecutionError {
57 message: format!(
58 "Integer {} in {}: {} {} {}",
59 $error_kind, $op_name, x, $op_sym, y
60 ),
61 })
62 } else {
63 Ok(StackItem::Integer(x.$wrapping(y)))
64 }
65 }
66 }
67 };
68}
69
70macro_rules! divmod_op {
75 ($fn_name:ident, $op_name:literal, $checked_fn:ident, $error_msg:literal) => {
76 fn $fn_name(&self, a: StackItem, b: StackItem) -> Result<StackItem, RuntimeError> {
77 let use_unsigned =
78 matches!(a, StackItem::UnsignedInteger(_)) || matches!(b, StackItem::UnsignedInteger(_));
79
80 if use_unsigned {
81 let x = self.coerce_item_to_u64(&a).ok_or_else(|| RuntimeError::ExecutionError {
82 message: concat!("Invalid operands for ", $op_name).to_string(),
83 })?;
84 let y = self.coerce_item_to_u64(&b).ok_or_else(|| RuntimeError::ExecutionError {
85 message: concat!("Invalid operands for ", $op_name).to_string(),
86 })?;
87 if y == 0 {
88 return Err(RuntimeError::ExecutionError {
89 message: $error_msg.to_string(),
90 });
91 }
92 Ok(StackItem::UnsignedInteger(x.$checked_fn(y).unwrap()))
93 } else {
94 let x = self.coerce_item_to_i64(&a).ok_or_else(|| RuntimeError::ExecutionError {
95 message: concat!("Invalid operands for ", $op_name).to_string(),
96 })?;
97 let y = self.coerce_item_to_i64(&b).ok_or_else(|| RuntimeError::ExecutionError {
98 message: concat!("Invalid operands for ", $op_name).to_string(),
99 })?;
100 if y == 0 {
101 return Err(RuntimeError::ExecutionError {
102 message: $error_msg.to_string(),
103 });
104 }
105 x.$checked_fn(y)
106 .map(StackItem::Integer)
107 .ok_or_else(|| RuntimeError::ExecutionError {
108 message: format!(
109 "Signed integer overflow in {}: {} / -1",
110 $op_name, x
111 ),
112 })
113 }
114 }
115 };
116}
117
118impl ExecutionContext {
119 fn coerce_item_to_i64(&self, item: &StackItem) -> Option<i64> {
120 match item {
121 StackItem::Integer(value) => Some(*value),
122 StackItem::UnsignedInteger(value) => i64::try_from(*value).ok(),
123 StackItem::Boolean(value) => Some(if *value { 1 } else { 0 }),
124 StackItem::Null => Some(0),
125 StackItem::ByteArray(bytes) => {
126 let bytes = bytes.borrow();
127 if bytes.is_empty() {
128 return Some(0);
129 }
130 let len = bytes.len().min(8);
135 let mut buf = [0u8; 8];
136 buf[..len].copy_from_slice(&bytes[..len]);
137 Some(i64::from_le_bytes(buf))
138 }
139 _ => None,
140 }
141 }
142
143 fn coerce_item_to_u64(&self, item: &StackItem) -> Option<u64> {
144 match item {
145 StackItem::UnsignedInteger(value) => Some(*value),
146 StackItem::Integer(value) => {
147 if *value < 0 {
148 None
149 } else {
150 Some(*value as u64)
151 }
152 }
153 StackItem::Boolean(value) => Some(if *value { 1 } else { 0 }),
154 StackItem::Null => Some(0),
155 StackItem::ByteArray(bytes) => {
156 let bytes = bytes.borrow();
157 if bytes.is_empty() {
158 return Some(0);
159 }
160 let len = bytes.len().min(8);
163 let mut buf = [0u8; 8];
164 buf[..len].copy_from_slice(&bytes[..len]);
165 Some(u64::from_le_bytes(buf))
166 }
167 _ => None,
168 }
169 }
170
171 arithmetic_op!(add_stack_items, "ADD", "+", checked_add, wrapping_add, "overflow");
173 arithmetic_op!(sub_stack_items, "SUB", "-", checked_sub, wrapping_sub, "underflow");
174 arithmetic_op!(mul_stack_items, "MUL", "*", checked_mul, wrapping_mul, "overflow");
175 divmod_op!(div_stack_items, "DIV", checked_div, "Division by zero");
176 divmod_op!(mod_stack_items, "MOD", checked_rem, "Modulo by zero");
177}