Browse Source

Refactor SCALE encoder into codegen (#1128)

This PR makes the AbiEncoding trait more granular. This allowed to move most of the logic into default implementations, leaving only a few Types that needed to be handled differently in SCALE and Borsh. This means the Borsh and SCALE encoders can share most of their code.

The decoding side is untouched by this PR as it can be done independent.
Cyrill Leutwiler 2 năm trước cách đây
mục cha
commit
921be89f1d

+ 1 - 1
Cargo.toml

@@ -55,7 +55,7 @@ anchor-syn = { version = "0.26", features = ["idl"] }
 convert_case = "0.6"
 parse-display = "0.8.0"
 parity-scale-codec = "3.1"
-ink = "4.0.0-beta"
+ink = "4.0.0-beta.1"
 scale-info = "2.3"
 
 [dev-dependencies]

+ 4 - 12
src/codegen/cfg.rs

@@ -156,10 +156,8 @@ pub enum Instr {
     /// Emit event
     EmitEvent {
         event_no: usize,
-        data: Vec<Expression>,
-        data_tys: Vec<Type>,
+        data: Expression,
         topics: Vec<Expression>,
-        topic_tys: Vec<Type>,
     },
     /// Write Buffer
     WriteBuffer {
@@ -312,10 +310,7 @@ impl Instr {
             }
 
             Instr::EmitEvent { data, topics, .. } => {
-                for expr in data {
-                    expr.recurse(cx, f);
-                }
-
+                data.recurse(cx, f);
                 for expr in topics {
                     expr.recurse(cx, f);
                 }
@@ -1211,17 +1206,14 @@ impl ControlFlowGraph {
                 event_no,
                 ..
             } => format!(
-                "emit event {} topics {} data {}",
+                "emit event {} topics {} data {} ",
                 ns.events[*event_no].symbol_name(ns),
                 topics
                     .iter()
                     .map(|expr| self.expr_to_string(contract, ns, expr))
                     .collect::<Vec<String>>()
                     .join(", "),
-                data.iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
+                self.expr_to_string(contract, ns, data)
             ),
             Instr::Nop => String::from("nop"),
             Instr::MemCopy {

+ 1 - 10
src/codegen/constant_folding.rs

@@ -290,15 +290,8 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                 Instr::EmitEvent {
                     event_no,
                     data,
-                    data_tys,
                     topics,
-                    topic_tys,
                 } => {
-                    let data = data
-                        .iter()
-                        .map(|e| expression(e, Some(&vars), cfg, ns).0)
-                        .collect();
-
                     let topics = topics
                         .iter()
                         .map(|e| expression(e, Some(&vars), cfg, ns).0)
@@ -306,10 +299,8 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
 
                     cfg.blocks[block_no].instr[instr_no].1 = Instr::EmitEvent {
                         event_no: *event_no,
-                        data,
-                        data_tys: data_tys.clone(),
+                        data: expression(data, Some(&vars), cfg, ns).0,
                         topics,
-                        topic_tys: topic_tys.clone(),
                     }
                 }
                 Instr::MemCopy {

+ 3 - 3
src/codegen/constructor.rs

@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
-use crate::codegen::encoding::create_encoder;
 use crate::codegen::expression::{default_gas, expression};
 use crate::codegen::vartable::Vartable;
 use crate::codegen::{Expression, Options};
@@ -11,6 +10,8 @@ use crate::sema::{
 };
 use solang_parser::pt::Loc;
 
+use super::encoding::abi_encode;
+
 /// This function encodes the constructor arguments and place an instruction in the CFG to
 /// call the constructor of a contract.
 pub(super) fn call_constructor(
@@ -75,8 +76,7 @@ pub(super) fn call_constructor(
 
     args.append(&mut constructor_args);
 
-    let mut encoder = create_encoder(ns, false);
-    let (encoded_args, encoded_args_len) = encoder.abi_encode(loc, args, ns, vartab, cfg);
+    let (encoded_args, encoded_args_len) = abi_encode(loc, args, ns, vartab, cfg, false);
 
     cfg.add(
         vartab,

+ 4 - 2
src/codegen/dispatch.rs

@@ -15,6 +15,8 @@ use num_bigint::{BigInt, Sign};
 use num_traits::Zero;
 use solang_parser::{pt, pt::Loc};
 
+use super::encoding::abi_encode;
+
 /// Create the dispatch for the Solana target
 pub(super) fn function_dispatch(
     contract_no: usize,
@@ -305,7 +307,7 @@ fn add_function_dispatch_case(
         .iter()
         .map(|e| e.ty.clone())
         .collect::<Vec<Type>>();
-    let mut encoder = create_encoder(ns, false);
+    let encoder = create_encoder(ns, false);
     let decoded = encoder.abi_decode(
         &Loc::Codegen,
         argsdata,
@@ -337,7 +339,7 @@ fn add_function_dispatch_case(
     );
 
     if !func_cfg.returns.is_empty() {
-        let (data, data_len) = encoder.abi_encode(&Loc::Codegen, returns_expr, ns, vartab, cfg);
+        let (data, data_len) = abi_encode(&Loc::Codegen, returns_expr, ns, vartab, cfg, false);
         let zext_len = Expression::ZeroExt(Loc::Codegen, Type::Uint(64), Box::new(data_len));
         cfg.add(
             vartab,

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 87 - 735
src/codegen/encoding/borsh_encoding.rs


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 797 - 330
src/codegen/encoding/mod.rs


+ 198 - 42
src/codegen/encoding/scale_encoding.rs

@@ -1,66 +1,202 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
-use crate::codegen::encoding::AbiEncoding;
+use crate::codegen::encoding::{increment_by, AbiEncoding};
 use crate::codegen::vartable::Vartable;
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{Namespace, Parameter, RetrieveType, Type};
-use solang_parser::pt::Loc;
+use crate::sema::ast::{Namespace, Parameter, Type, Type::Uint};
+use solang_parser::pt::{Loc, Loc::Codegen};
+use std::collections::HashMap;
 
-/// This struct implements the trait AbiEncoding for Parity's Scale encoding
 pub(super) struct ScaleEncoding {
-    /// Are we pakced encoding?
+    storage_cache: HashMap<usize, Expression>,
     packed_encoder: bool,
 }
 
 impl ScaleEncoding {
-    pub fn new(packed: bool) -> ScaleEncoding {
-        ScaleEncoding {
+    pub fn new(packed: bool) -> Self {
+        Self {
+            storage_cache: HashMap::new(),
             packed_encoder: packed,
         }
     }
 }
 
-impl AbiEncoding for ScaleEncoding {
-    fn abi_encode(
-        &mut self,
-        loc: &Loc,
-        mut args: Vec<Expression>,
-        _ns: &Namespace,
-        vartab: &mut Vartable,
-        cfg: &mut ControlFlowGraph,
-    ) -> (Expression, Expression) {
-        let tys = args.iter().map(|e| e.ty()).collect::<Vec<Type>>();
+/// Encode `expr` into `buffer` as a compact integer. More information can found in the
+/// [SCALE documentation](https://docs.substrate.io/reference/scale-codec/).
+fn encode_compact(
+    expr: &Expression,
+    buffer: Option<&Expression>,
+    offset: Option<&Expression>,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> Expression {
+    let small = cfg.new_basic_block("small".into());
+    let medium = cfg.new_basic_block("medium".into());
+    let medium_or_big = cfg.new_basic_block("medium_or_big".into());
+    let big = cfg.new_basic_block("big".into());
+    let done = cfg.new_basic_block("done".into());
+    let fail = cfg.new_basic_block("fail".into());
+    let prepare = cfg.new_basic_block("prepare".into());
+    let cmp_val = Expression::NumberLiteral(Codegen, Uint(32), 0x40000000.into());
+    let compare = Expression::UnsignedMore(Codegen, expr.clone().into(), cmp_val.into());
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: compare,
+            true_block: fail,
+            false_block: prepare,
+        },
+    );
 
-        let encoded_buffer = vartab.temp_anonymous(&Type::DynamicBytes);
-        let mut packed: Vec<Expression> = Vec::new();
-        if self.packed_encoder {
-            std::mem::swap(&mut packed, &mut args);
-        }
+    cfg.set_basic_block(fail);
+    cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
+
+    cfg.set_basic_block(prepare);
+    let cmp_val = Expression::NumberLiteral(Codegen, Uint(32), 0x40.into());
+    let compare = Expression::UnsignedMore(Codegen, expr.clone().into(), cmp_val.into());
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: compare,
+            true_block: medium_or_big,
+            false_block: small,
+        },
+    );
+
+    cfg.set_basic_block(medium_or_big);
+    let cmp_val = Expression::NumberLiteral(Codegen, Uint(32), 0x4000.into());
+    let compare = Expression::UnsignedMore(Codegen, expr.clone().into(), cmp_val.into());
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: compare,
+            true_block: big,
+            false_block: medium,
+        },
+    );
+    vartab.new_dirty_tracker();
+    let size_variable = vartab.temp_anonymous(&Uint(32));
+    let four = Expression::NumberLiteral(Codegen, Uint(32), 4.into()).into();
+    let mul = Expression::Multiply(Codegen, Uint(32), false, expr.clone().into(), four);
 
+    cfg.set_basic_block(small);
+    if let (Some(buffer), Some(offset)) = (buffer, offset) {
         cfg.add(
             vartab,
-            Instr::Set {
-                loc: *loc,
-                res: encoded_buffer,
-                expr: Expression::AbiEncode {
-                    loc: *loc,
-                    packed,
-                    args,
-                    tys,
-                },
+            Instr::WriteBuffer {
+                buf: buffer.clone(),
+                offset: offset.clone(),
+                value: mul.clone(),
             },
         );
+    }
+    let one = Expression::NumberLiteral(Codegen, Uint(32), 1.into());
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_variable,
+            expr: one.clone(),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: done });
 
-        let encoded_expr = Expression::Variable(*loc, Type::DynamicBytes, encoded_buffer);
-        let buffer_len = Expression::Builtin(
-            *loc,
-            vec![Type::Uint(32)],
-            Builtin::ArrayLength,
-            vec![encoded_expr.clone()],
+    cfg.set_basic_block(medium);
+    if let (Some(buffer), Some(offset)) = (buffer, offset) {
+        let mul2 = Expression::BitwiseOr(Codegen, Uint(32), mul.clone().into(), one.into());
+        cfg.add(
+            vartab,
+            Instr::WriteBuffer {
+                buf: buffer.clone(),
+                offset: offset.clone(),
+                value: mul2,
+            },
         );
+    }
+    let two = Expression::NumberLiteral(Codegen, Uint(32), 2.into());
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_variable,
+            expr: two.clone(),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: done });
 
-        (encoded_expr, buffer_len)
+    cfg.set_basic_block(big);
+    if let (Some(buffer), Some(offset)) = (buffer, offset) {
+        let mul2 = Expression::BitwiseOr(Codegen, Uint(32), mul.into(), two.into());
+        cfg.add(
+            vartab,
+            Instr::WriteBuffer {
+                buf: buffer.clone(),
+                offset: offset.clone(),
+                value: mul2,
+            },
+        );
+    }
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_variable,
+            expr: Expression::NumberLiteral(Codegen, Uint(32), 4.into()),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: done });
+
+    cfg.set_basic_block(done);
+    cfg.set_phis(done, vartab.pop_dirty_tracker());
+    Expression::Variable(Codegen, Uint(32), size_variable)
+}
+
+impl AbiEncoding for ScaleEncoding {
+    fn size_width(
+        &self,
+        size: &Expression,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        // FIXME:
+        // It should be possible to optimize this to estimate always 4 bytes.
+        // `codegen::abi_encode()` also returns the actual encoded size,
+        // so slightly overestimating it shouldn't  matter.
+        // However, the actual length of the encoded data produced by `codegen::abi_encode()`
+        // is ignored in some places, wich results in buggy contracts if we have not an exact estimate.
+        // Once this is fixed (the encoded size return by `codegen::abi_encode()` must never be ignored),
+        // this can just be always 4 bytes .
+        encode_compact(size, None, None, vartab, cfg)
+    }
+
+    fn encode_external_function(
+        &mut self,
+        expr: &Expression,
+        buffer: &Expression,
+        offset: &Expression,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        let addr_len = ns.address_length.into();
+        let address = expr.external_function_address();
+        let size = self.encode_directly(&address, buffer, offset, vartab, cfg, addr_len);
+        let offset = Expression::Add(Codegen, Uint(32), false, offset.clone().into(), size.into());
+        let selector = expr.external_function_selector();
+        self.encode_directly(&selector, buffer, &offset, vartab, cfg, 4.into());
+        Expression::NumberLiteral(Codegen, Uint(32), (ns.address_length + 4).into())
+    }
+
+    fn encode_size(
+        &mut self,
+        expr: &Expression,
+        buffer: &Expression,
+        offset: &Expression,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        encode_compact(expr, Some(buffer), Some(offset), vartab, cfg)
     }
 
     fn abi_decode(
@@ -108,12 +244,32 @@ impl AbiEncoding for ScaleEncoding {
         returns
     }
 
-    fn cache_storage_loaded(&mut self, _arg_no: usize, _expr: Expression) {
-        unreachable!("This function is not needed for Scale encoding");
+    fn storage_cache_insert(&mut self, arg_no: usize, expr: Expression) {
+        self.storage_cache.insert(arg_no, expr);
     }
 
-    fn get_encoding_size(&self, _expr: &Expression, _ty: &Type, _ns: &Namespace) -> Expression {
-        unreachable!("This function is not needed for Scale encoding");
+    fn storage_cache_remove(&mut self, arg_no: usize) -> Option<Expression> {
+        self.storage_cache.remove(&arg_no)
+    }
+
+    fn calculate_string_size(
+        &self,
+        expr: &Expression,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        // When encoding a variable length array, the total size is "compact encoded array length + N elements"
+        let length = Expression::Builtin(
+            Codegen,
+            vec![Uint(32)],
+            Builtin::ArrayLength,
+            vec![expr.clone()],
+        );
+        if self.is_packed() {
+            length
+        } else {
+            increment_by(encode_compact(&length, None, None, vartab, cfg), length)
+        }
     }
 
     fn is_packed(&self) -> bool {

+ 3 - 8
src/codegen/events/solana.rs

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
-use crate::codegen::encoding::create_encoder;
+use crate::codegen::encoding::abi_encode;
 use crate::codegen::events::EventEmitter;
 use crate::codegen::expression::expression;
 use crate::codegen::vartable::Vartable;
@@ -45,19 +45,14 @@ impl EventEmitter for SolanaEventEmitter<'_> {
 
         let mut to_be_encoded: Vec<Expression> = vec![discriminator];
         to_be_encoded.append(&mut codegen_args);
-
-        let mut encoder = create_encoder(self.ns, false);
-        let (abi_encoded, abi_encoded_size) =
-            encoder.abi_encode(&self.loc, to_be_encoded, self.ns, vartab, cfg);
+        let data = abi_encode(&self.loc, to_be_encoded, self.ns, vartab, cfg, false).0;
 
         cfg.add(
             vartab,
             Instr::EmitEvent {
                 event_no: self.event_no,
-                data: vec![abi_encoded, abi_encoded_size],
-                data_tys: vec![],
+                data,
                 topics: vec![],
-                topic_tys: vec![],
             },
         );
     }

+ 10 - 10
src/codegen/events/substrate.rs

@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use std::collections::VecDeque;
+use std::vec;
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::encoding::abi_encode;
 use crate::codegen::events::EventEmitter;
 use crate::codegen::expression::expression;
 use crate::codegen::vartable::Vartable;
@@ -41,16 +43,18 @@ impl EventEmitter for SubstrateEventEmitter<'_> {
         vartab: &mut Vartable,
         opt: &Options,
     ) {
-        let mut data = Vec::new();
-        let mut data_tys = Vec::new();
-        let mut topics = Vec::new();
-        let mut topic_tys = Vec::new();
-
         let loc = pt::Loc::Builtin;
         let event = &self.ns.events[self.event_no];
         // For freestanding events the name of the emitting contract is used
         let contract_name = &self.ns.contracts[event.contract.unwrap_or(contract_no)].name;
         let hash_len = Box::new(Expression::NumberLiteral(loc, Type::Uint(32), 32.into()));
+        let id = self.ns.contracts[contract_no]
+            .emits_events
+            .iter()
+            .position(|e| *e == self.event_no)
+            .expect("contract emits this event");
+        let mut data = vec![Expression::NumberLiteral(loc, Type::Uint(8), id.into())];
+        let mut topics = vec![];
 
         // Events that are not anonymous always have themselves as a topic.
         // This is static and can be calculated at compile time.
@@ -63,7 +67,6 @@ impl EventEmitter for SubstrateEventEmitter<'_> {
                 hash_len.clone(),
                 Some(topic_hash(encoded.as_bytes())),
             ));
-            topic_tys.push(Type::DynamicBytes);
         };
 
         // Topic prefixes are static and can be calculated at compile time.
@@ -95,7 +98,6 @@ impl EventEmitter for SubstrateEventEmitter<'_> {
                     expr: value_exp,
                 },
             );
-            data_tys.push(value.ty());
             data.push(value.clone());
 
             if !field.indexed {
@@ -169,18 +171,16 @@ impl EventEmitter for SubstrateEventEmitter<'_> {
             cfg.set_basic_block(done_block);
             cfg.set_phis(done_block, vartab.pop_dirty_tracker());
 
-            topic_tys.push(Type::DynamicBytes);
             topics.push(buffer);
         }
 
+        let data = abi_encode(&loc, data, self.ns, vartab, cfg, false).0;
         cfg.add(
             vartab,
             Instr::EmitEvent {
                 event_no: self.event_no,
                 data,
-                data_tys,
                 topics,
-                topic_tys,
             },
         );
     }

+ 13 - 18
src/codegen/expression.rs

@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
+use super::encoding::abi_encode;
 use super::storage::{
     array_offset, array_pop, array_push, storage_slots_array_pop, storage_slots_array_push,
 };
@@ -842,7 +843,7 @@ pub fn expression(
             kind: ast::Builtin::AbiEncode,
             args,
             ..
-        } => abi_encode(args, cfg, contract_no, func, ns, vartab, loc, opt),
+        } => abi_encode_many(args, cfg, contract_no, func, ns, vartab, loc, opt),
         ast::Expression::Builtin {
             loc,
             kind: ast::Builtin::AbiEncodePacked,
@@ -1473,7 +1474,7 @@ fn payable_transfer(
     Expression::Poison
 }
 
-fn abi_encode(
+fn abi_encode_many(
     args: &[ast::Expression],
     cfg: &mut ControlFlowGraph,
     contract_no: usize,
@@ -1488,8 +1489,7 @@ fn abi_encode(
         .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
         .collect::<Vec<Expression>>();
 
-    let mut encoder = create_encoder(ns, false);
-    encoder.abi_encode(loc, args, ns, vartab, cfg).0
+    abi_encode(loc, args, ns, vartab, cfg, false).0
 }
 
 fn abi_encode_packed(
@@ -1507,12 +1507,11 @@ fn abi_encode_packed(
         .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
         .collect::<Vec<Expression>>();
 
-    let mut encoder = create_encoder(ns, true);
-    let (encoded, _) = encoder.abi_encode(loc, packed, ns, vartab, cfg);
+    let (encoded, _) = abi_encode(loc, packed, ns, vartab, cfg, true);
     encoded
 }
 
-fn encode_with_selector(
+fn encode_many_with_selector(
     loc: &pt::Loc,
     selector: Expression,
     mut args: Vec<Expression>,
@@ -1523,8 +1522,7 @@ fn encode_with_selector(
     let mut encoder_args: Vec<Expression> = Vec::with_capacity(args.len() + 1);
     encoder_args.push(selector);
     encoder_args.append(&mut args);
-    let mut encoder = create_encoder(ns, false);
-    encoder.abi_encode(loc, encoder_args, ns, vartab, cfg).0
+    abi_encode(loc, encoder_args, ns, vartab, cfg, false).0
 }
 
 fn abi_encode_with_selector(
@@ -1550,7 +1548,7 @@ fn abi_encode_with_selector(
     let args = args_iter
         .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
         .collect::<Vec<Expression>>();
-    encode_with_selector(loc, selector, args, ns, vartab, cfg)
+    encode_many_with_selector(loc, selector, args, ns, vartab, cfg)
 }
 
 fn abi_encode_with_signature(
@@ -1581,7 +1579,7 @@ fn abi_encode_with_signature(
     let args = args_iter
         .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
         .collect::<Vec<Expression>>();
-    encode_with_selector(loc, selector, args, ns, vartab, cfg)
+    encode_many_with_selector(loc, selector, args, ns, vartab, cfg)
 }
 
 fn abi_encode_call(
@@ -1612,7 +1610,7 @@ fn abi_encode_call(
     let args = args_iter
         .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
         .collect::<Vec<Expression>>();
-    encode_with_selector(loc, selector, args, ns, vartab, cfg)
+    encode_many_with_selector(loc, selector, args, ns, vartab, cfg)
 }
 
 fn builtin_evm_gasprice(
@@ -2468,8 +2466,7 @@ pub fn emit_function_call(
                     Expression::BytesLiteral(*loc, Type::Bytes(selector.len() as u8), selector),
                 );
 
-                let mut encoder = create_encoder(ns, false);
-                let (payload, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
+                let (payload, _) = abi_encode(loc, args, ns, vartab, cfg, false);
 
                 cfg.add(
                     vartab,
@@ -2533,8 +2530,7 @@ pub fn emit_function_call(
                 tys.insert(0, Type::Bytes(ns.target.selector_length()));
                 args.insert(0, selector);
 
-                let mut encoder = create_encoder(ns, false);
-                let (payload, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
+                let (payload, _) = abi_encode(loc, args, ns, vartab, cfg, false);
 
                 cfg.add(
                     vartab,
@@ -3043,8 +3039,7 @@ pub(super) fn assert_failure(
     let selector = Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(selector));
     let args = vec![selector, arg.unwrap()];
 
-    let mut encoder = create_encoder(ns, false);
-    let (encoded_buffer, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
+    let (encoded_buffer, _) = abi_encode(loc, args, ns, vartab, cfg, false);
 
     cfg.add(
         vartab,

+ 2 - 3
src/codegen/statements.rs

@@ -3,6 +3,7 @@
 use num_bigint::BigInt;
 use std::collections::LinkedList;
 
+use super::encoding::abi_encode;
 use super::expression::{assign_single, default_gas, emit_function_call, expression};
 use super::Options;
 use super::{
@@ -10,7 +11,6 @@ use super::{
     vartable::Vartable,
 };
 use crate::codegen::constructor::call_constructor;
-use crate::codegen::encoding::create_encoder;
 use crate::codegen::events::new_event_emitter;
 use crate::codegen::unused_variable::{
     should_remove_assignment, should_remove_variable, SideEffectsCheckParameters,
@@ -1061,8 +1061,7 @@ fn try_catch(
                 let address = function.external_function_address();
 
                 args.insert(0, selector);
-                let mut encoder = create_encoder(ns, false);
-                let (payload, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
+                let (payload, _) = abi_encode(loc, args, ns, vartab, cfg, false);
 
                 cfg.add(
                     vartab,

+ 1 - 5
src/codegen/strength_reduce/mod.rs

@@ -203,11 +203,7 @@ fn block_reduce(
                     .iter()
                     .map(|e| expression_reduce(e, &vars, ns))
                     .collect();
-
-                *data = data
-                    .iter()
-                    .map(|e| expression_reduce(e, &vars, ns))
-                    .collect();
+                *data = expression_reduce(data, &vars, ns);
             }
             Instr::WriteBuffer { offset, .. } => {
                 *offset = expression_reduce(offset, &vars, ns);

+ 2 - 12
src/codegen/subexpression_elimination/instruction.rs

@@ -142,10 +142,7 @@ impl AvailableExpressionSet {
             }
 
             Instr::EmitEvent { data, topics, .. } => {
-                for expr in data {
-                    let _ = self.gen_expression(expr, ave, cst);
-                }
-
+                let _ = self.gen_expression(data, ave, cst);
                 for expr in topics {
                     let _ = self.gen_expression(expr, ave, cst);
                 }
@@ -420,21 +417,14 @@ impl AvailableExpressionSet {
             Instr::EmitEvent {
                 event_no,
                 data,
-                data_tys,
                 topics,
-                topic_tys,
             } => Instr::EmitEvent {
                 event_no: *event_no,
-                data: data
-                    .iter()
-                    .map(|v| self.regenerate_expression(v, ave, cst).1)
-                    .collect::<Vec<Expression>>(),
-                data_tys: data_tys.clone(),
+                data: self.regenerate_expression(data, ave, cst).1,
                 topics: topics
                     .iter()
                     .map(|v| self.regenerate_expression(v, ave, cst).1)
                     .collect::<Vec<Expression>>(),
-                topic_tys: topic_tys.clone(),
             },
 
             Instr::MemCopy {

+ 3 - 16
src/emit/instructions.rs

@@ -1109,26 +1109,13 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
 
             target.selfdestruct(bin, recipient, ns);
         }
-        Instr::EmitEvent {
-            event_no,
-            data,
-            data_tys,
-            topics,
-            topic_tys,
-        } => {
-            let data = data
-                .iter()
-                .map(|a| expression(target, bin, a, &w.vars, function, ns))
-                .collect::<Vec<BasicValueEnum>>();
-
+        Instr::EmitEvent { data, topics, .. } => {
+            let data = expression(target, bin, data, &w.vars, function, ns);
             let topics = topics
                 .iter()
                 .map(|a| expression(target, bin, a, &w.vars, function, ns))
                 .collect::<Vec<BasicValueEnum>>();
-
-            target.emit_event(
-                bin, contract, function, *event_no, &data, data_tys, &topics, topic_tys, ns,
-            );
+            target.emit_event(bin, function, data, &topics);
         }
         Instr::WriteBuffer { buf, offset, value } => {
             let v = expression(target, bin, buf, &w.vars, function, ns);

+ 2 - 7
src/emit/mod.rs

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::Expression;
-use crate::sema::ast::{CallTy, Contract, Function, Namespace, Parameter, Type};
+use crate::sema::ast::{CallTy, Function, Namespace, Parameter, Type};
 use std::collections::HashMap;
 use std::fmt;
 use std::str;
@@ -331,14 +331,9 @@ pub trait TargetRuntime<'a> {
     fn emit_event<'b>(
         &self,
         bin: &Binary<'b>,
-        contract: &Contract,
         function: FunctionValue<'b>,
-        event_no: usize,
-        data: &[BasicValueEnum<'b>],
-        data_tys: &[Type],
+        data: BasicValueEnum<'b>,
         topics: &[BasicValueEnum<'b>],
-        topic_tys: &[Type],
-        ns: &Namespace,
     );
 
     /// Return ABI encoded data

+ 3 - 8
src/emit/solana/target.rs

@@ -1895,14 +1895,9 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     fn emit_event<'b>(
         &self,
         binary: &Binary<'b>,
-        _contract: &ast::Contract,
         function: FunctionValue<'b>,
-        _event_no: usize,
-        data: &[BasicValueEnum<'b>],
-        _data_tys: &[ast::Type],
+        data: BasicValueEnum<'b>,
         _topics: &[BasicValueEnum<'b>],
-        _topic_tys: &[ast::Type],
-        _ns: &ast::Namespace,
     ) {
         let fields = binary.build_array_alloca(
             function,
@@ -1922,7 +1917,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
             )
         };
 
-        let bytes_pointer = binary.vector_bytes(data[0]);
+        let bytes_pointer = binary.vector_bytes(data);
         binary.builder.build_store(field_data, bytes_pointer);
 
         let field_len = unsafe {
@@ -1939,7 +1934,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         binary.builder.build_store(
             field_len,
             binary.builder.build_int_z_extend(
-                data[1].into_int_value(),
+                binary.vector_len(data),
                 binary.context.i64_type(),
                 "data_len64",
             ),

+ 1 - 16
src/emit/substrate/mod.rs

@@ -828,7 +828,7 @@ impl SubstrateTarget {
                 let address =
                     self.decode_ty(binary, function, &ast::Type::Address(false), data, end, ns);
                 let selector =
-                    self.decode_ty(binary, function, &ast::Type::Uint(32), data, end, ns);
+                    self.decode_ty(binary, function, &ast::Type::Bytes(4), data, end, ns);
 
                 let ty = binary.llvm_type(ty, ns);
 
@@ -1846,21 +1846,6 @@ impl SubstrateTarget {
     }
 }
 
-/// Substrate events should be prefixed with the index of the event in the metadata
-fn event_id<'b>(
-    binary: &Binary<'b>,
-    contract: &ast::Contract,
-    event_no: usize,
-) -> Option<IntValue<'b>> {
-    let event_id = contract
-        .emits_events
-        .iter()
-        .position(|e| *e == event_no)
-        .unwrap();
-
-    Some(binary.context.i8_type().const_int(event_id as u64, false))
-}
-
 /// Print the return code of API calls to the debug buffer.
 fn log_return_code<'b>(binary: &Binary<'b>, api: &'static str, code: IntValue) {
     if !binary.options.log_api_return_codes {

+ 5 - 20
src/emit/substrate/target.rs

@@ -4,7 +4,7 @@ use crate::codegen::cfg::{HashTy, ReturnCode};
 use crate::emit::binary::Binary;
 use crate::emit::expression::expression;
 use crate::emit::storage::StorageSlot;
-use crate::emit::substrate::{event_id, log_return_code, SubstrateTarget, SCRATCH_SIZE};
+use crate::emit::substrate::{log_return_code, SubstrateTarget, SCRATCH_SIZE};
 use crate::emit::{TargetRuntime, Variable};
 use crate::sema::ast;
 use crate::sema::ast::{Function, Namespace, Type};
@@ -1453,14 +1453,9 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn emit_event<'b>(
         &self,
         binary: &Binary<'b>,
-        contract: &ast::Contract,
-        function: FunctionValue<'b>,
-        event_no: usize,
-        data: &[BasicValueEnum<'b>],
-        data_tys: &[ast::Type],
+        _function: FunctionValue<'b>,
+        data: BasicValueEnum<'b>,
         topics: &[BasicValueEnum<'b>],
-        _topic_tys: &[ast::Type],
-        ns: &ast::Namespace,
     ) {
         emit_context!(binary);
 
@@ -1520,23 +1515,13 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             byte_ptr!().const_null()
         };
 
-        let (data_ptr, data_len) = self.abi_encode(
-            binary,
-            event_id(binary, contract, event_no),
-            false,
-            function,
-            data,
-            data_tys,
-            ns,
-        );
-
         call!(
             "seal_deposit_event",
             &[
                 topic_buf.into(),
                 topic_size.into(),
-                data_ptr.into(),
-                data_len.into(),
+                binary.vector_bytes(data).into(),
+                binary.vector_len(data).into(),
             ]
         );
     }

+ 1 - 2
src/sema/ast.rs

@@ -1349,8 +1349,7 @@ impl CodeLocation for Instr {
             Instr::Call { args, .. } => args[0].loc(),
             Instr::Return { value } if value.is_empty() => pt::Loc::Codegen,
             Instr::Return { value } => value[0].loc(),
-            Instr::EmitEvent { data, .. } if data.is_empty() => pt::Loc::Codegen,
-            Instr::EmitEvent { data, .. } => data[0].loc(),
+            Instr::EmitEvent { data, .. } => data.loc(),
             Instr::BranchCond { cond, .. } => cond.loc(),
             Instr::Store { dest, .. } => dest.loc(),
             Instr::SetStorageBytes { storage, .. }

+ 5 - 5
tests/codegen_testcases/solidity/borsh_encoding_complex_types.sol

@@ -13,7 +13,7 @@ contract EncodingTest {
         bytes memory b = abi.encode(non_cte_array);
 
         // CHECK: %temp.7 = load storage slot(uint32 16) ty:string[]
-        // CHECK: ty:uint32 %array_bytes_size_0.temp.8 = uint32 0
+        // CHECK: ty:uint32 %array_bytes_size_0.temp.8 = uint32 4
 	    // CHECK: ty:uint32 %for_i_0.temp.9 = uint32 0
 	    // CHECK: branch block1
 
@@ -61,7 +61,7 @@ contract EncodingTest {
         NonConstantStruct memory cte = NonConstantStruct(1, non_cte_array);
         bytes memory b = abi.encode(cte);
 
-	    // CHECK: ty:uint32 %array_bytes_size_0.temp.15 = uint32 0
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.15 = uint32 4
 	    // CHECK: ty:uint32 %for_i_0.temp.16 = uint32 0
 	    // CHECK: branch block1
 
@@ -77,7 +77,7 @@ contract EncodingTest {
 	    // CHECK: branch block2
 
         // CHECK: block4: # end_for
-	    // CHECK: ty:bytes %abi_encoded.temp.17 = (alloc bytes len (uint32 8 + %array_bytes_size_0.temp.15))
+		// CHECK: ty:bytes %abi_encoded.temp.17 = (alloc bytes len (uint32 8 + %array_bytes_size_0.temp.15))
 	    // CHECK: writebuffer buffer:%abi_encoded.temp.17 offset:uint32 0 value:(load (struct %cte field 0))
 		// CHECK: ty:uint32 %temp.18 = uint32 8
 	    // CHECK: writebuffer buffer:%abi_encoded.temp.17 offset:uint32 8 value:(builtin ArrayLength ((load (struct %cte field 1))))
@@ -111,7 +111,7 @@ contract EncodingTest {
         bytes memory b = abi.encode(complex_array);
 
 	    // CHECK: %temp.21 = load storage slot(uint32 20) ty:struct EncodingTest.NonConstantStruct[]
-	    // CHECK: ty:uint32 %array_bytes_size_0.temp.22 = uint32 0
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.22 = uint32 4
 	    // CHECK: ty:uint32 %for_i_0.temp.23 = uint32 0
 	    // CHECK: branch block1
 
@@ -123,7 +123,7 @@ contract EncodingTest {
 	    // CHECK: branch block1
 
         // CHECK: block3: # body
-	    // CHECK: ty:uint32 %array_bytes_size_0.temp.24 = uint32 0
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.24 = uint32 4
 	    // CHECK: ty:uint32 %for_i_0.temp.25 = uint32 0
 	    // CHECK: branch block5
 

+ 2 - 2
tests/codegen_testcases/solidity/borsh_encoding_simple_types.sol

@@ -155,10 +155,10 @@ contract EncodingTest {
         noPadStruct[2] memory str_vec = [noPadStruct(1,2), noPadStruct(3, 4)];
         bytes memory b1 = abi.encode(test_vec_1, mem_vec, str_vec);
         // CHECK: %temp.66 = load storage slot(uint32 16) ty:struct EncodingTest.noPadStruct[]
-	    // CHECK: ty:uint32 %temp.67 = (((builtin ArrayLength (%temp.66)) * uint32 8) + uint32 4)
+	    // CHECK: ty:uint32 %temp.67 = ((builtin ArrayLength (%temp.66)) * uint32 8)
 	    // CHECK: ty:uint32 %temp.68 = uint32 16
 	    // CHECK: ty:uint32 %temp.69 = uint32 16
-	    // CHECK: ty:bytes %abi_encoded.temp.70 = (alloc bytes len ((%temp.67 + uint32 16) + uint32 16))
+	    // CHECK: ty:bytes %abi_encoded.temp.70 = (alloc bytes len (((%temp.67 + uint32 4) + uint32 16) + uint32 16))
 	    // CHECK: ty:uint32 %temp.71 = (builtin ArrayLength (%temp.66))
 	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:uint32 0 value:%temp.71
 	    // CHECK: memcpy src: %temp.66, dest: (advance ptr: %abi_encoded.temp.70, by: uint32 4), bytes_len: (%temp.71 * uint32 8)

+ 2 - 2
tests/codegen_testcases/solidity/common_subexpression_elimination.sol

@@ -315,13 +315,13 @@ contract c1 {
             emit testEvent(a + get(a/(2*b) -p, b), p, ast+bst);
         }
 
-        // CHECK: branchcond %2.cse_temp, block7, block8
+        // CHECK: branchcond %2.cse_temp, block21, block22
         if (ast == bst) {
             ast = ast + "b";
         }
         // CHECK: call c1::c1::function::get__int256_int256 (%1.cse_temp - %p), (arg #1)
 
-        // CHECK: branchcond (strcmp (%ast) (%bst)), block10, block11
+        // CHECK: branchcond (strcmp (%ast) (%bst)), block24, block25
         while (ast == bst) {
             ast = ast + "a";
         }

+ 57 - 0
tests/codegen_testcases/solidity/scale.sol

@@ -0,0 +1,57 @@
+// RUN: --target substrate --emit cfg
+
+contract ExternalFunctions {
+    function(int32) external returns (uint64) func;
+
+    // BEGIN-CHECK: ExternalFunctions::ExternalFunctions::function::to_storage
+    function to_storage() public {
+        // CHECK: ty:function(int32) external returns (uint64) storage %temp.3 = function(int32) external returns (uint64)(function(int32) external returns (uint64)(struct { hex"42761137", (builtin GetAddress ()) }))
+        // CHECK: store storage slot(uint256 0) ty:function(int32) external returns (uint64) = %temp.3
+        func = this.foo;
+    }
+
+    function foo(int32) public returns (uint64) {
+        return 0xabbaabba;
+    }
+
+    function bar(function(int32) external returns (uint64) f) public {
+        assert(f(102) == 0xabbaabba);
+    }
+
+    // BEGIN-CHECK: ExternalFunctions::ExternalFunctions::function::storage_callback
+    function storage_callback() public {
+        // CHECK: %temp.6 = load storage slot(uint256 0) ty:function(int32) external returns (uint64)
+        // CHECK: ty:bytes %abi_encoded.temp.7 = (alloc bytes len uint32 40)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.7 offset:uint32 0 value:hex"f503f5fe"
+        // CHECK: writebuffer buffer:%abi_encoded.temp.7 offset:uint32 4 value:(load (struct function(int32) external returns (uint64)(%temp.6) field 1))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.7 offset:uint32 36 value:(load (struct function(int32) external returns (uint64)(%temp.6) field 0))
+        // CHECK: _ = external call::regular address:(builtin GetAddress ()) payload:%abi_encoded.temp.7 value:uint128 0 gas:uint64 0 accounts: seeds:
+        this.bar(func);
+    }
+}
+
+contract CompactEncoding {
+    // BEGIN-CHECK: CompactEncoding::CompactEncoding::function::vector_length
+    function vector_length(string memory s) public {
+        // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 1073741824), block6, block7
+        // CHECK: block1: # small
+        // CHECK: ty:uint32 %temp.8 = uint32 1
+        // CHECK: block2: # medium
+        // CHECK: ty:uint32 %temp.8 = uint32 2
+        // CHECK: block4: # big
+        // CHECK: ty:uint32 %temp.8 = uint32 4
+        // CHECK: ty:bytes %abi_encoded.temp.9 = (alloc bytes len (%temp.8 + (builtin ArrayLength ((arg #0)))))
+        // CHECK: ty:uint32 %temp.10 = (builtin ArrayLength ((arg #0)))
+        // CHECK: branchcond (unsigned more %temp.10 > uint32 1073741824), block13, block14
+        // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 64), block3, block1
+        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:(%temp.10 * uint32 4)
+        // CHECK: ty:uint32 %temp.11 = uint32 1
+        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:((%temp.10 * uint32 4) | uint32 1)
+        // CHECK: ty:uint32 %temp.11 = uint32 2
+        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:((%temp.10 * uint32 4) | uint32 2)
+        // CHECK: ty:uint32 %temp.11 = uint32 4
+        // CHECK: memcpy src: (arg #0), dest: (advance ptr: %abi_encoded.temp.9, by: (uint32 0 + %temp.11)), bytes_len: %temp.10
+        // CHECK: branchcond (unsigned more %temp.10 > uint32 64), block10, block8
+        abi.encode(s);
+    }
+}

+ 3 - 3
tests/codegen_testcases/solidity/unused_variable_elimination.sol

@@ -66,7 +66,7 @@ contract c {
         x = 102 + t*y/(t+5*y) + g + test3() - vec.push(2) + ct.sum(1, 2);
 		return 2;
 // CHECK: push array ty:int32[] value:int32 2
-// CHECK: _ = external call::regular address:%ct payload:%temp.75 value:uint128 0 gas:uint64 0 accounts: seeds:
+// CHECK: _ = external call::regular address:%ct payload:%abi_encoded.temp.75 value:uint128 0 gas:uint64 0 accounts: seeds:
 }
 
 }
@@ -77,13 +77,13 @@ contract c3 {
         c2 ct = new c2();
 
         return 3;
-// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %temp.79, buffer len: (builtin ArrayLength (%temp.79)))
+// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.79, buffer len: uint32 4)
     }
 
 // BEGIN-CHECK: c3::function::test7
     function test7() public returns (int32) {
         c2 ct = new c2();
-// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %temp.81, buffer len: (builtin ArrayLength (%temp.81)))
+// constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.81, buffer len: uint32 4)
         address ad = address(ct);
         (bool p, ) = ad.call(hex'ba');
 // CHECK: external call::regular address:%ad payload:(alloc bytes uint32 1 hex"ba") value:uint128 0 gas:uint64 0

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác