Browse Source

get rid of dispatch_no

Signed-off-by: xermicus <cyrill@parity.io>
xermicus 2 years ago
parent
commit
b75799bb23

+ 1 - 1
src/codegen/dispatch/mod.rs

@@ -4,7 +4,7 @@ use super::{cfg::ControlFlowGraph, Options};
 use crate::{sema::ast::Namespace, Target};
 use crate::{sema::ast::Namespace, Target};
 
 
 pub(super) mod solana;
 pub(super) mod solana;
-pub(super) mod substrate;
+pub(crate) mod substrate;
 
 
 pub(super) fn function_dispatch(
 pub(super) fn function_dispatch(
     contract_no: usize,
     contract_no: usize,

+ 3 - 1
src/codegen/dispatch/solana.rs

@@ -13,6 +13,8 @@ use solang_parser::{pt, pt::Loc};
 
 
 use crate::codegen::encoding::{abi_decode, abi_encode};
 use crate::codegen::encoding::{abi_decode, abi_encode};
 
 
+pub const SOLANA_DISPATCH_CFG_NAME: &str = "solang_dispatch";
+
 /// Create the dispatch for the Solana target
 /// Create the dispatch for the Solana target
 pub(crate) fn function_dispatch(
 pub(crate) fn function_dispatch(
     contract_no: usize,
     contract_no: usize,
@@ -21,7 +23,7 @@ pub(crate) fn function_dispatch(
     opt: &Options,
     opt: &Options,
 ) -> ControlFlowGraph {
 ) -> ControlFlowGraph {
     let mut vartab = Vartable::new(ns.next_id);
     let mut vartab = Vartable::new(ns.next_id);
-    let mut cfg = ControlFlowGraph::new("solang_dispatch".into(), ASTFunction::None);
+    let mut cfg = ControlFlowGraph::new(SOLANA_DISPATCH_CFG_NAME.into(), ASTFunction::None);
 
 
     let switch_block = cfg.new_basic_block("switch".to_string());
     let switch_block = cfg.new_basic_block("switch".to_string());
     let no_function_matched = cfg.new_basic_block("no_function_matched".to_string());
     let no_function_matched = cfg.new_basic_block("no_function_matched".to_string());

+ 35 - 6
src/codegen/dispatch/substrate.rs

@@ -13,6 +13,40 @@ use crate::{
 use num_bigint::{BigInt, Sign};
 use num_bigint::{BigInt, Sign};
 use solang_parser::pt::{FunctionTy, Loc::Codegen};
 use solang_parser::pt::{FunctionTy, Loc::Codegen};
 
 
+/// On Substrate, contracts export  a `call` and a `deploy` function.
+/// The `contracts` pallet will invoke `deploy` on contract instatiation,
+/// and `call` on any contract calls after the instantiation.
+///
+/// On Ethereum, constructors do not exist on-chain; they are only executed once.
+/// To cope with that model, we emit different code for the dispatcher,
+/// depending on the exported function:
+/// * On `deploy`, match only on constructor selectors
+/// * On `call`, match only on selectors of externally callable functions
+pub enum DispatchType {
+    Deploy,
+    Call,
+}
+
+impl ToString for DispatchType {
+    fn to_string(&self) -> String {
+        match self {
+            Self::Deploy => "substrate_deploy_dispatch",
+            Self::Call => "substrate_call_dispatch",
+        }
+        .into()
+    }
+}
+
+impl From<FunctionTy> for DispatchType {
+    fn from(value: FunctionTy) -> Self {
+        match value {
+            FunctionTy::Constructor => Self::Deploy,
+            FunctionTy::Function => Self::Call,
+            _ => unreachable!("only constructors and functions have corresponding dispatch types"),
+        }
+    }
+}
+
 /// The dispatch algorithm consists of these steps:
 /// The dispatch algorithm consists of these steps:
 /// 1. If the input is less than the expected selector length (default 4 bytes), fallback or receive.
 /// 1. If the input is less than the expected selector length (default 4 bytes), fallback or receive.
 /// 2. Match the function selector
 /// 2. Match the function selector
@@ -50,12 +84,7 @@ struct Dispatch<'a> {
 }
 }
 
 
 fn new_cfg(ns: &Namespace, ty: FunctionTy) -> ControlFlowGraph {
 fn new_cfg(ns: &Namespace, ty: FunctionTy) -> ControlFlowGraph {
-    let name = match ty {
-        FunctionTy::Constructor => "substrate_deploy_dispatch",
-        FunctionTy::Function => "substrate_call_dispatch",
-        _ => unreachable!(),
-    };
-    let mut cfg = ControlFlowGraph::new(name.into(), ASTFunction::None);
+    let mut cfg = ControlFlowGraph::new(DispatchType::from(ty).to_string(), ASTFunction::None);
     let input_ptr = Parameter {
     let input_ptr = Parameter {
         loc: Codegen,
         loc: Codegen,
         id: None,
         id: None,

+ 1 - 2
src/codegen/mod.rs

@@ -5,7 +5,7 @@ pub mod cfg;
 mod constant_folding;
 mod constant_folding;
 mod constructor;
 mod constructor;
 mod dead_storage;
 mod dead_storage;
-mod dispatch;
+pub(crate) mod dispatch;
 mod encoding;
 mod encoding;
 mod events;
 mod events;
 mod expression;
 mod expression;
@@ -243,7 +243,6 @@ fn contract(contract_no: usize, ns: &mut Namespace, opt: &Options) {
         }
         }
 
 
         for dispatch_cfg in function_dispatch(contract_no, &all_cfg, ns, opt) {
         for dispatch_cfg in function_dispatch(contract_no, &all_cfg, ns, opt) {
-            ns.contracts[contract_no].dispatch_no = all_cfg.len();
             all_cfg.push(dispatch_cfg);
             all_cfg.push(dispatch_cfg);
         }
         }
 
 

+ 7 - 6
src/codegen/solana_accounts/account_management.rs

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::dispatch::solana::SOLANA_DISPATCH_CFG_NAME;
 use crate::codegen::{Builtin, Expression};
 use crate::codegen::{Builtin, Expression};
 use crate::sema::ast::{ArrayLength, Function, Namespace, StructType, Type};
 use crate::sema::ast::{ArrayLength, Function, Namespace, StructType, Type};
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::solana_accounts::BuiltinAccounts;
@@ -32,12 +33,12 @@ pub(crate) fn manage_contract_accounts(contract_no: usize, ns: &mut Namespace) {
     }
     }
 
 
     if let Some(constructor) = constructor_no {
     if let Some(constructor) = constructor_no {
-        let dispatch = ns.contracts[contract_no].dispatch_no;
-        traverse_cfg(
-            &mut ns.contracts[contract_no].cfg[dispatch],
-            &ns.functions,
-            constructor,
-        );
+        let dispatch = ns.contracts[contract_no]
+            .cfg
+            .iter_mut()
+            .find(|cfg| cfg.name == SOLANA_DISPATCH_CFG_NAME)
+            .expect("dispatch CFG is always generated");
+        traverse_cfg(dispatch, &ns.functions, constructor);
     }
     }
 }
 }
 
 

+ 0 - 2
src/codegen/yul/tests/expression.rs

@@ -156,7 +156,6 @@ fn contract_constant_variable() {
         cfg: vec![],
         cfg: vec![],
         code: OnceCell::new(),
         code: OnceCell::new(),
         instantiable: true,
         instantiable: true,
-        dispatch_no: 0,
         program_id: None,
         program_id: None,
     };
     };
     ns.contracts.push(contract);
     ns.contracts.push(contract);
@@ -292,7 +291,6 @@ fn slot_suffix() {
         cfg: vec![],
         cfg: vec![],
         code: OnceCell::new(),
         code: OnceCell::new(),
         instantiable: true,
         instantiable: true,
-        dispatch_no: 0,
         program_id: None,
         program_id: None,
     };
     };
     ns.contracts.push(contract);
     ns.contracts.push(contract);

+ 9 - 5
src/emit/substrate/mod.rs

@@ -7,6 +7,7 @@ use inkwell::module::{Linkage, Module};
 use inkwell::values::{BasicMetadataValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::values::{BasicMetadataValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
 use inkwell::AddressSpace;
 
 
+use crate::codegen::dispatch::substrate::DispatchType;
 use crate::emit::functions::{emit_functions, emit_initializer};
 use crate::emit::functions::{emit_functions, emit_initializer};
 use crate::emit::{Binary, TargetRuntime};
 use crate::emit::{Binary, TargetRuntime};
 
 
@@ -330,17 +331,20 @@ impl SubstrateTarget {
         let export_name = if init.is_some() { "deploy" } else { "call" };
         let export_name = if init.is_some() { "deploy" } else { "call" };
         let func = bin.module.add_function(export_name, ty, None);
         let func = bin.module.add_function(export_name, ty, None);
         let (input, input_length) = self.public_function_prelude(bin, func);
         let (input, input_length) = self.public_function_prelude(bin, func);
-        let dispatch_cfg_name = &format!("substrate_{}_dispatch", export_name);
         let args = vec![
         let args = vec![
             BasicMetadataValueEnum::PointerValue(input),
             BasicMetadataValueEnum::PointerValue(input),
             BasicMetadataValueEnum::IntValue(input_length),
             BasicMetadataValueEnum::IntValue(input_length),
             BasicMetadataValueEnum::IntValue(self.value_transferred(bin, ns)),
             BasicMetadataValueEnum::IntValue(self.value_transferred(bin, ns)),
             BasicMetadataValueEnum::PointerValue(bin.selector.as_pointer_value()),
             BasicMetadataValueEnum::PointerValue(bin.selector.as_pointer_value()),
         ];
         ];
-        // Call the storage initializers on deploy
-        if let Some(initializer) = init {
-            bin.builder.build_call(initializer, &[], "");
-        }
+        let dispatch_cfg_name = &init
+            .map(|initializer| {
+                // Call the storage initializers on deploy
+                bin.builder.build_call(initializer, &[], "");
+                DispatchType::Deploy
+            })
+            .unwrap_or(DispatchType::Call)
+            .to_string();
         let cfg = bin.module.get_function(dispatch_cfg_name).unwrap();
         let cfg = bin.module.get_function(dispatch_cfg_name).unwrap();
         bin.builder.build_call(cfg, &args, dispatch_cfg_name);
         bin.builder.build_call(cfg, &args, dispatch_cfg_name);
 
 

+ 0 - 2
src/sema/ast.rs

@@ -749,8 +749,6 @@ pub struct Contract {
     pub code: OnceCell<Vec<u8>>,
     pub code: OnceCell<Vec<u8>>,
     /// Can the contract be instantiated, i.e. not abstract, no errors, etc.
     /// Can the contract be instantiated, i.e. not abstract, no errors, etc.
     pub instantiable: bool,
     pub instantiable: bool,
-    /// CFG number of this contract's dispatch function
-    pub dispatch_no: usize,
     /// Account of deployed program code on Solana
     /// Account of deployed program code on Solana
     pub program_id: Option<Vec<u8>>,
     pub program_id: Option<Vec<u8>>,
 }
 }

+ 0 - 1
src/sema/contracts.rs

@@ -46,7 +46,6 @@ impl ast::Contract {
             cfg: Vec::new(),
             cfg: Vec::new(),
             code: OnceCell::new(),
             code: OnceCell::new(),
             instantiable,
             instantiable,
-            dispatch_no: 0,
             program_id: None,
             program_id: None,
         }
         }
     }
     }