neo_solidity/cli/bytecode/bytecode_helpers/
ops_and_literals.rs

1fn emit_binary_op(bytecode: &mut Vec<u8>, operator: ir::BinaryOperator) {
2    let opcode = match operator {
3        ir::BinaryOperator::Add => 0x9E,
4        ir::BinaryOperator::Sub => 0x9F,
5        ir::BinaryOperator::Mul => 0xA0,
6        ir::BinaryOperator::Div => 0xA1,
7        ir::BinaryOperator::Mod => 0xA2,
8        ir::BinaryOperator::BitAnd => 0x91,
9        ir::BinaryOperator::BitOr => 0x92,
10        ir::BinaryOperator::BitXor => 0x93,
11        ir::BinaryOperator::Shl => 0xA8,
12        ir::BinaryOperator::Shr => 0xA9,
13        ir::BinaryOperator::Lt => 0xB5,
14        ir::BinaryOperator::Le => 0xB6,
15        ir::BinaryOperator::Gt => 0xB7,
16        ir::BinaryOperator::Ge => 0xB8,
17        ir::BinaryOperator::Eq => 0x97,
18        ir::BinaryOperator::Ne => 0x98,
19    };
20    bytecode.push(opcode);
21}
22
23fn push_literal_value(bytecode: &mut Vec<u8>, literal: &LiteralValue) {
24    match literal {
25        LiteralValue::Integer(value) => push_integer_bigint(bytecode, value),
26        // Use NeoVM boolean opcodes so returned values match the manifest ABI type.
27        LiteralValue::Boolean(true) => bytecode.push(0x08),  // PUSHT
28        LiteralValue::Boolean(false) => bytecode.push(0x09), // PUSHF
29        LiteralValue::String(bytes) => push_data(bytecode, bytes),
30        LiteralValue::ByteArray(bytes) => push_data(bytecode, bytes),
31        LiteralValue::Address(bytes) => push_data(bytecode, bytes),
32        LiteralValue::Null => bytecode.push(0x0B),
33    }
34}
35
36fn push_integer_bigint(bytecode: &mut Vec<u8>, value: &BigInt) {
37    if value.is_zero() {
38        bytecode.push(0x10);
39        return;
40    }
41
42    if *value == BigInt::from(-1) {
43        bytecode.push(0x0F);
44        return;
45    }
46
47    if value.is_positive() {
48        if let Some(n) = value.to_u8() {
49            if n <= 16 {
50                bytecode.push(0x10 + n);
51                return;
52            }
53        }
54    }
55
56    // NeoVM integer literals should be pushed as Integer stack items so that operations like
57    // `EQUAL` behave correctly for Solidity numeric types. Use the dedicated PUSHINT opcodes
58    // whenever the value fits in their width, falling back to raw bytes only when needed.
59    if let Some(n) = value.to_i64() {
60        if (i8::MIN as i64..=i8::MAX as i64).contains(&n) {
61            bytecode.push(0x00); // PUSHINT8
62            bytecode.push(n as i8 as u8);
63            return;
64        }
65        if (i16::MIN as i64..=i16::MAX as i64).contains(&n) {
66            bytecode.push(0x01); // PUSHINT16
67            bytecode.extend_from_slice(&(n as i16).to_le_bytes());
68            return;
69        }
70        if (i32::MIN as i64..=i32::MAX as i64).contains(&n) {
71            bytecode.push(0x02); // PUSHINT32
72            bytecode.extend_from_slice(&(n as i32).to_le_bytes());
73            return;
74        }
75        bytecode.push(0x03); // PUSHINT64
76        bytecode.extend_from_slice(&n.to_le_bytes());
77        return;
78    }
79
80    let bytes = value.to_signed_bytes_le();
81    if bytes.len() <= 16 {
82        bytecode.push(0x04); // PUSHINT128
83        let fill = if value.is_negative() { 0xFF } else { 0x00 };
84        let mut buf = [fill; 16];
85        buf[..bytes.len()].copy_from_slice(&bytes);
86        bytecode.extend_from_slice(&buf);
87        return;
88    }
89    if bytes.len() <= 32 {
90        bytecode.push(0x05); // PUSHINT256
91        let fill = if value.is_negative() { 0xFF } else { 0x00 };
92        let mut buf = [fill; 32];
93        buf[..bytes.len()].copy_from_slice(&bytes);
94        bytecode.extend_from_slice(&buf);
95        return;
96    }
97
98    // Fallback: out-of-range literal (bigger than 256-bit signed). NeoVM integer literals are
99    // signed, so we cannot encode values like `2^256-1` (Solidity `uint256.max`) with PUSHINT256
100    // without changing their numeric meaning. Push the signed little-endian bytes and coerce them
101    // into an Integer stack item via `value + 0`.
102    push_data(bytecode, &bytes);
103    bytecode.push(0x10); // PUSH0
104    bytecode.push(0x9E); // ADD
105}