Compare commits

...

28 Commits

Author SHA1 Message Date
Matt Witherspoon 69599db279 Merge pull request #388 from heifner/deferred_transaction
Move deferred transaction into deferred_transaction.hpp
2025-08-28 14:41:32 -04:00
kevin Heifner d81cb903ad Removed outdated comment 2025-08-27 09:06:53 -05:00
kevin Heifner 06a3c518fe Move deferred transaction into deferred_transaction.hpp 2025-08-27 08:20:04 -05:00
Matt Witherspoon 7a2c49262f Merge pull request #383 from AntelopeIO/jsoncons_fix_main
[4.1 -> main] fix compile on recent clang versions
2025-07-31 13:06:56 -04:00
Matt Witherspoon 7479f2be35 Merge remote-tracking branch 'origin/release/4.1' into HEAD 2025-07-31 10:40:17 -04:00
Matt Witherspoon acfb0a7cc2 Merge pull request #382 from AntelopeIO/jsoncons_fix_41
[4.1.1] fix compile on recent clang versions
2025-07-31 10:39:43 -04:00
Matt Witherspoon d658c90b6d fix compile on recent clang versions (at least 20 but maybe 18/19 too) 2025-07-30 23:06:24 -04:00
Lin Huang 2ae2708fd8 Merge pull request #379 from AntelopeIO/unhide_cust_entry
Update cdt-llvm to take in customized sync call entry function export fix and add tests for it
2025-07-28 11:14:45 -04:00
Lin Huang 36f2da87c3 Update to use main of cdt-llvm which includes cdt-lld which has customized sync call entry function export fix 2025-07-28 11:02:52 -04:00
Lin Huang fef5731b58 Update to use cdt-llvm unhide_cust_entry branch for missing export of customized sync call entry function fix 2025-07-27 18:29:29 -04:00
Lin Huang 92945e4572 Add tests for customized sync call entry function 2025-07-27 18:23:52 -04:00
Lin Huang c32f4e4f7a Merge pull request #377 from AntelopeIO/update_latest_llvm
[5.0.0-dev2] Attempt 2: Update to use main branches of cdt-clang and cdt-lld
2025-07-25 17:15:21 -04:00
Lin Huang c8535689e2 Update to use latest cdt-llvm (including latest cdt-clang and cdt-lld) 2025-07-25 17:03:11 -04:00
Lin Huang 8b76fa3f7c Merge pull request #373 from AntelopeIO/merge_from_4_1
[4.1 -> main] Merge type_check fix used by action_wrapper
2025-07-24 09:01:27 -04:00
Lin Huang a762ebdbab Resolve merging conflicts 2025-07-23 15:26:40 -04:00
Lin Huang d244ead4d8 Merge remote-tracking branch 'origin/release/4.1' into merge_from_4_1 2025-07-23 15:22:49 -04:00
Lin Huang 4153ba41f7 Merge pull request #372 from AntelopeIO/fix_bool_in_action_wrapper
[4.1.0] Fix action_wrapper failure if its first argument is a bool
2025-07-23 14:51:27 -04:00
Lin Huang 62f04bd94d Merge pull request #369 from AntelopeIO/is_sync_call
[5.0.0-dev] Add `is_sync_call()` to `eosio::contract` class
2025-07-22 11:07:25 -04:00
Lin Huang d607a92712 Revert back the change for allowing private deriving from eosio::contract 2025-07-22 08:12:13 -04:00
Lin Huang 425fc6c3c2 Add a missing () 2025-07-22 07:58:24 -04:00
Lin Huang 00cb315396 Allow a contract derives from eosio::contract privately 2025-07-22 07:46:50 -04:00
Lin Huang a53fc34ce3 Fix action_wrapper fails to compile if it contains bool as the first argument due to ambiguity of partial specializations of is_same<bool,U> and is_same<T,bool> 2025-07-21 17:54:33 -04:00
Lin Huang a294a08ab7 Make separate_cpp_hpp derive publicly from eosio::contract such that set_exec_type() can be used 2025-07-21 15:46:46 -04:00
Lin Huang bad7b81900 Do not use unnecessary and expensivestd::optional for _exec_type 2025-07-21 15:41:39 -04:00
Lin Huang db42ab457f Use set_exec_type() to explicitly set a contract's execution type (action or call) to prevent accidental errors by contract authors 2025-07-21 15:08:17 -04:00
Lin Huang b086797420 Add tests for is_sync_call() 2025-07-18 15:54:51 -04:00
Lin Huang 09b234bf35 Update sync_call_addr_book_callee for new eosio::contract constructor 2025-07-18 15:44:45 -04:00
Lin Huang b9f47e0119 Add is_sync_call() to eosio::contract class 2025-07-18 15:44:00 -04:00
16 changed files with 375 additions and 112 deletions
@@ -29,6 +29,12 @@ namespace eosio {
*/
class contract {
public:
enum class exec_type_t : uint8_t {
action,
call,
unknown
};
/**
* Construct a new contract given the contract name
*
@@ -75,7 +81,27 @@ class contract {
*/
inline const datastream<const char*>& get_datastream()const { return _ds; }
/**
* Whether this contract is for a sync call
*
* @return bool - Whether this contract is for a sync call
*/
inline bool is_sync_call()const {
check(_exec_type != exec_type_t::unknown, "too early to call is_sync_call(). _exec_type has not been set yet");
return (_exec_type == exec_type_t::call);
}
/**
* Set the exectution type.
*
* @param type - The exectution type to be set.
*/
inline void set_exec_type(exec_type_t type) {
_exec_type = type;
}
protected:
/**
* The name of the account this contract is deployed on.
*/
@@ -90,5 +116,10 @@ class contract {
* The datastream for this contract
*/
datastream<const char*> _ds = datastream<const char*>(nullptr, 0);
/**
* The execution type: action or sync call
*/
exec_type_t _exec_type = exec_type_t::unknown;
};
}
@@ -0,0 +1,138 @@
/**
* @file
* @copyright defined in eos/LICENSE
*/
#pragma once
#include "system.hpp"
#include "transaction.hpp"
#include "../../core/eosio/serialize.hpp"
#include <vector>
namespace eosio {
namespace internal_use_do_not_use {
extern "C" {
__attribute__((eosio_wasm_import))
void send_deferred(const uint128_t&, uint64_t, const char*, size_t, uint32_t);
__attribute__((eosio_wasm_import))
int cancel_deferred(const uint128_t&);
}
}
/**
* @defgroup deferred_transaction Transaction
* @ingroup contracts
* @brief Type-safe C++ wrappers for transaction C API
*
* deferred_transaction is no longer supported on Vaulta. This class is provided to
* support legacy test contracts that use deferred transactions.
*
* @details An inline message allows one contract to send another contract a message
* which is processed immediately after the current message's processing
* ends such that the success or failure of the parent transaction is
* dependent on the success of the message. If an inline message fails in
* processing then the whole tree of transactions and actions rooted in the
* block will me marked as failing and none of effects on the database will
* persist.
*
* Inline actions and Deferred transactions must adhere to the permissions
* available to the parent transaction or, in the future, delegated to the
* contract account for future use.
*
* @note There are some methods from the @ref transactioncapi that can be used directly from C++
*/
/**
* Class transaction contains the actions, context_free_actions and extensions type for a transaction
*
* @ingroup transaction
*/
class deferred_transaction : public transaction {
public:
/**
* Construct a new deferred_transaction with an expiration of now + 60 seconds.
*/
deferred_transaction(time_point_sec exp = time_point_sec(current_time_point()) + 60) : transaction( exp ) {}
/**
* Sends this transaction, packs the transaction then sends it as a deferred transaction
*
* @details Writes the symbol_code as a string to the provided char buffer
* @param sender_id - ID of sender
* @param payer - Account paying for RAM
* @param replace_existing - Defaults to false, if this is `0`/false then if the provided sender_id is already in use by an in-flight transaction from this contract, which will be a failing assert. If `1` then transaction will atomically cancel/replace the inflight transaction
*/
void send(const uint128_t& sender_id, name payer, bool replace_existing = false) const {
auto serialize = pack(*static_cast<const transaction*>(this));
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialize.data(), serialize.size(), replace_existing);
}
};
/**
* Struct onerror contains and sender id and packed transaction
*
* @ingroup transaction
*/
struct onerror {
uint128_t sender_id;
std::vector<char> sent_trx;
/**
* from_current_action unpacks and returns a onerror struct
*
* @ingroup transaction
*/
static onerror from_current_action() {
return unpack_action_data<onerror>();
}
/**
* Unpacks and returns a transaction
*/
transaction unpack_sent_trx() const {
return unpack<transaction>(sent_trx);
}
EOSLIB_SERIALIZE( onerror, (sender_id)(sent_trx) )
};
/**
* Send a deferred transaction
*
* @ingroup transaction
* @param sender_id - Account name of the sender of this deferred transaction
* @param payer - Account name responsible for paying the RAM for this deferred transaction
* @param serialized_transaction - The packed transaction to be deferred
* @param size - The size of the packed transaction, required for persistence.
* @param replace - If true, will replace an existing transaction.
*/
inline void send_deferred(const uint128_t& sender_id, name payer, const char* serialized_transaction, size_t size, bool replace = false) {
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialized_transaction, size, replace);
}
/**
* Cancels a deferred transaction.
*
* @ingroup transaction
* @param sender_id - The id of the sender
*
* @pre The deferred transaction ID exists.
* @pre The deferred transaction ID has not yet been published.
* @post Deferred transaction canceled.
*
* @return 1 if transaction was canceled, 0 if transaction was not found
*
* Example:
*
* @code
* id = 0xffffffffffffffff
* cancel_deferred( id );
* @endcode
*/
inline int cancel_deferred(const uint128_t& sender_id) {
return internal_use_do_not_use::cancel_deferred(sender_id);
}
}
@@ -46,6 +46,11 @@ namespace eosio { namespace detail {
template <typename T>
struct is_same<T,bool> { static constexpr bool value = std::is_integral<T>::value; };
// Full specialization to resolve ambiguity introduced by partial specializations
// of is_same<bool,U> and is_same<T,bool>
template <>
struct is_same<bool, bool> { static constexpr bool value = true; };
template <size_t N, size_t I, auto Arg, auto... Args>
struct get_nth_impl { static constexpr auto value = get_nth_impl<N,I+1,Args...>::value; };
@@ -13,12 +13,6 @@
namespace eosio {
namespace internal_use_do_not_use {
extern "C" {
__attribute__((eosio_wasm_import))
void send_deferred(const uint128_t&, uint64_t, const char*, size_t, uint32_t);
__attribute__((eosio_wasm_import))
int cancel_deferred(const uint128_t&);
__attribute__((eosio_wasm_import))
size_t read_transaction(char*, size_t);
@@ -47,17 +41,7 @@ namespace eosio {
* @ingroup contracts
* @brief Type-safe C++ wrappers for transaction C API
*
* @details An inline message allows one contract to send another contract a message
* which is processed immediately after the current message's processing
* ends such that the success or failure of the parent transaction is
* dependent on the success of the message. If an inline message fails in
* processing then the whole tree of transactions and actions rooted in the
* block will me marked as failing and none of effects on the database will
* persist.
*
* Inline actions and Deferred transactions must adhere to the permissions
* available to the parent transaction or, in the future, delegated to the
* contract account for future use.
* @details See action for the ability to send an inline action.
*
* @note There are some methods from the @ref transactioncapi that can be used directly from C++
*/
@@ -83,17 +67,17 @@ namespace eosio {
public:
/**
* Construct a new transaction_header with an expiration of now + 60 seconds.
* Construct a new transaction_header
*
* @brief Construct a new transaction_header object initialising the transaction header expiration to now + 60 seconds
* @brief Construct a new transaction_header object
*/
transaction_header( time_point_sec exp = time_point_sec(current_time_point()) + 60)
transaction_header( time_point_sec exp = time_point_sec{} )
:expiration(exp)
{}
time_point_sec expiration;
uint16_t ref_block_num;
uint32_t ref_block_prefix;
uint16_t ref_block_num = 0UL;
uint32_t ref_block_prefix = 0UL;
unsigned_int max_net_usage_words = 0UL; /// number of 8 byte words this transaction can serialize into after compressions
uint8_t max_cpu_usage_ms = 0UL; /// number of CPU usage units to bill transaction for
unsigned_int delay_sec = 0UL; /// number of seconds to delay transaction, default: 0
@@ -110,22 +94,9 @@ namespace eosio {
public:
/**
* Construct a new transaction with an expiration of now + 60 seconds.
* Construct a new transaction
*/
transaction(time_point_sec exp = time_point_sec(current_time_point()) + 60) : transaction_header( exp ) {}
/**
* Sends this transaction, packs the transaction then sends it as a deferred transaction
*
* @details Writes the symbol_code as a string to the provided char buffer
* @param sender_id - ID of sender
* @param payer - Account paying for RAM
* @param replace_existing - Defaults to false, if this is `0`/false then if the provided sender_id is already in use by an in-flight transaction from this contract, which will be a failing assert. If `1` then transaction will atomically cancel/replace the inflight transaction
*/
void send(const uint128_t& sender_id, name payer, bool replace_existing = false) const {
auto serialize = pack(*this);
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialize.data(), serialize.size(), replace_existing);
}
transaction(time_point_sec exp = time_point_sec{}) : transaction_header( exp ) {}
std::vector<action> context_free_actions;
std::vector<action> actions;
@@ -134,47 +105,6 @@ namespace eosio {
EOSLIB_SERIALIZE_DERIVED( transaction, transaction_header, (context_free_actions)(actions)(transaction_extensions) )
};
/**
* Struct onerror contains and sender id and packed transaction
*
* @ingroup transaction
*/
struct onerror {
uint128_t sender_id;
std::vector<char> sent_trx;
/**
* from_current_action unpacks and returns a onerror struct
*
* @ingroup transaction
*/
static onerror from_current_action() {
return unpack_action_data<onerror>();
}
/**
* Unpacks and returns a transaction
*/
transaction unpack_sent_trx() const {
return unpack<transaction>(sent_trx);
}
EOSLIB_SERIALIZE( onerror, (sender_id)(sent_trx) )
};
/**
* Send a deferred transaction
*
* @ingroup transaction
* @param sender_id - Account name of the sender of this deferred transaction
* @param payer - Account name responsible for paying the RAM for this deferred transaction
* @param serialized_transaction - The packed transaction to be deferred
* @param size - The size of the packed transaction, required for persistence.
* @param replace - If true, will replace an existing transaction.
*/
inline void send_deferred(const uint128_t& sender_id, name payer, const char* serialized_transaction, size_t size, bool replace = false) {
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialized_transaction, size, replace);
}
/**
* Retrieve the indicated action from the active transaction.
*
@@ -204,29 +134,6 @@ namespace eosio {
return internal_use_do_not_use::read_transaction( ptr, sz );
}
/**
* Cancels a deferred transaction.
*
* @ingroup transaction
* @param sender_id - The id of the sender
*
* @pre The deferred transaction ID exists.
* @pre The deferred transaction ID has not yet been published.
* @post Deferred transaction canceled.
*
* @return 1 if transaction was canceled, 0 if transaction was not found
*
* Example:
*
* @code
* id = 0xffffffffffffffff
* cancel_deferred( id );
* @endcode
*/
inline int cancel_deferred(const uint128_t& sender_id) {
return internal_use_do_not_use::cancel_deferred(sender_id);
}
/**
* Gets the size of the currently executing transaction.
*
+41 -3
View File
@@ -140,12 +140,11 @@ BOOST_AUTO_TEST_CASE(mixed_action_call_tags_test) { try {
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hstmulprmtst"_n, "caller"_n, {}));
// Make sure we can push an action using `sum`.
//BOOST_REQUIRE_NO_THROW(t.push_action("callee"_n, "sum"_n, "callee"_n,
t.push_action("callee"_n, "sum"_n, "callee"_n,
BOOST_REQUIRE_NO_THROW(t.push_action("callee"_n, "sum"_n, "callee"_n,
mvo()
("a", 1)
("b", 2)
("c", 3)); //);
("c", 3)));
} FC_LOG_AND_RETHROW() }
// Verify the receiver contract with only one sync call function works
@@ -234,4 +233,43 @@ BOOST_AUTO_TEST_CASE(addr_book_tests) { try {
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "get"_n, "caller"_n, mvo() ("user", "alice")));
} FC_LOG_AND_RETHROW() }
// For a function tagged as both `action` and `call`, verify is_sync_call()
// returns true if tagged as `call` and false if tagged as `action`.
BOOST_AUTO_TEST_CASE(is_sync_call_test) { try {
call_tester t({
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
});
// issynccall() is tagged as both `action` and `call`, and returns
// is_sync_call(). makesynccall() calls issynccall() as a sync call
// and returns its result. So the current action return value must be
// true.
auto trx_trace = t.push_action("caller"_n, "makesynccall"_n, "caller"_n, {});
auto& action_trace = trx_trace->action_traces[0];
bool return_value = fc::raw::unpack<bool>(action_trace.return_value);
BOOST_REQUIRE(return_value == true);
// Call issynccall() directly as an action. Since issynccall()
// returns is_sync_call(), the current action return value must be false.
trx_trace = t.push_action("callee"_n, "issynccall"_n, "callee"_n, {});
auto& action_trace1 = trx_trace->action_traces[0];
return_value = fc::raw::unpack<bool>(action_trace1.return_value);
BOOST_REQUIRE(return_value == false);
} FC_LOG_AND_RETHROW() }
// Verify customized sync call entry function, without sync call tag
BOOST_AUTO_TEST_CASE(customized_call_entry_func_test1) { try {
call_tester t({{"receiver"_n, contracts::cust_entry_wasm(), contracts::cust_entry_abi().data()}});
BOOST_REQUIRE_NO_THROW(t.push_action("receiver"_n, "cusentrytst1"_n, "receiver"_n, {}));
} FC_LOG_AND_RETHROW() }
// Verify customized sync call entry function, with sync call tag
BOOST_AUTO_TEST_CASE(customized_call_entry_func_test2) { try {
call_tester t({{"receiver"_n, contracts::cust_entry_wasm(), contracts::cust_entry_abi().data()}});
BOOST_REQUIRE_NO_THROW(t.push_action("receiver"_n, "cusentrytst2"_n, "receiver"_n, {}));
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()
+2
View File
@@ -54,5 +54,7 @@ namespace eosio::testing {
static std::vector<char> addr_book_callee_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_callee.abi"); }
static std::vector<uint8_t> addr_book_caller_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_caller.wasm"); }
static std::vector<char> addr_book_caller_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_caller.abi"); }
static std::vector<uint8_t> cust_entry_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_cust_entry.wasm"); }
static std::vector<char> cust_entry_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_cust_entry.abi"); }
};
} //ns eosio::testing
@@ -1,6 +1,6 @@
#include <eosio/eosio.hpp>
class [[eosio::contract]] separate_cpp_hpp : eosio::contract {
class [[eosio::contract]] separate_cpp_hpp : public eosio::contract {
public:
using contract::contract;
+1
View File
@@ -19,6 +19,7 @@ add_contract(sync_call_not_supported sync_call_not_supported sync_call_not_suppo
add_contract(sync_call_single_func sync_call_single_func sync_call_single_func.cpp)
add_contract(sync_call_addr_book_callee sync_call_addr_book_callee sync_call_addr_book_callee.cpp)
add_contract(sync_call_addr_book_caller sync_call_addr_book_caller sync_call_addr_book_caller.cpp)
add_contract(sync_call_cust_entry sync_call_cust_entry sync_call_cust_entry.cpp)
add_contract(capi_tests capi_tests capi/capi.c capi/action.c capi/chain.c capi/crypto.c capi/db.c capi/permission.c
capi/print.c capi/privileged.c capi/system.c capi/transaction.c capi/call.c)
+2 -2
View File
@@ -1,5 +1,5 @@
#include <eosio/eosio.hpp>
#include <eosio/transaction.hpp>
#include <eosio/deferred_transaction.hpp>
#include <eosio/bitset.hpp>
#include "transfer.hpp"
@@ -52,7 +52,7 @@ class [[eosio::contract]] simple_tests : public contract {
[[eosio::action]]
void testd(name nm) {
transaction t;
deferred_transaction t;
action act;
act.account = "other"_n;
act.name = "testc"_n;
@@ -31,3 +31,6 @@ struct1_t sync_call_callee::pass_multi_structs(struct1_t s1, int32_t m, struct2_
return { .a = s1.a * m + s2.c, .b = s1.b * m + s2.d };
}
bool sync_call_callee::issynccall() {
return is_sync_call();
}
@@ -38,6 +38,11 @@ public:
[[eosio::call]]
struct1_t pass_multi_structs(struct1_t s1, int32_t m, struct2_t s2);
// return is_sync_call()
[[eosio::action, eosio::call]]
bool issynccall();
using issynccall_func = eosio::call_wrapper<"issynccall"_i, &sync_call_callee::issynccall>;
using return_ten_func = eosio::call_wrapper<"return_ten"_i, &sync_call_callee::return_ten>;
using echo_input_func = eosio::call_wrapper<"echo_input"_i, &sync_call_callee::echo_input>;
using void_func_func = eosio::call_wrapper<"void_func"_i, &sync_call_callee::void_func>;
@@ -172,4 +172,11 @@ public:
status = eosio::call("callee"_n, 0, bad_version_data.data(), bad_version_data.size());
eosio::check(status == -10000, "call did not return -10000 for invalid version");
}
// Call issynccall as a sync call and return its return value
[[eosio::action]]
bool makesynccall() {
sync_call_callee::issynccall_func is_sync_call_func{ "callee"_n };
return is_sync_call_func();
}
};
@@ -0,0 +1,83 @@
/*
* This contract is used to verify customized sync_call entry function works.
* It verifies customized sync_call entry is exported, and `call()` host function
* can reach it.
*/
#include <eosio/eosio.hpp>
#include <eosio/call.hpp>
namespace eosio {
class [[eosio::contract]] sync_call_cust_entry : public contract {
public:
using contract::contract;
[[eosio::action]]
void cusentrytst1() {
// Make a sync call to "receiver"_n account, pass function ID in `data`,
// go to `sync_call()` entry function, and dispatch the call to `get_10()`
// based on the function ID.
const std::vector<char> data{ eosio::pack(1) }; // function ID 1
eosio::call("receiver"_n, 0, data.data(), data.size());
// Retrieve return value and unpack it
std::vector<char> return_value(sizeof(uint32_t));
eosio::get_call_return_value(return_value.data(), return_value.size());
// Verify it
eosio::check(eosio::unpack<uint32_t>(return_value) == 10, "return value is not 10");
}
[[eosio::action]]
void cusentrytst2() {
// Make a sync call to "receiver"_n account, pass function ID in `data`,
// go to `sync_call()` entry function, and dispatch the call to `get_20()`
// based on the function ID.
const std::vector<char> data{ eosio::pack(2) }; // function ID 2
eosio::call("receiver"_n, 0, data.data(), data.size());
// Retrieve return value and unpack it
std::vector<char> return_value(sizeof(uint32_t));
eosio::get_call_return_value(return_value.data(), return_value.size());
// Verify it
eosio::check(eosio::unpack<uint32_t>(return_value) == 20, "return value is not 20");
}
// `eosio::call` tag is optional
uint32_t get_10() {
return 10;
}
[[eosio::call]]
uint32_t get_20() {
return 20;
}
};
} /// namespace eosio
extern "C" {
[[eosio::wasm_entry]]
int64_t sync_call(uint64_t sender, uint64_t receiver, uint32_t data_size) {
eosio::datastream<const char*> ds(nullptr, 0); // for testing, not used
eosio::sync_call_cust_entry obj(eosio::name{receiver}, eosio::name{receiver}, ds);
std::vector<char> data(sizeof(uint32_t));
eosio::get_call_data(data.data(), data.size());
auto func_id = eosio::unpack<uint32_t>(data);
// Dispatch sync calls
uint32_t rv = 0;
switch (func_id) {
case 1: rv = obj.get_10(); break;
case 2: rv = obj.get_20(); break;
default: eosio::check(false, "wrong function ID");
}
// set return value
eosio::set_call_return_value(&rv, sizeof(uint32_t));
return 0; // return 0 to indicate success
}
}
+47 -2
View File
@@ -146,11 +146,28 @@ namespace eosio { namespace cdt {
return "";
}
// Return `true` if the method `decl`'s base class if `eosio::contract`
bool base_is_eosio_contract_class(const clang::CXXMethodDecl* decl) {
auto cxx_decl = decl->getParent();
// on this point it could be just an attribute so let's check base classes
for (const auto& base : cxx_decl->bases()) {
if (const clang::Type *base_type = base.getType().getTypePtrOrNull()) {
if (const auto* cur_cxx_decl = base_type->getAsCXXRecordDecl()) {
if (cur_cxx_decl->getQualifiedNameAsString() == "eosio::contract") {
return true;;
}
}
}
}
return false;
}
template <typename F>
void create_dispatch(const std::string& attr, const std::string& func_name, F&& get_str, CXXMethodDecl* decl) {
constexpr static uint32_t max_stack_size = 512;
codegen& cg = codegen::get();
std::string nm = decl->getNameAsString()+"_"+decl->getParent()->getNameAsString();
if (cg.is_eosio_contract(decl, cg.contract_name)) {
ss << "\n\n#include <eosio/datastream.hpp>\n";
ss << "#include <eosio/name.hpp>\n";
@@ -190,8 +207,22 @@ namespace eosio { namespace cdt {
ss << tn << " arg" << i << "; ds >> arg" << i << ";\n";
i++;
}
// Create contract object
ss << decl->getParent()->getQualifiedNameAsString()
<< " obj {eosio::name{r},eosio::name{c},ds};\n";
// Call `set_exec_type()` only for contracts dervied from `eosio::contract`.
// A contract class can have only `eosio::contract` attribute
// but does not inherit from the `eosio::contract` class;
// it may not have `set_exec_type()`. We need to make sure the class derives
// from `eosio::contract` before calling set_exec_type().
if (base_is_eosio_contract_class(decl)) {
ss << "obj.set_exec_type(eosio::contract::exec_type_t::action);\n";
}
const auto& call_action = [&]() {
ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{r},eosio::name{c},ds}." << decl->getNameAsString() << "(";
ss << "obj." << decl->getNameAsString() << "(";
for (int i=0; i < decl->parameters().size(); i++) {
ss << "arg" << i;
if (i < decl->parameters().size()-1)
@@ -260,8 +291,22 @@ namespace eosio { namespace cdt {
ss << tn << " arg" << i << "; ds >> arg" << i << ";\n";
i++;
}
// Create contract object
ss << decl->getParent()->getQualifiedNameAsString()
<< " obj {eosio::name{receiver},eosio::name{receiver},ds};\n";
// Call `set_exec_type()` only for contracts dervied from `eosio::contract`.
// A contract class can have only `eosio::contract` attribute
// but does not inherit from the `eosio::contract` class;
// it may not have `set_exec_type()`. We need to make sure the class derives
// from `eosio::contract` before calling set_exec_type().
if (base_is_eosio_contract_class(decl)) {
ss << "obj.set_exec_type(eosio::contract::exec_type_t::call);\n";
}
const auto& call_function = [&]() {
ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{receiver},eosio::name{receiver},ds}." << decl->getNameAsString() << "(";
ss << "obj." << decl->getNameAsString() << "(";
for (int i=0; i < decl->parameters().size(); i++) {
ss << "arg" << i;
if (i < decl->parameters().size()-1)
@@ -170,7 +170,6 @@ public:
{
using std::swap;
swap(lhs.it_,rhs.it_);
swap(lhs.empty_,rhs.empty_);
}
private:
@@ -248,7 +247,6 @@ public:
{
using std::swap;
swap(lhs.it_,rhs.it_);
swap(lhs.empty_,rhs.empty_);
}
private: