Skip to content

Commit

Permalink
Merge a449e6e into e1fcf98
Browse files Browse the repository at this point in the history
  • Loading branch information
esdrubal committed Jun 6, 2024
2 parents e1fcf98 + a449e6e commit 8599040
Show file tree
Hide file tree
Showing 46 changed files with 1,025 additions and 421 deletions.
27 changes: 25 additions & 2 deletions sway-ast/src/item/item_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,41 @@ use crate::priv_prelude::*;
#[derive(Clone, Debug, Serialize)]
pub struct ItemStorage {
pub storage_token: StorageToken,
pub fields: Braces<Punctuated<Annotated<StorageField>, CommaToken>>,
pub entries: Braces<Punctuated<Annotated<StorageEntry>, CommaToken>>,
}

impl Spanned for ItemStorage {
fn span(&self) -> Span {
Span::join(self.storage_token.span(), &self.fields.span())
Span::join(self.storage_token.span(), &self.entries.span())
}
}

#[derive(Clone, Debug, Serialize)]

pub struct StorageEntry {
pub name: Ident,
pub namespace: Option<Braces<Punctuated<Annotated<Box<StorageEntry>>, CommaToken>>>,
pub field: Option<StorageField>,
}

impl Spanned for StorageEntry {
fn span(&self) -> Span {
if let Some(namespace) = &self.namespace {
Span::join(self.name.span(), &namespace.span())
} else if let Some(field) = &self.field {
Span::join(self.name.span(), &field.span())
} else {
self.name.span()
}
}
}

#[derive(Clone, Debug, Serialize)]

