neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
fn pop_value(stack: &mut Vec<AbstractValue>) -> Result<AbstractValue, ()> {
    stack.pop().ok_or(())
}

fn pop_n(stack: &mut Vec<AbstractValue>, n: usize) -> Result<(), ()> {
    if stack.len() < n {
        return Err(());
    }
    for _ in 0..n {
        stack.pop();
    }
    Ok(())
}

fn apply_instruction(state: &mut AbstractState, instr: &ir::Instruction, ir_module: &ir::Module) -> Result<(), ()> {
    use ir::Instruction::{Drop, LoadParameter, StoreParameter, PushLiteral, Return, ReturnVoid, ReturnDefault, Abort, AbortMsg, Throw, BinaryOp, LoadState, StoreState, LoadStorageDynamic, LoadLocal, StoreLocal, LoadMappingElement, StoreMappingElement, StoreArrayDeepCopy, LoadStructField, StoreStructField, LoadStructArrayElement, StoreStructArrayElement, LoadStructFieldMappingElement, StoreStructFieldMappingElement, LoadRuntimeValue, GetSize, CallFunction, CallBuiltin, PushFunctionOffset, CallIndirect, EmitEvent, EmitEventByName, Convert, IsType, NewBuffer, NewArray, NewMap, ArrayGet, ArraySet, HasKey, MemCpy, Substr, ReverseItems, BitwiseNot, LogicalNot, Try, EndTry, Jump, Label, JumpIf, Dup, Swap};

    match instr {
        Drop(_) => {
            pop_value(&mut state.stack)?;
        }
        LoadParameter(_) => state.stack.push(AbstractValue::Unknown),
        StoreParameter(_) => {
            // Task #156 — write-to-parameter mirrors StoreLocal for stack-shape
            // analysis: consumes the top-of-stack value and discards it from
            // the abstract tracker (we don't model parameter slot state).
            pop_value(&mut state.stack)?;
        }
        PushLiteral(lit) => state.stack.push(AbstractValue::literal(lit.clone())),
        Return | ReturnVoid | ReturnDefault(_) | Abort => {}
        AbortMsg | Throw => {
            pop_value(&mut state.stack)?;
        }
        BinaryOp(_) => {
            pop_n(&mut state.stack, 2)?;
            state.stack.push(AbstractValue::Unknown);
        }
        LoadState(_) => state.stack.push(AbstractValue::Unknown),
        StoreState(_) => {
            pop_value(&mut state.stack)?;
        }
        LoadStorageDynamic => {
            pop_value(&mut state.stack)?;
            state.stack.push(AbstractValue::Unknown);
        }
        LoadLocal(index) => {
            let value = state
                .locals
                .get(*index)
                .cloned()
                .unwrap_or(AbstractValue::Unknown);
            state.stack.push(value);
        }
        StoreLocal(index) => {
            let value = pop_value(&mut state.stack)?;
            if let Some(slot) = state.locals.get_mut(*index) {
                *slot = value;
            }
        }
        LoadMappingElement { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len())?;
            state.stack.push(AbstractValue::Unknown);
        }
        StoreMappingElement { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len() + 1)?;
        }
        StoreArrayDeepCopy { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len() + 1)?;
        }
        LoadStructField { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len())?;
            state.stack.push(AbstractValue::Unknown);
        }
        StoreStructField { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len() + 1)?;
        }
        LoadStructArrayElement { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len() + 1)?;
            state.stack.push(AbstractValue::Unknown);
        }
        StoreStructArrayElement { key_types, .. } => {
            pop_n(&mut state.stack, key_types.len() + 2)?;
        }
        LoadStructFieldMappingElement {
            key_types,
            trailing_key_types,
            ..
        } => {
            pop_n(&mut state.stack, key_types.len() + trailing_key_types.len())?;
            state.stack.push(AbstractValue::Unknown);
        }
        StoreStructFieldMappingElement {
            key_types,
            trailing_key_types,
            ..
        } => {
            pop_n(
                &mut state.stack,
                key_types.len() + trailing_key_types.len() + 1,
            )?;
        }
        LoadRuntimeValue(_) => state.stack.push(AbstractValue::Unknown),
        GetSize => {
            pop_value(&mut state.stack)?;
            state.stack.push(AbstractValue::Unknown);
        }
        CallFunction { name, arg_count } => {
            pop_n(&mut state.stack, *arg_count)?;
            let returns_value = ir_module.get_function(name).map(|f| !f.returns.is_empty()).unwrap_or(true);
            if returns_value {
                state.stack.push(AbstractValue::Unknown);
            }
        }
        CallBuiltin { builtin, arg_count } => {
            pop_n(&mut state.stack, *arg_count)?;
            match builtin {
                ir::BuiltinCall::Syscall(name)
                    if name == "System.Runtime.GetExecutingScriptHash" =>
                {
                    state.stack.push(AbstractValue::executing_script_hash());
                }
                _ => state.stack.push(AbstractValue::Unknown),
            }
        }
        PushFunctionOffset { .. } => {
            // Task #186 — pushes the target function's bytecode offset as an
            // integer literal. Stack effect: +1 (one new opaque integer).
            state.stack.push(AbstractValue::Unknown);
        }
        CallIndirect { arg_count, has_return } => {
            // Task #186 — indirect call through a function-pointer value.
            // Consumes `arg_count` arguments + 1 target offset; pushes a
            // return value iff the callee returns something.
            pop_n(&mut state.stack, *arg_count + 1)?;
            if *has_return {
                state.stack.push(AbstractValue::Unknown);
            }
        }
        EmitEvent { arg_count, .. } | EmitEventByName { arg_count, .. } => {
            pop_n(&mut state.stack, arg_count + 1)?;
        }
        Convert { .. } => {
            let value = pop_value(&mut state.stack)?;
            state.stack.push(value);
        }
        IsType { .. } => {
            pop_value(&mut state.stack)?;
            state.stack.push(AbstractValue::Unknown);
        }
        NewBuffer => {
            pop_value(&mut state.stack)?;
            state.stack.push(AbstractValue::Unknown);
        }
        NewArray { .. } => {
            pop_value(&mut state.stack)?;
            state.stack.push(AbstractValue::Unknown);
        }
        NewMap => {
            state.stack.push(AbstractValue::Unknown);
        }
        ArrayGet => {
            pop_n(&mut state.stack, 2)?;
            state.stack.push(AbstractValue::Unknown);
        }
        ArraySet => {
            pop_n(&mut state.stack, 3)?;
        }
        HasKey => {
            pop_n(&mut state.stack, 2)?;
            state.stack.push(AbstractValue::Unknown);
        }
        MemCpy => {
            // Real NeoVM: Pop 5, Push 0 (in-place buffer modification).
            pop_n(&mut state.stack, 5)?;
        }
        Substr => {
            // Substr pops [bytes, index, count] and pushes [bytes_substr].
            pop_n(&mut state.stack, 3)?;
            state.stack.push(AbstractValue::Unknown);
        }
        ReverseItems => {
            pop_value(&mut state.stack)?;
        }
        BitwiseNot | LogicalNot => {
            pop_value(&mut state.stack)?;
            state.stack.push(AbstractValue::Unknown);
        }
        Try { .. } | EndTry { .. } | Jump { .. } | Label(_) => {}
        JumpIf { .. } => {
            pop_value(&mut state.stack)?;
        }
        Dup => {
            let value = state.stack.last().cloned().unwrap_or(AbstractValue::Unknown);
            state.stack.push(value);
        }
        Swap => {
            let len = state.stack.len();
            if len >= 2 {
                state.stack.swap(len - 1, len - 2);
            }
        }
    }

    Ok(())
}