use super::Lowerer;
use crate::mir::{FunctionBuilder, ValueId};
use alloy_primitives::U256;
use solar_sema::{
hir::{self, ElementaryType},
ty::TyKind,
};
impl<'gcx> Lowerer<'gcx> {
pub(super) fn lower_index_expr(
&mut self,
builder: &mut FunctionBuilder<'_>,
expr: &hir::Expr<'_>,
base: &hir::Expr<'_>,
index: Option<&hir::Expr<'_>>,
) -> ValueId {
if let Some(mapping) = self.lower_mapping_element_slot(builder, base, index) {
if mapping.value_is_mapping {
return mapping.slot;
}
if self.expr_has_bytes_or_string_type(expr) {
return self.materialize_storage_bytes(builder, mapping.slot);
}
return builder.sload(mapping.slot);
}
if let Some((slot_val, fixed_len, elem_slots)) =
self.storage_array_slot_of_base(builder, base)
{
let index_val = self.lower_index_or_zero(builder, index);
let element_slot = self.lower_storage_array_element_slot(
builder, slot_val, fixed_len, index_val, elem_slots,
);
return builder.sload(element_slot);
}
if let Some((head, is_bytes)) = self.calldata_dyn_head(base) {
let index_val = self.lower_index_or_zero(builder, index);
let four = builder.imm_u64(4);
let len_pos = builder.add(four, head);
let len = builder.calldataload(len_pos);
self.emit_index_bounds_check(builder, index_val, len);
let offset_32 = builder.imm_u64(32);
let data_pos = builder.add(len_pos, offset_32);
if is_bytes {
let byte_pos = builder.add(data_pos, index_val);
let word = builder.calldataload(byte_pos);
let mask = builder.imm_u256(U256::from(0xffu64) << 248);
return builder.and(word, mask);
}
let byte_offset = builder.mul(index_val, offset_32);
let element_pos = builder.add(data_pos, byte_offset);
return builder.calldataload(element_pos);
}
if self.is_storage_bytes_expr(base) {
let base_val = self.lower_expr(builder, base);
let index_val = self.lower_index_or_zero(builder, index);
let len = builder.mload(base_val);
self.emit_index_bounds_check(builder, index_val, len);
let offset_32 = builder.imm_u64(32);
let data_base = builder.add(base_val, offset_32);
let byte_addr = builder.add(data_base, index_val);
let word = builder.mload(byte_addr);
let mask = builder.imm_u256(U256::from(0xffu64) << 248);
return builder.and(word, mask);
}
if self.is_memory_bytes_expr(base) {
let base_val = self.lower_expr(builder, base);
let index_val = self.lower_index_or_zero(builder, index);
let len = builder.mload(base_val);
self.emit_index_bounds_check(builder, index_val, len);
let offset_32 = builder.imm_u64(32);
let data_base = builder.add(base_val, offset_32);
let byte_addr = builder.add(data_base, index_val);
let word = builder.mload(byte_addr);
let mask = builder.imm_u256(U256::from(0xffu64) << 248);
return builder.and(word, mask);
}
if let Some(ty) = self.get_expr_type(base)
&& let TyKind::Elementary(ElementaryType::FixedBytes(n)) = ty.peel_refs().kind
{
let base_val = self.lower_expr(builder, base);
let index_val = self.lower_index_or_zero(builder, index);
let n_val = builder.imm_u64(u64::from(n.bytes()));
self.emit_index_bounds_check(builder, index_val, n_val);
let eight = builder.imm_u64(8);
let shift = builder.mul(index_val, eight);
let shifted = builder.shl(shift, base_val);
return self.clean_fixed_bytes(builder, shifted, 1);
}
let base_val = self.lower_expr(builder, base);
let index_val = self.lower_index_or_zero(builder, index);
let offset_32 = builder.imm_u64(32);
let byte_offset = builder.mul(index_val, offset_32);
let data_base = if self.is_dynamic_memory_array_expr(base) {
let len = self
.new_dynamic_memory_array_const_len(base)
.map(|len| builder.imm_u64(len))
.unwrap_or_else(|| builder.mload(base_val));
self.emit_index_bounds_check(builder, index_val, len);
builder.add(base_val, offset_32)
} else {
if let Some(len) = self.fixed_array_len_of_expr(base) {
let len_val = builder.imm_u64(len);
self.emit_index_bounds_check(builder, index_val, len_val);
}
base_val
};
let addr = builder.add(data_base, byte_offset);
builder.mload(addr)
}
pub(super) fn lower_index_assign(
&mut self,
builder: &mut FunctionBuilder<'_>,
lhs: &hir::Expr<'_>,
base: &hir::Expr<'_>,
index: Option<&hir::Expr<'_>>,
rhs: ValueId,
) {
if let Some(mapping) = self.lower_mapping_element_slot(builder, base, index) {
if self.expr_has_bytes_or_string_type(lhs) {
self.copy_memory_bytes_to_storage(builder, mapping.slot, rhs);
} else {
builder.sstore(mapping.slot, rhs);
}
return;
}
if self.is_storage_bytes_expr(base)
&& let Some(slot) = self.lower_lvalue_slot(builder, base)
{
let index_val = self.lower_index_or_zero(builder, index);
self.store_storage_bytes_element(builder, slot, index_val, rhs);
return;
}
if let Some((slot_val, fixed_len, elem_slots)) =
self.storage_array_slot_of_base(builder, base)
{
let index_val = self.lower_index_or_zero(builder, index);
let element_slot = self.lower_storage_array_element_slot(
builder, slot_val, fixed_len, index_val, elem_slots,
);
builder.sstore(element_slot, rhs);
return;
}
if self.is_memory_bytes_expr(base) {
let base_val = self.lower_expr(builder, base);
let index_val = self.lower_index_or_zero(builder, index);
let len = builder.mload(base_val);
self.emit_index_bounds_check(builder, index_val, len);
let offset_32 = builder.imm_u64(32);
let data_base = builder.add(base_val, offset_32);
let byte_addr = builder.add(data_base, index_val);
let byte_val = self.bytes1_store_byte(builder, rhs);
builder.mstore8(byte_addr, byte_val);
return;
}
let base_val = self.lower_expr(builder, base);
let index_val = self.lower_index_or_zero(builder, index);
let offset_32 = builder.imm_u64(32);
let byte_offset = builder.mul(index_val, offset_32);
let data_base = if self.is_dynamic_memory_array_expr(base) {
let len = builder.mload(base_val);
self.emit_index_bounds_check(builder, index_val, len);
builder.add(base_val, offset_32)
} else {
if let Some(len) = self.fixed_array_len_of_expr(base) {
let len_val = builder.imm_u64(len);
self.emit_index_bounds_check(builder, index_val, len_val);
}
base_val
};
let addr = builder.add(data_base, byte_offset);
builder.mstore(addr, rhs);
}
pub(super) fn lower_index_lvalue_slot(
&mut self,
builder: &mut FunctionBuilder<'_>,
base: &hir::Expr<'_>,
index: Option<&hir::Expr<'_>>,
) -> Option<ValueId> {
if let Some(mapping) = self.lower_mapping_element_slot(builder, base, index) {
return Some(mapping.slot);
}
if let Some((slot_val, fixed_len, elem_slots)) =
self.storage_array_slot_of_base(builder, base)
{
let index_val = self.lower_index_or_zero(builder, index);
return Some(self.lower_storage_array_element_slot(
builder, slot_val, fixed_len, index_val, elem_slots,
));
}
None
}
pub(super) fn lower_index_or_zero(
&mut self,
builder: &mut FunctionBuilder<'_>,
index: Option<&hir::Expr<'_>>,
) -> ValueId {
match index {
Some(index) => self.lower_expr(builder, index),
None => builder.imm_u64(0),
}
}
}