Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions src/codegen/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3866,22 +3866,22 @@ fn array_subscript(
elem_ty: array_ty.storage_array_elem().deref_into(),
}
} else {
// TODO(Soroban): Storage type here is None, since arrays are not yet supported in Soroban
let array_length = load_storage(
loc,
&Type::Uint(256),
array.clone(),
cfg,
vartab,
None,
ns,
);

array = Expression::Keccak256 {
loc: *loc,
ty: Type::Uint(256),
exprs: vec![array],
let ty = if ns.target == Target::Soroban {
Type::Uint(64)
} else {
Type::Uint(256)
};
// TODO(Soroban): Storage type here is None, it should be the same type as the array
let array_length =
load_storage(loc, &ty, array.clone(), cfg, vartab, None, ns);

if ns.target != Target::Soroban {
array = Expression::Keccak256 {
loc: *loc,
ty: Type::Uint(256),
exprs: vec![array],
};
}

array_length
}
Expand Down Expand Up @@ -4062,6 +4062,20 @@ fn array_subscript(
let elem_ty = ty.storage_array_elem();
let slot_ty = ns.storage_type();

if ns.target == Target::Soroban {
let index = index.cast(&Type::Uint(64), ns);

let index_encoded = soroban_encode_arg(index, cfg, vartab, ns);

return Expression::Subscript {
loc: *loc,
ty: elem_ty,
array_ty: array_ty.clone(),
expr: Box::new(array),
index: Box::new(index_encoded),
};
}

if ns.target == Target::Solana {
if ty.array_length().is_some() && ty.is_sparse_solana(ns) {
let index = Expression::Variable {
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ pub enum HostFunctions {
PutContractData,
GetContractData,
HasContractData,
DeleteContractData,
ExtendContractDataTtl,
ExtendCurrentContractInstanceAndCodeTtl,
LogFromLinearMemory,
Expand Down Expand Up @@ -144,6 +145,7 @@ impl HostFunctions {
HostFunctions::PutContractData => "l._",
HostFunctions::GetContractData => "l.1",
HostFunctions::HasContractData => "l.0",
HostFunctions::DeleteContractData => "l.2",
HostFunctions::ExtendContractDataTtl => "l.7",
HostFunctions::ExtendCurrentContractInstanceAndCodeTtl => "l.8",
HostFunctions::LogFromLinearMemory => "x._",
Expand Down
150 changes: 103 additions & 47 deletions src/codegen/storage.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: Apache-2.0

use crate::codegen::encoding::soroban_encoding::soroban_encode_arg;
use crate::codegen::Expression;
use crate::sema::ast;
use crate::Target;
use num_bigint::BigInt;
use num_traits::FromPrimitive;
use num_traits::One;
Expand Down Expand Up @@ -114,31 +116,55 @@ pub fn storage_slots_array_push(

let entry_pos = vartab.temp_anonymous(&slot_ty);

let array_offset = if ns.target == Target::Soroban {
let index = Expression::Variable {
loc: *loc,
ty: slot_ty.clone(),
var_no: length_pos,
};

let index_encoded = soroban_encode_arg(index, cfg, vartab, ns);

Expression::Subscript {
loc: *loc,
ty: elem_ty.clone(),
array_ty: Type::StorageRef(false, Box::new(elem_ty.clone())),
expr: Box::new(var_expr.clone()),
index: Box::new(index_encoded),
}
} else {
array_offset(
loc,
Expression::Keccak256 {
loc: *loc,
ty: slot_ty.clone(),
exprs: vec![var_expr.clone()],
},
Expression::Variable {
loc: *loc,
ty: slot_ty.clone(),
var_no: length_pos,
},
elem_ty.clone(),
ns,
)
};

cfg.add(
vartab,
Instr::Set {
loc: pt::Loc::Codegen,
res: entry_pos,
expr: array_offset(
loc,
Expression::Keccak256 {
loc: *loc,
ty: slot_ty.clone(),
exprs: vec![var_expr.clone()],
},
Expression::Variable {
loc: *loc,
ty: slot_ty.clone(),
var_no: length_pos,
},
elem_ty.clone(),
ns,
),
expr: array_offset,
},
);

if args.len() == 2 {
let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
let mut value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);

if ns.target == Target::Soroban {
value = soroban_encode_arg(value, cfg, vartab, ns);
}

cfg.add(
vartab,
Expand All @@ -156,7 +182,7 @@ pub fn storage_slots_array_push(
}

// increase length
let new_length = Expression::Add {
let mut new_length = Expression::Add {
loc: *loc,
ty: slot_ty.clone(),
overflowing: true,
Expand All @@ -172,6 +198,10 @@ pub fn storage_slots_array_push(
}),
};

if ns.target == Target::Soroban {
new_length = soroban_encode_arg(new_length, cfg, vartab, ns);
}

cfg.add(
vartab,
Instr::SetStorage {
Expand Down Expand Up @@ -263,26 +293,32 @@ pub fn storage_slots_array_pop(
cfg.set_basic_block(has_elements);
let new_length = vartab.temp_anonymous(&slot_ty);

let mut subtract = Expression::Subtract {
loc: *loc,
ty: length_ty.clone(),
overflowing: true,
left: Box::new(Expression::Variable {
loc: *loc,
ty: length_ty.clone(),
var_no: length_pos,
}),
right: Box::new(Expression::NumberLiteral {
loc: *loc,
ty: length_ty.clone(),
value: BigInt::one(),
}),
};

if ns.target == Target::Soroban {
subtract = soroban_encode_arg(subtract, cfg, vartab, ns);
}

cfg.add(
vartab,
Instr::Set {
loc: pt::Loc::Codegen,
res: new_length,
expr: Expression::Subtract {
loc: *loc,
ty: length_ty.clone(),
overflowing: true,
left: Box::new(Expression::Variable {
loc: *loc,
ty: length_ty.clone(),
var_no: length_pos,
}),
right: Box::new(Expression::NumberLiteral {
loc: *loc,
ty: length_ty,
value: BigInt::one(),
}),
},
expr: subtract,
},
);

Expand All @@ -291,26 +327,46 @@ pub fn storage_slots_array_pop(
let elem_ty = ty.storage_array_elem().deref_any().clone();
let entry_pos = vartab.temp_anonymous(&slot_ty);

let array_offset_expr = if ns.target == Target::Soroban {
let index = Expression::Variable {
loc: *loc,
ty: slot_ty.clone(),
var_no: length_pos,
};

let index_encoded = soroban_encode_arg(index, cfg, vartab, ns);

Expression::Subscript {
loc: *loc,
ty: elem_ty.clone(),
array_ty: Type::StorageRef(false, Box::new(elem_ty.clone())),
expr: Box::new(var_expr.clone()),
index: Box::new(index_encoded),
}
} else {
array_offset(
loc,
Expression::Keccak256 {
loc: *loc,
ty: slot_ty.clone(),
exprs: vec![var_expr.clone()],
},
Expression::Variable {
loc: *loc,
ty: slot_ty.clone(),
var_no: new_length,
},
elem_ty.clone(),
ns,
)
};

cfg.add(
vartab,
Instr::Set {
loc: pt::Loc::Codegen,
res: entry_pos,
expr: array_offset(
loc,
Expression::Keccak256 {
loc: *loc,
ty: slot_ty.clone(),
exprs: vec![var_expr.clone()],
},
Expression::Variable {
loc: *loc,
ty: slot_ty.clone(),
var_no: new_length,
},
elem_ty.clone(),
ns,
),
expr: array_offset_expr,
},
);

Expand Down
6 changes: 6 additions & 0 deletions src/emit/soroban/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ impl HostFunctions {
.context
.i64_type()
.fn_type(&[ty.into(), ty.into()], false),

HostFunctions::DeleteContractData => bin
.context
.i64_type()
.fn_type(&[ty.into(), ty.into()], false),
// https://github.com/stellar/stellar-protocol/blob/2fdc77302715bc4a31a784aef1a797d466965024/core/cap-0046-03.md#ledger-host-functions-mod-l
// ;; If the entry's TTL is below `threshold` ledgers, extend `live_until_ledger_seq` such that TTL == `extend_to`, where TTL is defined as live_until_ledger_seq - current ledger.
// (func $extend_contract_data_ttl (param $k_val i64) (param $t_storage_type i64) (param $threshold_u32_val i64) (param $extend_to_u32_val i64) (result i64))
Expand Down Expand Up @@ -370,6 +375,7 @@ impl SorobanTarget {
HostFunctions::PutContractData,
HostFunctions::GetContractData,
HostFunctions::HasContractData,
HostFunctions::DeleteContractData,
HostFunctions::ExtendContractDataTtl,
HostFunctions::ExtendCurrentContractInstanceAndCodeTtl,
HostFunctions::LogFromLinearMemory,
Expand Down
28 changes: 26 additions & 2 deletions src/emit/soroban/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,15 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {

// In case of struct, we receive a buffer in that format: [ field1, field2, ... ] where each field is a Soroban tagged value of type i64
// therefore, for each field, we need to extract it from the buffer and call PutContractData for each field separately
if let Type::Struct(ast::StructType::UserDefined(n)) = ty {

// This check is added to handle the case we are stroing a struct in storage
let inner_ty = if let Type::StorageRef(mutable, inner) = ty {
inner
} else {
ty
};

if let Type::Struct(ast::StructType::UserDefined(n)) = inner_ty {
let field_count = &bin.ns.structs[*n].fields.len();

let data_ptr = bin.vector_bytes(dest);
Expand Down Expand Up @@ -198,7 +206,23 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
slot: &mut IntValue<'a>,
function: FunctionValue<'a>,
) {
unimplemented!()
let storage_type = storage_type_to_int(&None);

let type_int = bin.context.i64_type().const_int(storage_type, false);

let function_value = bin
.module
.get_function(HostFunctions::DeleteContractData.name())
.unwrap();

let call = bin
.builder
.build_call(
function_value,
&[slot.as_basic_value_enum().into(), type_int.into()],
"del_contract_data",
)
.unwrap();
}

// Bytes and string have special storage layout
Expand Down
1 change: 1 addition & 0 deletions tests/soroban_testcases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod mappings;
mod math;
mod print;
mod storage;
mod storage_array;
mod structs;
mod token;
mod ttl;
Loading
Loading