pub struct StorageField {
pub name: Ident,
pub in_token: Option<InToken>,
pub key_expr: Option<Expr>,
pub colon_token: ColonToken,
pub ty: Ty,
pub eq_token: EqToken,
Expand Down
2 changes: 1 addition & 1 deletion sway-ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub use crate::{
item_enum::ItemEnum,
item_fn::ItemFn,
item_impl::{ItemImpl, ItemImplItem},
item_storage::{ItemStorage, StorageField},
item_storage::{ItemStorage, StorageEntry, StorageField},
item_struct::ItemStruct,
item_trait::{ItemTrait, ItemTraitItem, Traits},
item_type_alias::ItemTypeAlias,
Expand Down
52 changes: 39 additions & 13 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{
compile::compile_function,
convert::*,
lexical_map::LexicalMap,
storage::{add_to_b256, get_storage_key},
storage::{add_to_b256, get_storage_field_id, get_storage_key},
types::*,
};
use crate::{
Expand All @@ -11,7 +11,7 @@ use crate::{
compile_constant_expression, compile_constant_expression_to_constant,
},
language::{
ty::{self, ProjectionKind, TyConstantDecl, TyExpressionVariant},
ty::{self, ProjectionKind, TyConstantDecl, TyExpressionVariant, TyStorageField},
*,
},
metadata::MetadataManager,
Expand All @@ -28,7 +28,6 @@ use sway_types::{
ident::Ident,
integer_bits::IntegerBits,
span::{Span, Spanned},
state::StateIndex,
u256::U256,
Named,
};
Expand Down Expand Up @@ -588,9 +587,22 @@ impl<'eng> FnCompiler<'eng> {
Ok(TerminatorValue::new(val, context))
}
ty::TyExpressionVariant::StorageAccess(access) => {
let span_md_idx = md_mgr.span_to_md(context, &access.span());
let ns = access.namespace.as_ref().map(|ns| ns.as_str());
self.compile_storage_access(context, ns, &access.ix, &access.fields, span_md_idx)
let span_md_idx: Option<MetadataIndex> = md_mgr.span_to_md(context, &access.span());
let key = TyStorageField::get_key_expression_const(
&access.key_expression.clone().map(|v| *v),
self.engines,
context,
md_mgr,
self.module,
)?;
self.compile_storage_access(
context,
access.storage_field_names.clone(),
access.struct_field_names.clone(),
key,
&access.fields,
span_md_idx,
)
}
ty::TyExpressionVariant::IntrinsicFunction(kind) => {
self.compile_intrinsic_function(context, md_mgr, kind, ast_expr.span.clone())
Expand Down Expand Up @@ -3726,8 +3738,9 @@ impl<'eng> FnCompiler<'eng> {
fn compile_storage_access(
&mut self,
context: &mut Context,
ns: Option<&str>,
ix: &StateIndex,
storage_field_names: Vec<String>,
struct_field_names: Vec<String>,
key: Option<U256>,
fields: &[ty::TyStorageAccessDescriptor],
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
Expand All @@ -3752,7 +3765,15 @@ impl<'eng> FnCompiler<'eng> {

// Do the actual work. This is a recursive function because we want to drill down
// to load each primitive type in the storage field in its own storage slot.
self.compile_storage_read(context, ns, ix, &field_idcs, &base_type, span_md_idx)
self.compile_storage_read(
context,
storage_field_names,
struct_field_names,
key,
&field_idcs,
&base_type,
span_md_idx,
)
}

#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -3854,11 +3875,13 @@ impl<'eng> FnCompiler<'eng> {
Ok(TerminatorValue::new(val, context))
}

#[allow(clippy::too_many_arguments)]
fn compile_storage_read(
&mut self,
context: &mut Context,
ns: Option<&str>,
ix: &StateIndex,
storage_field_names: Vec<String>,
struct_field_names: Vec<String>,
key: Option<U256>,
indices: &[u64],
base_type: &Type,
span_md_idx: Option<MetadataIndex>,
Expand Down Expand Up @@ -3895,7 +3918,10 @@ impl<'eng> FnCompiler<'eng> {
// plus the offset, in number of slots, computed above. The offset within this
// particular slot is the remaining offset, in words.
(
add_to_b256(get_storage_key::<u64>(ns, ix, &[]), offset_in_slots),
add_to_b256(
get_storage_key(storage_field_names.clone(), key.clone()),
offset_in_slots,
),
offset_remaining,
)
};
Expand Down Expand Up @@ -3950,7 +3976,7 @@ impl<'eng> FnCompiler<'eng> {
.add_metadatum(context, span_md_idx);

// Store the field identifier as the third field in the `StorageKey` struct
let unique_field_id = get_storage_key(ns, ix, indices); // use the indices to get a field id that is unique even for zero-sized values that live in the same slot
let unique_field_id = get_storage_field_id(storage_field_names, struct_field_names); // use the struct_field_names to get a field id that is unique even for zero-sized values that live in the same slot
let field_id = convert_literal_to_value(context, &Literal::B256(unique_field_id.into()))
.add_metadatum(context, span_md_idx);
let gep_2_val =
Expand Down
86 changes: 63 additions & 23 deletions sway-core/src/ir_generation/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use sway_ir::{
context::Context,
irtype::Type,
};
use sway_types::state::StateIndex;
use sway_types::u256::U256;

/// Determines how values that are less then a word in length
/// has to be padded to word boundary when in structs or enums.
Expand All @@ -19,20 +19,61 @@ enum InByte8Padding {
Left,
}

/// Hands out storage keys using a state index and a list of subfield indices.
/// Basically returns sha256("storage_<state_index>_<idx1>_<idx2>_..")
/// or sha256("storage_<storage_namespace>_<state_index>_<idx1>_<idx2>_..")
pub(super) fn get_storage_key<T>(namespace: Option<&str>, ix: &StateIndex, indices: &[T]) -> Bytes32
where
T: std::fmt::Display,
{
/// Hands out storage keys using storage field names or an existing key.
/// Basically returns sha256("storage::<storage_namespace_name1>::<storage_namespace_name2>.<storage_field_name>")
/// or key if defined.
pub(super) fn get_storage_key(storage_field_names: Vec<String>, key: Option<U256>) -> Bytes32 {
if let Some(key) = key {
return key.to_be_bytes().into();
}

Hasher::hash(get_storage_key_string(storage_field_names))
}

fn get_storage_key_string(storage_field_names: Vec<String>) -> String {
if storage_field_names.len() == 1 {
format!(
"{}{}{}",
sway_utils::constants::STORAGE_DOMAIN,
sway_utils::constants::STORAGE_FIELD_SEPARATOR,
storage_field_names.last().unwrap(),
)
} else {
format!(
"{}{}{}{}{}",
sway_utils::constants::STORAGE_DOMAIN,
sway_utils::constants::STORAGE_NAMESPACE_SEPARATOR,
storage_field_names
.iter()
.take(storage_field_names.len() - 1)
.cloned()
.collect::<Vec<_>>()
.join(sway_utils::constants::STORAGE_NAMESPACE_SEPARATOR),
sway_utils::constants::STORAGE_FIELD_SEPARATOR,
storage_field_names.last().unwrap(),
)
}
}

/// Hands out unique storage field ids using storage field names and struct field names.
/// Basically returns sha256("storage::<storage_namespace_name1>::<storage_namespace_name2>.<storage_field_name>.<struct_field_name1>.<struct_field_name2>")
pub(super) fn get_storage_field_id(
storage_field_names: Vec<String>,
struct_field_names: Vec<String>,
) -> Bytes32 {
let data = format!(
"{}{}{}",
sway_utils::constants::STORAGE_DOMAIN_SEPARATOR,
namespace.map(|ns| format!("{ns}_")).unwrap_or("".into()),
ix.to_usize(),
"{}{}",
get_storage_key_string(storage_field_names),
if struct_field_names.is_empty() {
"".to_string()
} else {
format!(
"{}{}",
sway_utils::constants::STRUCT_FIELD_SEPARATOR,
struct_field_names.join(sway_utils::constants::STRUCT_FIELD_SEPARATOR),
)
}
);
let data = indices.iter().fold(data, |acc, i| format!("{acc}_{i}"));
Hasher::hash(data)
}

Expand Down Expand Up @@ -64,22 +105,21 @@ pub(super) fn add_to_b256(x: Bytes32, y: u64) -> Bytes32 {
pub fn serialize_to_storage_slots(
constant: &Constant,
context: &Context,
ix: &StateIndex,
ns: Option<&str>,
storage_field_names: Vec<String>,
key: Option<U256>,
ty: &Type,
indices: &[usize],
) -> Vec<StorageSlot> {
match &constant.value {
ConstantValue::Undef => vec![],
// If not being a part of an aggregate, single byte values like `bool`, `u8`, and unit
// are stored as a byte at the beginning of the storage slot.
ConstantValue::Unit if ty.is_unit(context) => vec![StorageSlot::new(
get_storage_key(ns, ix, indices),
get_storage_key(storage_field_names, key),
Bytes32::new([0; 32]),
)],
ConstantValue::Bool(b) if ty.is_bool(context) => {
vec![StorageSlot::new(
get_storage_key(ns, ix, indices),
get_storage_key(storage_field_names, key),
Bytes32::new([
if *b { 1 } else { 0 },
0,
Expand Down Expand Up @@ -118,7 +158,7 @@ pub fn serialize_to_storage_slots(
}
ConstantValue::Uint(b) if ty.is_uint8(context) => {
vec![StorageSlot::new(
get_storage_key(ns, ix, indices),
get_storage_key(storage_field_names, key),
Bytes32::new([
*b as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
Expand All @@ -128,7 +168,7 @@ pub fn serialize_to_storage_slots(
// Similarly, other uint values are stored at the beginning of the storage slot.
ConstantValue::Uint(n) if ty.is_uint(context) => {
vec![StorageSlot::new(
get_storage_key(ns, ix, indices),
get_storage_key(storage_field_names, key),
Bytes32::new(
n.to_be_bytes()
.iter()
Expand All @@ -142,13 +182,13 @@ pub fn serialize_to_storage_slots(
}
ConstantValue::U256(b) if ty.is_uint_of(context, 256) => {
vec![StorageSlot::new(
get_storage_key(ns, ix, indices),
get_storage_key(storage_field_names, key),
Bytes32::new(b.to_be_bytes()),
)]
}
ConstantValue::B256(b) if ty.is_b256(context) => {
vec![StorageSlot::new(
get_storage_key(ns, ix, indices),
get_storage_key(storage_field_names, key),
Bytes32::new(b.to_be_bytes()),
)]
}
Expand Down Expand Up @@ -185,7 +225,7 @@ pub fn serialize_to_storage_slots(
ty.as_string(context)
);
(0..(type_size_in_bytes + 31) / 32)
.map(|i| add_to_b256(get_storage_key(ns, ix, indices), i))
.map(|i| add_to_b256(get_storage_key(storage_field_names.clone(), key.clone()), i))
.zip((0..packed.len() / 4).map(|i| {
Bytes32::new(
Vec::from_iter((0..4).flat_map(|j| *packed[4 * i + j]))
Expand Down
44 changes: 42 additions & 2 deletions sway-core/src/language/parsed/declaration/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use sway_types::{ident::Ident, span::Span, Spanned};
/// All values in this struct are mutable and persistent among executions of the same contract deployment.
pub struct StorageDeclaration {
pub attributes: transform::AttributesMap,
pub fields: Vec<StorageField>,
pub entries: Vec<StorageEntry>,
pub span: Span,
pub storage_keyword: Ident,
}
Expand All @@ -20,7 +20,7 @@ impl EqWithEngines for StorageDeclaration {}
impl PartialEqWithEngines for StorageDeclaration {
fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
self.attributes == other.attributes
&& self.fields.eq(&other.fields, ctx)
&& self.entries.eq(&other.entries, ctx)
&& self.span == other.span
&& self.storage_keyword == other.storage_keyword
}
Expand All @@ -32,13 +32,53 @@ impl Spanned for StorageDeclaration {
}
}

#[derive(Debug, Clone)]
pub struct StorageNamespace {
pub name: Ident,
pub entries: Vec<Box<StorageEntry>>,
}

impl EqWithEngines for StorageNamespace {}
impl PartialEqWithEngines for StorageNamespace {
fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
self.name.eq(&other.name) && self.entries.eq(&other.entries, ctx)
}
}

#[derive(Debug, Clone)]
pub enum StorageEntry {
Namespace(StorageNamespace),
Field(StorageField),
}

impl StorageEntry {
pub fn name(&self) -> Ident {
match self {
StorageEntry::Namespace(namespace) => namespace.name.clone(),
StorageEntry::Field(field) => field.name.clone(),
}
}
}

impl EqWithEngines for StorageEntry {}
impl PartialEqWithEngines for StorageEntry {
fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
match (self, other) {
(StorageEntry::Namespace(n1), StorageEntry::Namespace(n2)) => n1.eq(n2, ctx),
(StorageEntry::Field(f1), StorageEntry::Field(f2)) => f1.eq(f2, ctx),
_ => false,
}
}
}

/// An individual field in a storage declaration.
/// A type annotation _and_ initializer value must be provided. The initializer value must be a
/// constant expression. For now, that basically means just a literal, but as constant folding
/// improves, we can update that.
#[derive(Debug, Clone)]
pub struct StorageField {
pub name: Ident,
pub key_expression: Option<Expression>,
pub attributes: transform::AttributesMap,
pub type_argument: TypeArgument,
pub span: Span,
Expand Down
Loading

0 comments on commit 8599040

Please sign in to comment.