Quellcode durchsuchen

Solana contract storage array length

The codegen for storage arrays must be removed from sema, as it does
not belong there and it should be target dependant; Solana requires
different handling. A single Expression::StorageArrayLength is used
for storage bytes, bytesN, and all arrays.

This also removes the need of for Expression::StorageBytesLength
which is now removed.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young vor 4 Jahren
Ursprung
Commit
b72b3bafde

+ 2 - 2
src/bin/languageserver/mod.rs

@@ -602,8 +602,8 @@ impl SolangServer {
                 SolangServer::construct_expr(expr1, lookup_tbl, symtab, fnc_map, ns);
                 SolangServer::construct_expr(expr1, lookup_tbl, symtab, fnc_map, ns);
                 SolangServer::construct_expr(expr2, lookup_tbl, symtab, fnc_map, ns);
                 SolangServer::construct_expr(expr2, lookup_tbl, symtab, fnc_map, ns);
             }
             }
-            Expression::StorageBytesLength(_locs, expr1) => {
-                SolangServer::construct_expr(expr1, lookup_tbl, symtab, fnc_map, ns);
+            Expression::StorageArrayLength { array, .. } => {
+                SolangServer::construct_expr(array, lookup_tbl, symtab, fnc_map, ns);
             }
             }
 
 
             //String operations expression
             //String operations expression

+ 4 - 3
src/codegen/cfg.rs

@@ -442,9 +442,10 @@ impl ControlFlowGraph {
                 self.expr_to_string(contract, ns, a),
                 self.expr_to_string(contract, ns, a),
                 self.expr_to_string(contract, ns, i)
                 self.expr_to_string(contract, ns, i)
             ),
             ),
-            Expression::StorageBytesLength(_, a) => format!(
-                "(storage bytes length {})",
-                self.expr_to_string(contract, ns, a),
+            Expression::StorageArrayLength { array, elem_ty, .. } => format!(
+                "(storage array length {}[{}])",
+                self.expr_to_string(contract, ns, array),
+                elem_ty.to_string(ns),
             ),
             ),
             Expression::StructMember(_, _, a, f) => format!(
             Expression::StructMember(_, _, a, f) => format!(
                 "(struct {} field {})",
                 "(struct {} field {})",

+ 12 - 2
src/codegen/constant_folding.rs

@@ -944,11 +944,21 @@ fn expression(
                 false,
                 false,
             )
             )
         }
         }
-        Expression::StorageBytesLength(loc, array) => {
+        Expression::StorageArrayLength {
+            loc,
+            ty,
+            array,
+            elem_ty,
+        } => {
             let array = expression(array, vars, pos, cfg, ns);
             let array = expression(array, vars, pos, cfg, ns);
 
 
             (
             (
-                Expression::StorageBytesLength(*loc, Box::new(array.0)),
+                Expression::StorageArrayLength {
+                    loc: *loc,
+                    ty: ty.clone(),
+                    array: Box::new(array.0),
+                    elem_ty: elem_ty.clone(),
+                },
                 false,
                 false,
             )
             )
         }
         }

+ 47 - 4
src/codegen/expression.rs

@@ -368,6 +368,53 @@ pub fn expression(
 
 
             Expression::InternalFunctionCfg(ns.contracts[contract_no].all_functions[function_no])
             Expression::InternalFunctionCfg(ns.contracts[contract_no].all_functions[function_no])
         }
         }
