Skip to content

Commit 8810984

Browse files
authored
Soroban/support array in contract storage (#1980)
support dynamic arrays in contract storage. operations - push - pop - length - read subscript - write subscript type - Bool - i32/u32 - i64/u64 - i128/u128 - i256/u256 - string - bytes/bytesN - address - struct
2 parents e030cbf + 0512dbb commit 8810984

13 files changed

Lines changed: 3290 additions & 948 deletions

File tree

src/codegen/expression.rs

Lines changed: 126 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use super::revert::{
44
assert_failure, expr_assert, log_runtime_error, require, PanicCode, SolidityError,
55
};
66
use super::storage::array_offset;
7-
use super::targets::soroban::encoding::soroban_encode_arg;
87
use super::Options;
98
use super::{
109
cfg::{ControlFlowGraph, Instr, InternalCallTy},
@@ -14,9 +13,12 @@ use crate::codegen::array_boundary::handle_array_assign;
1413
use crate::codegen::constructor::call_constructor;
1514
use crate::codegen::interface::TargetCodegen;
1615
use crate::codegen::targets::polkadot::return_code as polkadot;
16+
use crate::codegen::targets::soroban::bytes::{
17+
soroban_bytes_length, soroban_bytes_subscript_read, soroban_strings_length,
18+
};
1719
use crate::codegen::targets::soroban::{
18-
soroban_bytes_length, soroban_strings_length, soroban_struct_load, soroban_struct_member_load,
19-
soroban_struct_member_store,
20+
soroban_storage_array_length_ast, soroban_storage_assign, soroban_storage_incdec,
21+
soroban_storage_load,
2022
};
2123
use crate::codegen::unused_variable::should_remove_assignment;
2224
use crate::codegen::{Builtin, Expression};
@@ -63,36 +65,19 @@ pub fn expression(
6365
}
6466
ast::Expression::StorageLoad { loc, ty, expr } => {
6567
if ns.target == Target::Soroban {
66-
if let ast::Expression::StructMember {
67-
expr: var, field, ..
68-
} = expr.as_ref()
69-
{
70-
return soroban_struct_member_load(
71-
loc,
72-
var,
73-
*field,
74-
cfg,
75-
contract_no,
76-
func,
77-
ns,
78-
vartab,
79-
opt,
80-
target,
81-
);
82-
}
83-
if matches!(ty, Type::Struct(_)) {
84-
return soroban_struct_load(
85-
loc,
86-
expr,
87-
ty,
88-
cfg,
89-
contract_no,
90-
func,
91-
ns,
92-
vartab,
93-
opt,
94-
target,
95-
);
68+
if let Some(loaded) = soroban_storage_load(
69+
loc,
70+
expr,
71+
ty,
72+
cfg,
73+
contract_no,
74+
func,
75+
ns,
76+
vartab,
77+
opt,
78+
target,
79+
) {
80+
return loaded;
9681
}
9782
}
9883

@@ -792,6 +777,22 @@ pub fn expression(
792777
array,
793778
elem_ty,
794779
} => {
780+
if ns.target == Target::Soroban {
781+
if let Some(len) = soroban_storage_array_length_ast(
782+
array,
783+
ty,
784+
cfg,
785+
contract_no,
786+
func,
787+
ns,
788+
vartab,
789+
opt,
790+
target,
791+
) {
792+
return len;
793+
}
794+
}
795+
795796
// TODO-refactor : a good change to move the implementation of storage array length
796797
// to CFG instead of llvm-ir.
797798
// right now for bytes and strings we simply use a host function
@@ -1618,6 +1619,25 @@ fn post_incdec(
16181619
opt: &Options,
16191620
target: &dyn TargetCodegen,
16201621
) -> Expression {
1622+
if ns.target == Target::Soroban {
1623+
if let Some(result) = soroban_storage_incdec(
1624+
loc,
1625+
var,
1626+
ty,
1627+
expr,
1628+
overflowing,
1629+
cfg,
1630+
contract_no,
1631+
func,
1632+
ns,
1633+
vartab,
1634+
opt,
1635+
target,
1636+
) {
1637+
return result;
1638+
}
1639+
}
1640+
16211641
let res = vartab.temp_anonymous(ty);
16221642
let v = expression(var, cfg, contract_no, func, ns, vartab, opt, target);
16231643

@@ -1763,6 +1783,25 @@ fn pre_incdec(
17631783
opt: &Options,
17641784
target: &dyn TargetCodegen,
17651785
) -> Expression {
1786+
if ns.target == Target::Soroban {
1787+
if let Some(result) = soroban_storage_incdec(
1788+
loc,
1789+
var,
1790+
ty,
1791+
expr,
1792+
overflowing,
1793+
cfg,
1794+
contract_no,
1795+
func,
1796+
ns,
1797+
vartab,
1798+
opt,
1799+
target,
1800+
) {
1801+
return result;
1802+
}
1803+
}
1804+
17661805
let res = vartab.temp_anonymous(ty);
17671806
let v = expression(var, cfg, contract_no, func, ns, vartab, opt, target);
17681807
let storage_type = storage_type(var, ns);
@@ -2987,30 +3026,18 @@ pub fn assign_single(
29873026
}
29883027
_ => {
29893028
if ns.target == Target::Soroban {
2990-
if let ast::Expression::StructMember {
2991-
expr: var,
2992-
field,
2993-
ty: member_ty,
2994-
..
2995-
} = left
2996-
{
2997-
if member_ty.is_contract_storage()
2998-
&& matches!(var.ty().deref_any(), Type::Struct(_))
2999-
{
3000-
return soroban_struct_member_store(
3001-
&left.loc(),
3002-
var,
3003-
*field,
3004-
cfg_right,
3005-
cfg,
3006-
contract_no,
3007-
func,
3008-
ns,
3009-
vartab,
3010-
opt,
3011-
target,
3012-
);
3013-
}
3029+
if let Some(result) = soroban_storage_assign(
3030+
left,
3031+
cfg_right.clone(),
3032+
cfg,
3033+
contract_no,
3034+
func,
3035+
ns,
3036+
vartab,
3037+
opt,
3038+
target,
3039+
) {
3040+
return result;
30143041
}
30153042
}
30163043

@@ -3074,17 +3101,12 @@ pub fn assign_single(
30743101
}
30753102
}
30763103
Type::StorageRef(..) => {
3077-
let value = target.prepare_storage_value(
3078-
Expression::Variable {
3079-
loc: left.loc(),
3080-
ty: ty.clone(),
3081-
var_no: pos,
3082-
},
3083-
&dest,
3084-
cfg,
3085-
vartab,
3086-
ns,
3087-
);
3104+
let value = Expression::Variable {
3105+
loc: left.loc(),
3106+
ty: ty.clone(),
3107+
var_no: pos,
3108+
};
3109+
let value = target.prepare_storage_value(value, &dest, cfg, vartab, ns);
30883110
cfg.add(
30893111
vartab,
30903112
Instr::SetStorage {
@@ -3659,12 +3681,23 @@ fn array_subscript(
36593681
opt: &Options,
36603682
target: &dyn TargetCodegen,
36613683
) -> Expression {
3662-
// TODO: the function handling many subscript
3663-
// e.g array - bytes - map - ...
3664-
// i prefer to split it and dispatch to separte functions
3665-
// TODO: for bytes storage subscript we can write it in CFG
3666-
// instead of llvm-ir.
36673684
if array_ty.is_storage_bytes() {
3685+
if ns.target == Target::Soroban {
3686+
if let Some(byte) = soroban_bytes_subscript_read(
3687+
array,
3688+
index,
3689+
elem_ty,
3690+
cfg,
3691+
contract_no,
3692+
func,
3693+
ns,
3694+
vartab,
3695+
opt,
3696+
target,
3697+
) {
3698+
return byte;
3699+
}
3700+
}
36683701
return Expression::Subscript {
36693702
loc: *loc,
36703703
ty: elem_ty.clone(),
@@ -3723,11 +3756,26 @@ fn array_subscript(
37233756
None => {
37243757
if let Type::StorageRef(..) = array_ty {
37253758
if ns.target == Target::Solana || ns.target == Target::Soroban {
3726-
Expression::StorageArrayLength {
3727-
loc: *loc,
3728-
ty: ns.storage_type(),
3729-
array: Box::new(array.clone()),
3730-
elem_ty: array_ty.storage_array_elem().deref_into(),
3759+
let elem_ty = array_ty.storage_array_elem().deref_into();
3760+
// On Soroban every array is a host VecObject: get the length via
3761+
// the hook (arrays.rs, vec_len). Solana keeps StorageArrayLength.
3762+
if ns.target == Target::Soroban {
3763+
target.lower_storage_array_length(
3764+
loc,
3765+
&ns.storage_type(),
3766+
array.clone(),
3767+
&elem_ty,
3768+
cfg,
3769+
vartab,
3770+
ns,
3771+
)
3772+
} else {
3773+
Expression::StorageArrayLength {
3774+
loc: *loc,
3775+
ty: ns.storage_type(),
3776+
array: Box::new(array.clone()),
3777+
elem_ty,
3778+
}
37313779
}
37323780
} else {
37333781
let ty = if ns.target == Target::Soroban {
@@ -3937,12 +3985,6 @@ fn array_subscript(
39373985
if ns.target == Target::Soroban {
39383986
let index = index.cast(&Type::Uint(64), ns);
39393987

3940-
let index = if elem_ty.is_reference_type(ns) {
3941-
soroban_encode_arg(index, cfg, vartab, ns)
3942-
} else {
3943-
index
3944-
};
3945-
39463988
let val = Expression::Subscript {
39473989
loc: *loc,
39483990
ty: elem_ty.clone(),

0 commit comments

Comments
 (0)