Эх сурвалжийг харах

Allow null pointer for empty string

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 жил өмнө
parent
commit
e0289d6366

+ 4 - 41
src/emit/ethabiencoder.rs

@@ -364,22 +364,7 @@ impl EthAbiEncoder {
                     arg
                 };
 
-                // Now, write the length to dynamic
-                let len = unsafe {
-                    contract.builder.build_gep(
-                        arg.into_pointer_value(),
-                        &[
-                            contract.context.i32_type().const_zero(),
-                            contract.context.i32_type().const_zero(),
-                        ],
-                        "array.len",
-                    )
-                };
-
-                let len = contract
-                    .builder
-                    .build_load(len, "array.len")
-                    .into_int_value();
+                let len = contract.vector_len(arg);
 
                 // write the length to dynamic
                 self.encode_primitive(
@@ -406,16 +391,7 @@ impl EthAbiEncoder {
                 );
 
                 // now copy the string data
-                let string_start = unsafe {
-                    contract.builder.build_gep(
-                        arg.into_pointer_value(),
-                        &[
-                            contract.context.i32_type().const_zero(),
-                            contract.context.i32_type().const_int(2, false),
-                        ],
-                        "string_start",
-                    )
-                };
+                let string_start = contract.vector_bytes(arg);
 
                 contract.builder.build_call(
                     contract.module.get_function("__memcpy").unwrap(),
@@ -1037,26 +1013,12 @@ impl EthAbiEncoder {
                     arg
                 };
 
-                let len = unsafe {
-                    contract.builder.build_gep(
-                        arg.into_pointer_value(),
-                        &[
-                            contract.context.i32_type().const_zero(),
-                            contract.context.i32_type().const_zero(),
-                        ],
-                        "string.len",
-                    )
-                };
-
                 // The dynamic part is the length (=32 bytes) and the string
                 // data itself. Length 0 occupies no space, length 1-32 occupies
                 // 32 bytes, etc
                 contract.builder.build_and(
                     contract.builder.build_int_add(
-                        contract
-                            .builder
-                            .build_load(len, "string.len")
-                            .into_int_value(),
+                        contract.vector_len(arg),
                         contract.context.i32_type().const_int(32 + 31, false),
                         "",
                     ),
@@ -1711,6 +1673,7 @@ impl EthAbiEncoder {
                     "string_len",
                 );
 
+                // Special case string_len == 0 => null pointer?
                 let string_end =
                     contract
                         .builder

+ 36 - 2
src/emit/mod.rs

@@ -2956,6 +2956,20 @@ pub trait TargetRuntime<'a> {
 
         for (no, v) in &cfg.vars {
             match v.storage {
+                Storage::Local if v.ty == ast::Type::String || v.ty == ast::Type::DynamicBytes => {
+                    vars.insert(
+                        *no,
+                        Variable {
+                            value: contract
+                                .module
+                                .get_struct_type("struct.vector")
+                                .unwrap()
+                                .ptr_type(AddressSpace::Generic)
+                                .const_null()
+                                .into(),
+                        },
+                    );
+                }
                 Storage::Local if v.ty.is_reference_type() && !v.ty.is_contract_storage() => {
                     let ty = contract.llvm_type(&v.ty);
 
@@ -6163,6 +6177,17 @@ impl<'a> Contract<'a> {
         elem_size: IntValue<'a>,
         init: Option<&Vec<u8>>,
     ) -> PointerValue<'a> {
+        if let Some(init) = init {
+            if init.is_empty() {
+                return self
+                    .module
+                    .get_struct_type("struct.vector")
+                    .unwrap()
+                    .ptr_type(AddressSpace::Generic)
+                    .const_null();
+            }
+        }
+
         let init = match init {
             None => self.builder.build_int_to_ptr(
                 self.context.i32_type().const_all_ones(),
@@ -6205,9 +6230,11 @@ impl<'a> Contract<'a> {
                 .into_int_value()
         } else {
             // field 0 is the length
+            let vector = vector.into_pointer_value();
+
             let len = unsafe {
                 self.builder.build_gep(
-                    vector.into_pointer_value(),
+                    vector,
                     &[
                         self.context.i32_type().const_zero(),
                         self.context.i32_type().const_zero(),
@@ -6216,7 +6243,14 @@ impl<'a> Contract<'a> {
                 )
             };
 
-            self.builder.build_load(len, "vector_len").into_int_value()
+            self.builder
+                .build_select(
+                    self.builder.build_is_null(vector, "vector_is_null"),
+                    self.context.i32_type().const_zero(),
+                    self.builder.build_load(len, "vector_len").into_int_value(),
+                    "length",
+                )
+                .into_int_value()
         }
     }
 

+ 91 - 10
src/emit/substrate.rs

@@ -1840,14 +1840,36 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn set_storage_string(
         &self,
         contract: &Contract<'a>,
-        _function: FunctionValue<'a>,
+        function: FunctionValue<'a>,
         slot: PointerValue<'a>,
         dest: BasicValueEnum<'a>,
     ) {
         let len = contract.vector_len(dest);
         let data = contract.vector_bytes(dest);
 
-        // TODO: check for non-zero
+        let exists = contract.builder.build_int_compare(
+            IntPredicate::NE,
+            len,
+            contract.context.i32_type().const_zero(),
+            "exists",
+        );
+
+        let delete_block = contract
+            .context
+            .append_basic_block(function, "delete_block");
+
+        let set_block = contract.context.append_basic_block(function, "set_block");
+
+        let done_storage = contract
+            .context
+            .append_basic_block(function, "done_storage");
+
+        contract
+            .builder
+            .build_conditional_branch(exists, set_block, delete_block);
+
+        contract.builder.position_at_end(set_block);
+
         contract.builder.build_call(
             contract.module.get_function("seal_set_storage").unwrap(),
             &[
@@ -1871,6 +1893,27 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             ],
             "",
         );
+
+        contract.builder.build_unconditional_branch(done_storage);
+
+        contract.builder.position_at_end(delete_block);
+
+        contract.builder.build_call(
+            contract.module.get_function("seal_clear_storage").unwrap(),
+            &[contract
+                .builder
+                .build_pointer_cast(
+                    slot,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "",
+                )
+                .into()],
+            "",
+        );
+
+        contract.builder.build_unconditional_branch(done_storage);
+
+        contract.builder.position_at_end(done_storage);
     }
 
     /// Read from substrate storage
@@ -1954,7 +1997,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn get_storage_string(
         &self,
         contract: &Contract<'a>,
-        _function: FunctionValue,
+        function: FunctionValue,
         slot: PointerValue<'a>,
     ) -> PointerValue<'a> {
         let scratch_buf = contract.builder.build_pointer_cast(
@@ -2001,14 +2044,31 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             "storage_exists",
         );
 
-        let length = contract.builder.build_select(
-            exists,
-            contract.builder.build_load(scratch_len, "string_len"),
-            contract.context.i32_type().const_zero().into(),
-            "string_length",
-        );
+        let ty = contract
+            .module
+            .get_struct_type("struct.vector")
+            .unwrap()
+            .ptr_type(AddressSpace::Generic);
+
+        let entry = contract.builder.get_insert_block().unwrap();
+
+        let retrieve_block = contract
+            .context
+            .append_basic_block(function, "retrieve_block");
+
+        let done_storage = contract
+            .context
+            .append_basic_block(function, "done_storage");
 
         contract
+            .builder
+            .build_conditional_branch(exists, retrieve_block, done_storage);
+
+        contract.builder.position_at_end(retrieve_block);
+
+        let length = contract.builder.build_load(scratch_len, "string_len");
+
+        let loaded_string = contract
             .builder
             .build_call(
                 contract.module.get_function("vector_new").unwrap(),
@@ -2022,7 +2082,28 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             .try_as_basic_value()
             .left()
             .unwrap()
-            .into_pointer_value()
+            .into_pointer_value();
+
+        contract.builder.build_unconditional_branch(done_storage);
+
+        contract.builder.position_at_end(done_storage);
+
+        let res = contract.builder.build_phi(ty, "storage_res");
+
+        res.add_incoming(&[
+            (&loaded_string, retrieve_block),
+            (
+                &contract
+                    .module
+                    .get_struct_type("struct.vector")
+                    .unwrap()
+                    .ptr_type(AddressSpace::Generic)
+                    .const_null(),
+                entry,
+            ),
+        ]);
+
+        res.as_basic_value().into_pointer_value()
     }
 
     /// Read string from substrate storage