+        Expression::StorageArrayLength {
+            loc,
+            ty,
+            array,
+            elem_ty,
+        } => {
+            let array_ty = array.ty().deref_into();
+            let array = expression(array, cfg, contract_no, ns, vartab);
+
+            match array_ty {
+                Type::Bytes(length) => bigint_to_expression(
+                    loc,
+                    &BigInt::from_u8(length).unwrap(),
+                    ns,
+                    &mut Vec::new(),
+                    Some(ty),
+                )
+                .unwrap(),
+                Type::DynamicBytes => Expression::StorageArrayLength {
+                    loc: *loc,
+                    ty: ty.clone(),
+                    array: Box::new(array),
+                    elem_ty: elem_ty.clone(),
+                },
+                Type::Array(_, dim) => match dim.last().unwrap() {
+                    None => {
+                        if ns.target == Target::Solana {
+                            Expression::StorageArrayLength {
+                                loc: *loc,
+                                ty: ty.clone(),
+                                array: Box::new(array),
+                                elem_ty: elem_ty.clone(),
+                            }
+                        } else {
+                            Expression::StorageLoad(*loc, ns.storage_type(), Box::new(array))
+                        }
+                    }
+                    Some(length) => {
+                        bigint_to_expression(loc, length, ns, &mut Vec::new(), Some(ty)).unwrap()
+                    }
+                },
+                ty => {
+                    println!("what are you talking about {:?}", ty);
+                    unreachable!();
+                }
+            }
+        }
         Expression::Builtin(loc, returns, Builtin::ExternalFunctionAddress, func) => {
         Expression::Builtin(loc, returns, Builtin::ExternalFunctionAddress, func) => {
             if let Expression::ExternalFunction { address, .. } = &func[0] {
             if let Expression::ExternalFunction { address, .. } = &func[0] {
                 expression(address, cfg, contract_no, ns, vartab)
                 expression(address, cfg, contract_no, ns, vartab)
@@ -527,10 +574,6 @@ pub fn expression(
 
 
             Expression::Variable(*loc, elem_ty, address_res)
             Expression::Variable(*loc, elem_ty, address_res)
         }
         }
-        Expression::StorageBytesLength(loc, expr) => Expression::StorageBytesLength(
-            *loc,
-            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
-        ),
         Expression::Or(loc, left, right) => {
         Expression::Or(loc, left, right) => {
             let boolty = Type::Bool;
             let boolty = Type::Bool;
             let l = expression(left, cfg, contract_no, ns, vartab);
             let l = expression(left, cfg, contract_no, ns, vartab);

+ 0 - 8
src/emit/ewasm.rs

@@ -916,14 +916,6 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
     ) -> IntValue<'a> {
     ) -> IntValue<'a> {
         unimplemented!();
         unimplemented!();
     }
     }
-    fn storage_string_length(
-        &self,
-        _contract: &Contract<'a>,
-        _function: FunctionValue,
-        _slot: IntValue<'a>,
-    ) -> IntValue<'a> {
-        unimplemented!();
-    }
 
 
     fn set_storage(
     fn set_storage(
         &self,
         &self,

+ 1 - 0
src/emit/generic.rs

@@ -338,6 +338,7 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
         contract: &Contract<'a>,
         contract: &Contract<'a>,
         _function: FunctionValue,
         _function: FunctionValue,
         slot: IntValue<'a>,
         slot: IntValue<'a>,
+        _ty: &ast::Type,
     ) -> IntValue<'a> {
     ) -> IntValue<'a> {
         let slot_ptr = contract.builder.build_alloca(slot.get_type(), "slot");
         let slot_ptr = contract.builder.build_alloca(slot.get_type(), "slot");
         contract.builder.build_store(slot_ptr, slot);
         contract.builder.build_store(slot_ptr, slot);

+ 15 - 11
src/emit/mod.rs

@@ -180,10 +180,13 @@ pub trait TargetRuntime<'a> {
     ) -> IntValue<'a>;
     ) -> IntValue<'a>;
     fn storage_string_length(
     fn storage_string_length(
         &self,
         &self,
-        contract: &Contract<'a>,
-        function: FunctionValue,
-        slot: IntValue<'a>,
-    ) -> IntValue<'a>;
+        _contract: &Contract<'a>,
+        _function: FunctionValue,
+        _slot: IntValue<'a>,
+        _elem_ty: &ast::Type,
+    ) -> IntValue<'a> {
+        unimplemented!();
+    }
 
 
     /// keccak256 hash
     /// keccak256 hash
     fn keccak256_hash(
     fn keccak256_hash(
@@ -2175,13 +2178,6 @@ pub trait TargetRuntime<'a> {
                 self.get_storage_bytes_subscript(&contract, function, slot, index)
                 self.get_storage_bytes_subscript(&contract, function, slot, index)
                     .into()
                     .into()
             }
             }
-            Expression::StorageBytesLength(_, a) => {
-                let slot = self
-                    .expression(contract, a, vartab, function)
-                    .into_int_value();
-
-                self.storage_string_length(&contract, function, slot).into()
-            }
             Expression::DynamicArraySubscript(_, elem_ty, a, i) => {
             Expression::DynamicArraySubscript(_, elem_ty, a, i) => {
                 let array = self.expression(contract, a, vartab, function);
                 let array = self.expression(contract, a, vartab, function);
 
 
@@ -2552,6 +2548,14 @@ pub trait TargetRuntime<'a> {
                     .into()
                     .into()
             }
             }
             Expression::ReturnData(_) => self.return_data(contract).into(),
             Expression::ReturnData(_) => self.return_data(contract).into(),
+            Expression::StorageArrayLength { array, elem_ty, .. } => {
+                let slot = self
+                    .expression(contract, array, vartab, function)
+                    .into_int_value();
+
+                self.storage_string_length(contract, function, slot, elem_ty)
+                    .into()
+            }
             Expression::Builtin(_, _, Builtin::Calldata, _)
             Expression::Builtin(_, _, Builtin::Calldata, _)
                 if contract.ns.target != Target::Substrate =>
                 if contract.ns.target != Target::Substrate =>
             {
             {

+ 0 - 8
src/emit/sabre.rs

@@ -436,14 +436,6 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
     ) -> IntValue<'a> {
     ) -> IntValue<'a> {
         unimplemented!();
         unimplemented!();
     }
     }
-    fn storage_string_length(
-        &self,
-        _contract: &Contract<'a>,
-        _function: FunctionValue,
-        _slot: IntValue<'a>,
-    ) -> IntValue<'a> {
-        unimplemented!();
-    }
 
 
     fn get_storage_int(
     fn get_storage_int(
         &self,
         &self,

+ 13 - 3
src/emit/solana.rs

@@ -941,6 +941,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         contract: &Contract<'a>,
         contract: &Contract<'a>,
         _function: FunctionValue,
         _function: FunctionValue,
         slot: IntValue<'a>,
         slot: IntValue<'a>,
+        elem_ty: &ast::Type,
     ) -> IntValue<'a> {
     ) -> IntValue<'a> {
         // contract storage is in 2nd account
         // contract storage is in 2nd account
         let account = unsafe {
         let account = unsafe {
@@ -983,7 +984,12 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
             )
             )
             .into_int_value();
             .into_int_value();
 
 
-        contract
+        let member_size = contract
+            .context
+            .i32_type()
+            .const_int(elem_ty.size_of(contract.ns).to_u64().unwrap(), false);
+
+        let length_bytes = contract
             .builder
             .builder
             .build_call(
             .build_call(
                 contract.module.get_function("account_data_len").unwrap(),
                 contract.module.get_function("account_data_len").unwrap(),
@@ -993,7 +999,11 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
             .try_as_basic_value()
             .try_as_basic_value()
             .left()
             .left()
             .unwrap()
             .unwrap()
-            .into_int_value()
+            .into_int_value();
+
+        contract
+            .builder
+            .build_int_unsigned_div(length_bytes, member_size, "")
     }
     }
 
 
     fn get_storage_int(
     fn get_storage_int(
@@ -1754,7 +1764,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         _vartab: &HashMap<usize, Variable<'b>>,
         _vartab: &HashMap<usize, Variable<'b>>,
         _function: FunctionValue<'b>,
         _function: FunctionValue<'b>,
     ) -> BasicValueEnum<'b> {
     ) -> BasicValueEnum<'b> {
-        unimplemented!();
+        unimplemented!()
     }
     }
 
 
     /// Crypto Hash
     /// Crypto Hash

+ 1 - 0
src/emit/substrate.rs

@@ -2792,6 +2792,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         contract: &Contract<'a>,
         contract: &Contract<'a>,
         _function: FunctionValue,
         _function: FunctionValue,
         slot: IntValue<'a>,
         slot: IntValue<'a>,
+        _ty: &ast::Type,
     ) -> IntValue<'a> {
     ) -> IntValue<'a> {
         let slot_ptr = contract.builder.build_alloca(slot.get_type(), "slot");
         let slot_ptr = contract.builder.build_alloca(slot.get_type(), "slot");
         contract.builder.build_store(slot_ptr, slot);
         contract.builder.build_store(slot_ptr, slot);

+ 19 - 6
src/sema/ast.rs

