neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
fn resolve_struct_type_by_name(ctx: &LoweringContext<'_>, name: &str) -> Option<ValueType> {
    ctx.defined_struct_types
        .iter()
        .chain(ctx.state_types.iter())
        .chain(ctx.param_types.iter())
        .chain(ctx.return_types.iter())
        .chain(ctx.local_types.values())
        .find_map(|ty| find_named_struct_type(ty, name))
}

fn try_lower_struct_constructor_call(
    func: &Expression,
    args: &[Expression],
    ctx: &mut LoweringContext,
    instructions: &mut Vec<Instruction>,
) -> Option<bool> {
    let struct_name = match func {
        Expression::Variable(identifier) => identifier.name.clone(),
        Expression::MemberAccess(_, _, identifier) => identifier.name.clone(),
        _ => return None,
    };

    let struct_type = resolve_struct_type_by_name(ctx, &struct_name)?;
    let ValueType::Struct { fields, .. } = &struct_type else {
        return None;
    };

    Some(lower_struct_constructor_positional(
        &struct_name,
        &struct_type,
        fields,
        args,
        ctx,
        instructions,
    ))
}

fn try_lower_struct_constructor_named_call(
    func: &Expression,
    args: &[solang_parser::pt::NamedArgument],
    ctx: &mut LoweringContext,
    instructions: &mut Vec<Instruction>,
) -> Option<bool> {
    let struct_name = match func {
        Expression::Variable(identifier) => identifier.name.clone(),
        Expression::MemberAccess(_, _, identifier) => identifier.name.clone(),
        _ => return None,
    };

    let struct_type = resolve_struct_type_by_name(ctx, &struct_name)?;
    let ValueType::Struct { fields, .. } = &struct_type else {
        return None;
    };

    Some(lower_struct_constructor_named(
        &struct_name,
        &struct_type,
        fields,
        args,
        ctx,
        instructions,
    ))
}

fn lower_struct_constructor_positional(
    call_name: &str,
    struct_type: &ValueType,
    fields: &[StructField],
    args: &[Expression],
    ctx: &mut LoweringContext,
    instructions: &mut Vec<Instruction>,
) -> bool {
    let tmp_id = ctx.next_label();
    let struct_local = ctx.allocate_local(format!("__struct_ctor_{tmp_id}"), Some(struct_type.clone()));

    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
        fields.len() as u64,
    ))));
    instructions.push(Instruction::NewArray {
        element_type: ValueType::Any,
    });
    instructions.push(Instruction::StoreLocal(struct_local));

    let mut success = true;

    for (index, field) in fields.iter().enumerate() {
        instructions.push(Instruction::LoadLocal(struct_local));
        instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
            index as u64,
        ))));
        if let Some(arg) = args.get(index) {
            if !lower_expression(arg, ctx, instructions) {
                push_default_for_value_type(&field.ty, ctx, instructions);
                success = false;
            }
        } else {
            push_default_for_value_type(&field.ty, ctx, instructions);
        }
        instructions.push(Instruction::ArraySet);
    }

    if args.len() > fields.len() {
        ctx.record_error(format!(
            "struct constructor '{}' expects {} argument(s), got {}",
            call_name,
            fields.len(),
            args.len()
        ));
        for arg in &args[fields.len()..] {
            if lower_expression(arg, ctx, instructions) {
                instructions.push(Instruction::Drop(ValueType::Any));
            }
        }
        success = false;
    }

    instructions.push(Instruction::LoadLocal(struct_local));
    success
}

fn lower_struct_constructor_named(
    _call_name: &str,
    struct_type: &ValueType,
    fields: &[StructField],
    args: &[solang_parser::pt::NamedArgument],
    ctx: &mut LoweringContext,
    instructions: &mut Vec<Instruction>,
) -> bool {
    let tmp_id = ctx.next_label();
    let struct_local = ctx.allocate_local(format!("__struct_ctor_{tmp_id}"), Some(struct_type.clone()));

    let mut field_indexes = HashMap::with_capacity(fields.len());
    for (index, field) in fields.iter().enumerate() {
        field_indexes.insert(field.name.clone(), index);
    }

    let mut arg_locals: Vec<Option<usize>> = vec![None; fields.len()];
    let mut success = true;

    for arg in args {
        let Some(&field_index) = field_indexes.get(&arg.name.name) else {
            if lower_expression(&arg.expr, ctx, instructions) {
                instructions.push(Instruction::Drop(ValueType::Any));
            }
            continue;
        };

        if arg_locals[field_index].is_some() {
            if lower_expression(&arg.expr, ctx, instructions) {
                instructions.push(Instruction::Drop(ValueType::Any));
            }
            continue;
        }

        let field_ty = &fields[field_index].ty;
        let arg_local = ctx.allocate_local(
            format!("__struct_arg_{tmp_id}_{field_index}"),
            Some(field_ty.clone()),
        );

        if !lower_expression(&arg.expr, ctx, instructions) {
            push_default_for_value_type(field_ty, ctx, instructions);
            success = false;
        }

        instructions.push(Instruction::StoreLocal(arg_local));
        arg_locals[field_index] = Some(arg_local);
    }

    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
        fields.len() as u64,
    ))));
    instructions.push(Instruction::NewArray {
        element_type: ValueType::Any,
    });
    instructions.push(Instruction::StoreLocal(struct_local));

    for (index, field) in fields.iter().enumerate() {
        instructions.push(Instruction::LoadLocal(struct_local));
        instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
            index as u64,
        ))));
        if let Some(arg_local) = arg_locals[index] {
            instructions.push(Instruction::LoadLocal(arg_local));
        } else {
            push_default_for_value_type(&field.ty, ctx, instructions);
        }
        instructions.push(Instruction::ArraySet);
    }

    instructions.push(Instruction::LoadLocal(struct_local));
    success
}