@@ -528,7 +528,12 @@ pub enum Expression {
     DynamicArrayPush(pt::Loc, Box<Expression>, Type, Box<Expression>),
     DynamicArrayPush(pt::Loc, Box<Expression>, Type, Box<Expression>),
     DynamicArrayPop(pt::Loc, Box<Expression>, Type),
     DynamicArrayPop(pt::Loc, Box<Expression>, Type),
     StorageBytesSubscript(pt::Loc, Box<Expression>, Box<Expression>),
     StorageBytesSubscript(pt::Loc, Box<Expression>, Box<Expression>),
-    StorageBytesLength(pt::Loc, Box<Expression>),
+    StorageArrayLength {
+        loc: pt::Loc,
+        ty: Type,
+        array: Box<Expression>,
+        elem_ty: Type,
+    },
     StringCompare(pt::Loc, StringLocation, StringLocation),
     StringCompare(pt::Loc, StringLocation, StringLocation),
     StringConcat(pt::Loc, Type, StringLocation, StringLocation),
     StringConcat(pt::Loc, Type, StringLocation, StringLocation),
 
 
@@ -810,9 +815,17 @@ impl Expression {
                         Box::new(filter(index, ctx)),
                         Box::new(filter(index, ctx)),
                     )
                     )
                 }
                 }
-                Expression::StorageBytesLength(loc, expr) => {
-                    Expression::StorageBytesLength(*loc, Box::new(filter(expr, ctx)))
-                }
+                Expression::StorageArrayLength {
+                    loc,
+                    ty,
+                    array,
+                    elem_ty,
+                } => Expression::StorageArrayLength {
+                    loc: *loc,
+                    ty: ty.clone(),
+                    array: Box::new(filter(array, ctx)),
+                    elem_ty: elem_ty.clone(),
+                },
                 Expression::StringCompare(loc, left, right) => Expression::StringCompare(
                 Expression::StringCompare(loc, left, right) => Expression::StringCompare(
                     *loc,
                     *loc,
                     match left {
                     match left {
@@ -1014,8 +1027,8 @@ impl Expression {
                     left.recurse(cx, f);
                     left.recurse(cx, f);
                     right.recurse(cx, f);
                     right.recurse(cx, f);
                 }
                 }
-                Expression::StorageBytesLength(_, expr)
-                | Expression::DynamicArrayPop(_, expr, _) => expr.recurse(cx, f),
+                Expression::DynamicArrayPop(_, expr, _) => expr.recurse(cx, f),
+                Expression::StorageArrayLength { array, .. } => array.recurse(cx, f),
                 Expression::StringCompare(_, left, right)
                 Expression::StringCompare(_, left, right)
                 | Expression::StringConcat(_, _, left, right) => {
                 | Expression::StringConcat(_, _, left, right) => {
                     if let StringLocation::RunTime(expr) = left {
                     if let StringLocation::RunTime(expr) = left {

+ 22 - 26
src/sema/expression.rs

@@ -77,7 +77,7 @@ impl Expression {
             | Expression::DynamicArrayPush(loc, _, _, _)
             | Expression::DynamicArrayPush(loc, _, _, _)
             | Expression::DynamicArrayPop(loc, _, _)
             | Expression::DynamicArrayPop(loc, _, _)
             | Expression::StorageBytesSubscript(loc, _, _)
             | Expression::StorageBytesSubscript(loc, _, _)
-            | Expression::StorageBytesLength(loc, _)
+            | Expression::StorageArrayLength { loc, .. }
             | Expression::StringCompare(loc, _, _)
             | Expression::StringCompare(loc, _, _)
             | Expression::StringConcat(loc, _, _, _)
             | Expression::StringConcat(loc, _, _, _)
             | Expression::Keccak256(loc, _, _)
             | Expression::Keccak256(loc, _, _)
@@ -166,7 +166,7 @@ impl Expression {
                 }
                 }
             }
             }
             Expression::DynamicArrayLength(_, _) => Type::Uint(32),
             Expression::DynamicArrayLength(_, _) => Type::Uint(32),
-            Expression::StorageBytesLength(_, _) => Type::Uint(32),
+            Expression::StorageArrayLength { ty, .. } => ty.clone(),
             Expression::StorageBytesSubscript(_, _, _) => {
             Expression::StorageBytesSubscript(_, _, _) => {
                 Type::StorageRef(Box::new(Type::Bytes(1)))
                 Type::StorageRef(Box::new(Type::Bytes(1)))
             }
             }
@@ -4274,7 +4274,7 @@ fn member_access(
                     .find(|(_, field)| id.name == field.name)
                     .find(|(_, field)| id.name == field.name)
                 {
                 {
                     Ok(Expression::StructMember(
                     Ok(Expression::StructMember(
-                        *loc,
+                        id.loc,
                         Type::StorageRef(Box::new(field.ty.clone())),
                         Type::StorageRef(Box::new(field.ty.clone())),
                         Box::new(expr),
                         Box::new(expr),
                         field_no,
                         field_no,
@@ -4290,32 +4290,28 @@ fn member_access(
                     Err(())
                     Err(())
                 }
                 }
             }
             }
-            Type::Bytes(n) => {
+            Type::Array(_, _) => {
                 if id.name == "length" {
                 if id.name == "length" {
-                    return Ok(Expression::NumberLiteral(
-                        *loc,
-                        Type::Uint(8),
-                        BigInt::from_u8(n).unwrap(),
-                    ));
+                    let elem_ty = expr.ty().storage_array_elem().deref_into();
+
+                    return Ok(Expression::StorageArrayLength {
+                        loc: id.loc,
+                        ty: ns.storage_type(),
+                        array: Box::new(expr),
+                        elem_ty,
+                    });
                 }
                 }
             }
             }
-            Type::Array(_, dim) => {
+            Type::Bytes(_) | Type::DynamicBytes => {
                 if id.name == "length" {
                 if id.name == "length" {
-                    return match dim.last().unwrap() {
-                        None => Ok(Expression::StorageLoad(
-                            id.loc,
-                            ns.storage_type(),
-                            Box::new(expr),
-                        )),
-                        Some(d) => {
-                            bigint_to_expression(loc, d, ns, diagnostics, Some(&Type::Uint(256)))
-                        }
-                    };
-                }
-            }
-            Type::DynamicBytes => {
-                if id.name == "length" {
-                    return Ok(Expression::StorageBytesLength(*loc, Box::new(expr)));
+                    let elem_ty = expr.ty().storage_array_elem().deref_into();
+
+                    return Ok(Expression::StorageArrayLength {
+                        loc: id.loc,
+                        ty: Type::Uint(32),
+                        array: Box::new(expr),
+                        elem_ty,
+                    });
                 }
                 }
             }
             }
             _ => {}
             _ => {}
@@ -4328,7 +4324,7 @@ fn member_access(
                 .find(|f| id.name == f.1.name)
                 .find(|f| id.name == f.1.name)
             {
             {
                 return Ok(Expression::StructMember(
                 return Ok(Expression::StructMember(
-                    *loc,
+                    id.loc,
                     Type::Ref(Box::new(f.ty.clone())),
                     Type::Ref(Box::new(f.ty.clone())),
                     Box::new(expr),
                     Box::new(expr),
                     i,
                     i,

+ 1 - 1
src/sema/mutability.rs

@@ -231,7 +231,7 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
             right.recurse(state, read_expression);
             right.recurse(state, read_expression);
             left.recurse(state, write_expression);
             left.recurse(state, write_expression);
         }
         }
-        Expression::StorageBytesLength(loc, _)
+        Expression::StorageArrayLength { loc, .. }
         | Expression::StorageBytesSubscript(loc, _, _)
         | Expression::StorageBytesSubscript(loc, _, _)
         | Expression::StorageVariable(loc, _, _, _)
         | Expression::StorageVariable(loc, _, _, _)
         | Expression::StorageLoad(loc, _, _) => state.read(loc),
         | Expression::StorageLoad(loc, _, _) => state.read(loc),

+ 10 - 0
src/sema/types.rs

@@ -833,6 +833,7 @@ impl Type {
     /// array types and will cause a panic otherwise.
     /// array types and will cause a panic otherwise.
     pub fn storage_array_elem(&self) -> Self {
     pub fn storage_array_elem(&self) -> Self {
         match self {
         match self {
+            Type::DynamicBytes => Type::Bytes(1),
             Type::Array(ty, dim) if dim.len() > 1 => Type::StorageRef(Box::new(Type::Array(
             Type::Array(ty, dim) if dim.len() > 1 => Type::StorageRef(Box::new(Type::Array(
                 ty.clone(),
                 ty.clone(),
                 dim[..dim.len() - 1].to_vec(),
                 dim[..dim.len() - 1].to_vec(),
@@ -1090,6 +1091,15 @@ impl Type {
         }
         }
     }
     }
 
 
+    /// If the type is Ref or StorageRef, get the underlying type
+    pub fn deref_into(self) -> Self {
+        match self {
+            Type::StorageRef(r) => *r,
+            Type::Ref(r) => *r,
+            _ => self,
+        }
+    }
+
     /// If the type is Ref, get the underlying type
     /// If the type is Ref, get the underlying type
     pub fn deref_memory(&self) -> &Self {
     pub fn deref_memory(&self) -> &Self {
         match self {
         match self {