From 0d9a056c546b9cb723bf6865904c18318122d0cb Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 11 Feb 2020 15:24:02 -0500 Subject: [PATCH 1/7] support for action results --- libraries/eosiolib/capi/eosio/action.h | 2 +- libraries/eosiolib/contracts/eosio/action.hpp | 35 +- libraries/native/intrinsics.cpp | 2 +- modules/TestsExternalProject.txt | 2 +- tests/integration/CMakeLists.txt | 4 +- tests/integration/action_results_test.cpp | 67 ++ tests/integration/contracts.hpp.in | 2 + .../abigen-pass/action_results_test.abi | 52 ++ .../abigen-pass/action_results_test.cpp | 17 + .../abigen-pass/action_results_test.json | 9 + .../aliased_type_variant_template_arg.abi | 38 ++ .../aliased_type_variant_template_arg.json | 3 +- .../abigen-pass/struct_base_typedefd.abi | 48 ++ .../abigen-pass/struct_base_typedefd.json | 2 +- tests/unit/test_contracts/CMakeLists.txt | 1 + .../test_contracts/action_results_test.cpp | 17 + tests/unit/test_contracts/capi/action.c | 2 +- tools/CMakeLists.txt | 5 +- tools/abidiff/eosio-abidiff.cpp.in | 27 + tools/abigen/CMakeLists.txt | 3 - tools/abigen/eosio-abigen.cpp.in | 616 ------------------ tools/cc/eosio-cpp.cpp.in | 9 +- tools/include/compiler_options.hpp.in | 20 +- tools/include/eosio/abi.hpp | 15 +- tools/include/eosio/abigen.hpp | 39 +- tools/include/eosio/abimerge.hpp | 27 +- tools/include/eosio/codegen.hpp | 57 +- tools/toolchain-tester/tests.py | 8 +- 28 files changed, 412 insertions(+), 717 deletions(-) create mode 100644 tests/integration/action_results_test.cpp create mode 100644 tests/toolchain/abigen-pass/action_results_test.abi create mode 100644 tests/toolchain/abigen-pass/action_results_test.cpp create mode 100644 tests/toolchain/abigen-pass/action_results_test.json create mode 100644 tests/toolchain/abigen-pass/aliased_type_variant_template_arg.abi create mode 100644 tests/toolchain/abigen-pass/struct_base_typedefd.abi create mode 100644 tests/unit/test_contracts/action_results_test.cpp delete mode 100644 tools/abigen/CMakeLists.txt delete mode 100644 tools/abigen/eosio-abigen.cpp.in diff --git a/libraries/eosiolib/capi/eosio/action.h b/libraries/eosiolib/capi/eosio/action.h index 741c268c95..58d345848a 100644 --- a/libraries/eosiolib/capi/eosio/action.h +++ b/libraries/eosiolib/capi/eosio/action.h @@ -176,7 +176,7 @@ capi_name current_receiver( void ); * @pre `return_value` is a valid pointer to an array at least `size` bytes long */ __attribute__((eosio_wasm_import)) -void set_action_return_value(char *return_value, size_t size); +void set_action_return_value(void *return_value, size_t size); #ifdef __cplusplus } diff --git a/libraries/eosiolib/contracts/eosio/action.hpp b/libraries/eosiolib/contracts/eosio/action.hpp index 44d81e233d..195c85f842 100644 --- a/libraries/eosiolib/contracts/eosio/action.hpp +++ b/libraries/eosiolib/contracts/eosio/action.hpp @@ -52,9 +52,6 @@ namespace eosio { __attribute__((eosio_wasm_import)) uint64_t current_receiver(); - - __attribute__((eosio_wasm_import)) - void set_action_return_value(char *return_value, size_t size); } }; @@ -153,18 +150,6 @@ namespace eosio { return name{internal_use_do_not_use::current_receiver()}; } - /** - * Set the action return value which will be included in action_receipt - * @ingroup action - * @tparam T type of return value - * @param v the return value to set - */ - template - inline void set_action_return_value( const T& v ) { - auto packed_value = pack( v ); - internal_use_do_not_use::set_action_return_value( &packed_value[0], packed_value.size() ); - } - /** * Copy up to length bytes of current action data to the specified location * @@ -448,9 +433,9 @@ namespace eosio { } /** - * Wrapper for an action object. + * Wrapper for an action object. * - * @brief Used to wrap an a particular action to simplify the process of other contracts sending inline actions to "wrapped" action. + * @brief Used to wrap an a particular action to simplify the process of other contracts sending inline actions to "wrapped" action. * Example: * @code * // defined by contract writer of the actions @@ -591,7 +576,7 @@ INLINE_ACTION_SENDER3( CONTRACT_CLASS, NAME, ::eosio::name(#NAME) ) * Send an inline-action from inside a contract. * * @brief A macro to simplify calling inline actions - * @details The send inline action macro is intended to simplify the process of calling inline actions. When calling new actions from existing actions + * @details The send inline action macro is intended to simplify the process of calling inline actions. When calling new actions from existing actions * EOSIO supports two communication models, inline and deferred. Inline actions are executed as part of the current transaction. This macro * creates an @ref action using the supplied parameters and automatically calls action.send() on this newly created action. * @@ -599,15 +584,15 @@ INLINE_ACTION_SENDER3( CONTRACT_CLASS, NAME, ::eosio::name(#NAME) ) * @code * SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} ); * @endcode - * - * The example above is taken from eosio.token. - * This example: - * uses the passed in, dereferenced `this` pointer, to call this.get_self() i.e. the eosio.token contract; - * calls the eosio.token::transfer() action; + * + * The example above is taken from eosio.token. + * This example: + * uses the passed in, dereferenced `this` pointer, to call this.get_self() i.e. the eosio.token contract; + * calls the eosio.token::transfer() action; * uses the active permission of the "issuer" account; - * uses parameters st.issuer, to, quantity and memo. + * uses parameters st.issuer, to, quantity and memo. * This macro creates an action struct used to 'send()' (call) transfer(account_name from, account_name to, asset quantity, string memo) - * + * * @param CONTRACT - The contract to call, which contains the action being sent, maps to the @ref account * @param NAME - The name of the action to be called, maps to a @ref name * @param ... - The authorising permission, maps to an @ref authorization , followed by the parameters of the action, maps to a @ref data. diff --git a/libraries/native/intrinsics.cpp b/libraries/native/intrinsics.cpp index d70bf28a8c..8cf32e96a0 100644 --- a/libraries/native/intrinsics.cpp +++ b/libraries/native/intrinsics.cpp @@ -292,7 +292,7 @@ extern "C" { capi_name current_receiver() { return intrinsics::get().call(); } - void set_action_return_value( char* rv, size_t len ) { + void set_action_return_value( void* rv, size_t len ) { intrinsics::get().call(rv, len); } void require_recipient( capi_name name ) { diff --git a/modules/TestsExternalProject.txt b/modules/TestsExternalProject.txt index c33d1b6e8f..9efdf7906e 100644 --- a/modules/TestsExternalProject.txt +++ b/modules/TestsExternalProject.txt @@ -6,7 +6,7 @@ ExternalProject_Add( EosioWasmTests SOURCE_DIR "${CMAKE_SOURCE_DIR}/tests/unit" BINARY_DIR "${CMAKE_BINARY_DIR}/tests/unit" - CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_BINARY_DIR}/lib/cmake/eosio.cdt/EosioWasmToolchain.cmake -DCMAKE_BUILD_TYPE=Debug -DEOSIO_CDT_BIN=${CMAKE_BINARY_DIR}/lib/cmake/eosio.cdt/ -DBASE_BINARY_DIR=${CMAKE_BINARY_DIR} -D__APPLE=${APPLE} + CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_BINARY_DIR}/lib/cmake/eosio.cdt/EosioWasmToolchain.cmake -DCMAKE_BUILD_TYPE=Debug -DEOSIO_CDT_BIN=${CMAKE_BINARY_DIR}/lib/cmake/eosio.cdt/ -DBASE_BINARY_DIR=${CMAKE_BINARY_DIR} -D__APPLE=${APPLE} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} UPDATE_COMMAND "" PATCH_COMMAND "" TEST_COMMAND "" diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 816b4ad62b..018172d145 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required( VERSION 3.5 ) -set(EOSIO_VERSION_MIN "1.4") -set(EOSIO_VERSION_SOFT_MAX "2.0") +set(EOSIO_VERSION_MIN "2.0") +set(EOSIO_VERSION_SOFT_MAX "3.0") #set(EOSIO_VERSION_HARD_MAX "") find_package(eosio) diff --git a/tests/integration/action_results_test.cpp b/tests/integration/action_results_test.cpp new file mode 100644 index 0000000000..4886b9bbda --- /dev/null +++ b/tests/integration/action_results_test.cpp @@ -0,0 +1,67 @@ +#include +#include +#include + +#include + +#include + +#include + +using namespace eosio; +using namespace eosio::testing; +using namespace eosio::chain; +using namespace eosio::testing; +using namespace fc; + +using mvo = fc::mutable_variant_object; + +BOOST_AUTO_TEST_SUITE(action_results_tests_suite) + +BOOST_FIXTURE_TEST_CASE( action_results_tests, tester ) try { + create_accounts( { N(test) } ); + produce_block(); + + const auto& pfm = control->get_protocol_feature_manager(); + + set_code( N(test), contracts::action_results_test_wasm() ); + set_abi( N(test), contracts::action_results_test_abi().data() ); + auto d = pfm.get_builtin_digest(builtin_protocol_feature_t::action_return_value) + schedule_protocol_features( {*d} ); + + produce_blocks(); + const auto& trace = push_action(N(test), N(action1), N(test), mvo()); + + /* + BOOST_CHECK_THROW(push_action(N(test), N(test1), N(test), mvo()("nm", "notbucky")), + fc::exception); + + push_action(N(test), N(test2), N(test), + mvo() + ("arg0", 33) + ("arg1", "some string")); + BOOST_CHECK_THROW(push_action(N(test), N(test2), N(test), mvo() ("arg0", 30)("arg1", "some string")), fc::exception); + BOOST_CHECK_THROW(push_action(N(test), N(test2), N(test), mvo() ("arg0", 33)("arg1", "not some string")), fc::exception); + + set_abi( N(test), contracts::simple_wrong_abi().data() ); + produce_blocks(); + + BOOST_CHECK_THROW(push_action(N(test), N(test3), N(test), mvo() ("arg0", 33) ("arg1", "some string")), fc::exception); + + set_abi( N(test), contracts::simple_abi().data() ); + produce_blocks(); + + push_action(N(test), N(test4), N(test), mvo() ("to", "someone")); + push_action(N(test), N(test5), N(test), mvo() ("to", "someone")); + push_action(N(test), N(testa), N(test), mvo() ("to", "someone")); + BOOST_CHECK_THROW(push_action(N(test), N(testb), N(test), mvo() ("to", "someone")), fc::exception); + + // test that the pre_dispatch will short circuit dispatching if false + push_action(N(test), N(testc), N(test), mvo() ("nm", "bucky")); + BOOST_CHECK_THROW(push_action(N(test), N(testc), N(test), mvo() ("nm", "someone")), fc::exception); + push_action(N(test), N(testc), N(test), mvo() ("nm", "quit")); + */ + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/integration/contracts.hpp.in b/tests/integration/contracts.hpp.in index 8d0f140bbc..afd1ffff67 100644 --- a/tests/integration/contracts.hpp.in +++ b/tests/integration/contracts.hpp.in @@ -19,6 +19,8 @@ struct contracts { static std::vector capi_tests_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/capi_tests.wasm"); } static std::vector capi_tests_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/capi_tests.abi"); } + static std::vector action_results_test_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/action_results_test.wasm"); } + static std::vector action_results_test_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/action_results_test.abi"); } }; }} //ns eosio::testing diff --git a/tests/toolchain/abigen-pass/action_results_test.abi b/tests/toolchain/abigen-pass/action_results_test.abi new file mode 100644 index 0000000000..b9ae26e007 --- /dev/null +++ b/tests/toolchain/abigen-pass/action_results_test.abi @@ -0,0 +1,52 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [], + "structs": [ + { + "name": "action1", + "base": "", + "fields": [] + }, + { + "name": "action2", + "base": "", + "fields": [] + }, + { + "name": "action3", + "base": "", + "fields": [] + } + ], + "actions": [ + { + "name": "action1", + "type": "action1", + "ricardian_contract": "" + }, + { + "name": "action2", + "type": "action2", + "ricardian_contract": "" + }, + { + "name": "action3", + "type": "action3", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [], + "action_results": [ + { + "name": "action2", + "result_type": "uint32" + }, + { + "name": "action3", + "result_type": "string" + } + ] +} \ No newline at end of file diff --git a/tests/toolchain/abigen-pass/action_results_test.cpp b/tests/toolchain/abigen-pass/action_results_test.cpp new file mode 100644 index 0000000000..0d09d79a4d --- /dev/null +++ b/tests/toolchain/abigen-pass/action_results_test.cpp @@ -0,0 +1,17 @@ +#include + +using namespace eosio; + +class [[eosio::contract]] action_results_test : public contract { + public: + using contract::contract; + + [[eosio::action]] + void action1() {} + + [[eosio::action]] + uint32_t action2() { return 42; } + + [[eosio::action]] + std::string action3() { return "foo"; } +}; diff --git a/tests/toolchain/abigen-pass/action_results_test.json b/tests/toolchain/abigen-pass/action_results_test.json new file mode 100644 index 0000000000..cbe7d91c39 --- /dev/null +++ b/tests/toolchain/abigen-pass/action_results_test.json @@ -0,0 +1,9 @@ +{ + "tests" : [ + { + "expected" : { + "abi-file" : "action_results_test.abi" + } + } + ] +} diff --git a/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.abi b/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.abi new file mode 100644 index 0000000000..7720526915 --- /dev/null +++ b/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.abi @@ -0,0 +1,38 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [ + { + "new_type_name": "str", + "type": "string" + } + ], + "structs": [ + { + "name": "hi", + "base": "", + "fields": [ + { + "name": "v", + "type": "variant_uint64_str" + } + ] + } + ], + "actions": [ + { + "name": "hi", + "type": "hi", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [ + { + "name": "variant_uint64_str", + "types": ["uint64","str"] + } + ], + "action_results": [] +} \ No newline at end of file diff --git a/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.json b/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.json index 3a5f5d8150..9a34cb3238 100644 --- a/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.json +++ b/tests/toolchain/abigen-pass/aliased_type_variant_template_arg.json @@ -2,9 +2,8 @@ "tests": [ { "expected": { - "abi": "{\n \"____comment\": \"This file was generated with eosio-abigen. DO NOT EDIT \",\n \"version\": \"eosio::abi\/1.1\",\n \"types\": [\n {\n \"new_type_name\": \"str\",\n \"type\": \"string\"\n }\n ],\n \"structs\": [\n {\n \"name\": \"hi\",\n \"base\": \"\",\n \"fields\": [\n {\n \"name\": \"v\",\n \"type\": \"variant_uint64_str\"\n }\n ]\n }\n ],\n \"actions\": [\n {\n \"name\": \"hi\",\n \"type\": \"hi\",\n \"ricardian_contract\": \"\"\n }\n ],\n \"tables\": [],\n \"ricardian_clauses\": [],\n \"variants\": [\n {\n \"name\": \"variant_uint64_str\",\n \"types\": [\"uint64\",\"str\"]\n }\n ]\n}" + "abi-file": "aliased_type_variant_template_arg.abi" } } ] } - diff --git a/tests/toolchain/abigen-pass/struct_base_typedefd.abi b/tests/toolchain/abigen-pass/struct_base_typedefd.abi new file mode 100644 index 0000000000..d24aa61cbb --- /dev/null +++ b/tests/toolchain/abigen-pass/struct_base_typedefd.abi @@ -0,0 +1,48 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [ + { + "new_type_name": "bar", + "type": "foo" + } + ], + "structs": [ + { + "name": "baz", + "base": "bar", + "fields": [] + }, + { + "name": "foo", + "base": "", + "fields": [ + { + "name": "value", + "type": "int32" + } + ] + }, + { + "name": "hi", + "base": "", + "fields": [ + { + "name": "b", + "type": "baz" + } + ] + } + ], + "actions": [ + { + "name": "hi", + "type": "hi", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [], + "action_results": [] +} \ No newline at end of file diff --git a/tests/toolchain/abigen-pass/struct_base_typedefd.json b/tests/toolchain/abigen-pass/struct_base_typedefd.json index 0f56342de1..ba945e78dc 100644 --- a/tests/toolchain/abigen-pass/struct_base_typedefd.json +++ b/tests/toolchain/abigen-pass/struct_base_typedefd.json @@ -2,7 +2,7 @@ "tests": [ { "expected": { - "abi": "{\n \"____comment\": \"This file was generated with eosio-abigen. DO NOT EDIT \",\n \"version\": \"eosio::abi\/1.1\",\n \"types\": [\n {\n \"new_type_name\": \"bar\",\n \"type\": \"foo\"\n }\n ],\n \"structs\": [\n {\n \"name\": \"baz\",\n \"base\": \"bar\",\n \"fields\": []\n },\n {\n \"name\": \"foo\",\n \"base\": \"\",\n \"fields\": [\n {\n \"name\": \"value\",\n \"type\": \"int32\"\n }\n ]\n },\n {\n \"name\": \"hi\",\n \"base\": \"\",\n \"fields\": [\n {\n \"name\": \"b\",\n \"type\": \"baz\"\n }\n ]\n }\n ],\n \"actions\": [\n {\n \"name\": \"hi\",\n \"type\": \"hi\",\n \"ricardian_contract\": \"\"\n }\n ],\n \"tables\": [],\n \"ricardian_clauses\": [],\n \"variants\": []\n}" + "abi-file": "struct_base_typedefd.abi" } } ] diff --git a/tests/unit/test_contracts/CMakeLists.txt b/tests/unit/test_contracts/CMakeLists.txt index d5582ac3d0..ea206f4d3f 100644 --- a/tests/unit/test_contracts/CMakeLists.txt +++ b/tests/unit/test_contracts/CMakeLists.txt @@ -1,3 +1,4 @@ +add_contract(action_results_test action_results_test action_results_test.cpp) add_contract(malloc_tests malloc_tests malloc_tests.cpp) add_contract(malloc_tests old_malloc_tests malloc_tests.cpp) add_contract(simple_tests simple_tests simple_tests.cpp) diff --git a/tests/unit/test_contracts/action_results_test.cpp b/tests/unit/test_contracts/action_results_test.cpp new file mode 100644 index 0000000000..0d09d79a4d --- /dev/null +++ b/tests/unit/test_contracts/action_results_test.cpp @@ -0,0 +1,17 @@ +#include + +using namespace eosio; + +class [[eosio::contract]] action_results_test : public contract { + public: + using contract::contract; + + [[eosio::action]] + void action1() {} + + [[eosio::action]] + uint32_t action2() { return 42; } + + [[eosio::action]] + std::string action3() { return "foo"; } +}; diff --git a/tests/unit/test_contracts/capi/action.c b/tests/unit/test_contracts/capi/action.c index 63824f2bff..956510a44d 100644 --- a/tests/unit/test_contracts/capi/action.c +++ b/tests/unit/test_contracts/capi/action.c @@ -13,5 +13,5 @@ void test_action( void ) { send_context_free_inline(NULL, 0); publication_time(); current_receiver(); - set_action_return_value(NULL, 0); + //set_action_return_value(NULL, 0); } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a563e73dd5..141ae16197 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -29,9 +29,9 @@ macro (add_tool name) set(LLVM_LINK_COMPONENTS support) include_directories(include) - + add_executable(${name} ${CMAKE_BINARY_DIR}/${name}.cpp) - set_property(TARGET ${name} PROPERTY CXX_STANDARD 14) + set_property(TARGET ${name} PROPERTY CXX_STANDARD 17) target_compile_options(${name} PRIVATE -fexceptions -fno-rtti) target_include_directories(${name} PUBLIC ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/../jsoncons/include ${CMAKE_SOURCE_DIR}/eosio_llvm/tools/clang/include ${LLVM_INCLUDE_DIR}) target_link_libraries(${name} @@ -71,7 +71,6 @@ macro (add_tool name) add_custom_command( TARGET ${name} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_BINARY_DIR}/bin/ ) endmacro() -add_subdirectory(abigen) add_subdirectory(abidiff) add_subdirectory(cc) add_subdirectory(ld) diff --git a/tools/abidiff/eosio-abidiff.cpp.in b/tools/abidiff/eosio-abidiff.cpp.in index 2ab40bee1f..f927ec6c43 100644 --- a/tools/abidiff/eosio-abidiff.cpp.in +++ b/tools/abidiff/eosio-abidiff.cpp.in @@ -224,6 +224,26 @@ class abidiff { } } + void print_action_results(const ojson& abi, int index, char direction) { + std::cout << direction << " action_result\n"; + std::cout << pretty_print(abi["action_results"].at(index)) << "\n"; + } + + void find_action_results(const ojson& abi1, const ojson& abi2, char direction) { + for ( int i=0; i < abi1["action_results"].size(); i++ ) { + bool found = false; + for ( int j=0; j < abi2["action_results"].size(); j++ ) { + if (abi1["action_results"].at(i)["name"] == abi2["action_results"].at(j)["name"]) { + if (abi1["action_results"].at(i)["result_type"] != abi2["action_results"].at(i)["result_type"]) + break; + found = true; + } + + } + if (!found) + print_action_results(abi1, i, direction); + } + } void diff_structs() { find_structs(abi_1, abi_2, '<'); find_structs(abi_2, abi_1, '>'); @@ -254,6 +274,11 @@ class abidiff { find_variants(abi_2, abi_1, '>'); } + void diff_action_results() { + find_action_results(abi_1, abi_2, '<'); + find_action_results(abi_2, abi_1, '>'); + } + void diff() { diff_version(); diff_structs(); @@ -263,6 +288,8 @@ class abidiff { diff_clauses(); if ( get_version(abi_1) >= 11 && get_version(abi_2) >= 11 ) diff_variants(); + if ( get_version(abi_1) >= 12 && get_version(abi_2) >= 12 ) + diff_action_results(); } }; diff --git a/tools/abigen/CMakeLists.txt b/tools/abigen/CMakeLists.txt deleted file mode 100644 index d5001a6af0..0000000000 --- a/tools/abigen/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/eosio-abigen.cpp.in ${CMAKE_BINARY_DIR}/eosio-abigen.cpp) - -add_tool(eosio-abigen) diff --git a/tools/abigen/eosio-abigen.cpp.in b/tools/abigen/eosio-abigen.cpp.in deleted file mode 100644 index 96021b4f5b..0000000000 --- a/tools/abigen/eosio-abigen.cpp.in +++ /dev/null @@ -1,616 +0,0 @@ -// Declares clang::SyntaxOnlyAction. -#include "clang/Frontend/FrontendActions.h" -#include "clang/Tooling/CommonOptionsParser.h" -#include "clang/Tooling/Tooling.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/Expr.h" -#include "clang/Basic/Builtins.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Rewrite/Frontend/Rewriters.h" -#include "llvm/Support/FileSystem.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -// Declares llvm::cl::extrahelp. -#include "llvm/Support/CommandLine.h" -using namespace clang::tooling; -using namespace clang::ast_matchers; -using namespace llvm; -using namespace eosio; -using namespace eosio::cdt; -using jsoncons::json; -using jsoncons::ojson; - -struct abigen_exception : public std::exception { - virtual const char* what() const throw() { - return "eosio.abigen fatal error"; - } -} abigen_ex; - - -DeclarationMatcher function_decl_matcher = cxxMethodDecl().bind("eosio_abis"); -DeclarationMatcher record_decl_matcher = cxxRecordDecl().bind("eosio_abis"); -DeclarationMatcher typedef_decl_matcher = typedefDecl().bind("eosio_abis"); -auto class_tmp_matcher = classTemplateSpecializationDecl().bind("eosio_abis"); - -class abigen : public generation_utils { - public: - abigen() : generation_utils([&](){throw abigen_ex;}) {} - void add_typedef( const clang::QualType& t ) { - abi_typedef ret; - ret.new_type_name = get_base_type_name( t ); - auto td = get_type_alias(t); - if (td.empty()) - return; - ret.type = translate_type(td[0]); - if(!is_builtin_type(td[0])) - add_type(td[0]); - _abi.typedefs.insert(ret); - } - - void add_action( const clang::CXXRecordDecl* decl ) { - abi_action ret; - auto action_name = decl->getEosioActionAttr()->getName(); - - if (rcs[get_action_name(decl)].empty()) - std::cout << "Warning, action <"+get_action_name(decl)+"> does not have a ricardian contract\n"; - - ret.ricardian_contract = rcs[get_action_name(decl)]; - - if (action_name.empty()) { - try { - validate_name( decl->getName().str(), error_handler ); - } catch (...) { - std::cout << "Error, name <" <getName().str() << "> is an invalid EOSIO name.\n"; - throw; - } - ret.name = decl->getName().str(); - } - else { - try { - validate_name( action_name.str(), error_handler ); - } catch (...) { - std::cout << "Error, name <" << action_name.str() << "> is an invalid EOSIO name.\n"; - throw; - } - ret.name = action_name.str(); - } - ret.type = decl->getName().str(); - _abi.actions.insert(ret); - } - - void add_action( const clang::CXXMethodDecl* decl ) { - abi_action ret; - - auto action_name = decl->getEosioActionAttr()->getName(); - - if (rcs[get_action_name(decl)].empty()) - std::cout << "Warning, action <"+get_action_name(decl)+"> does not have a ricardian contract\n"; - - ret.ricardian_contract = rcs[get_action_name(decl)]; - - if (action_name.empty()) { - try { - validate_name( decl->getNameAsString(), error_handler ); - } catch (...) { - std::cout << "Error, name <" <getNameAsString() << "> is an invalid EOSIO name.\n"; - } - ret.name = decl->getNameAsString(); - } - else { - try { - validate_name( action_name.str(), error_handler ); - } catch (...) { - std::cout << "Error, name <" << action_name.str() << "> is an invalid EOSIO name.\n"; - } - ret.name = action_name.str(); - } - ret.type = decl->getNameAsString(); - _abi.actions.insert(ret); - } - - void add_tuple(const clang::QualType& type) { - auto pt = llvm::dyn_cast(type.getTypePtr()); - auto tst = llvm::dyn_cast(pt->desugar().getTypePtr()); - if (!tst) - throw abigen_ex; - abi_struct tup; - tup.name = get_type(type); - for (int i = 0; i < tst->getNumArgs(); ++i) { - clang::QualType ftype = get_template_argument(type, i).getAsType(); - add_type(ftype); - tup.fields.push_back( {"field_"+std::to_string(i), - translate_type(ftype)} ); - } - _abi.structs.insert(tup); - } - - void add_pair(const clang::QualType& type) { - for (int i = 0; i < 2; ++i) { - clang::QualType ftype = get_template_argument(type, i).getAsType(); - std::string ty = translate_type(ftype); - add_type(ftype); - } - abi_struct pair; - pair.name = get_type(type); - pair.fields.push_back( {"first", translate_type(get_template_argument(type).getAsType())} ); - pair.fields.push_back( {"second", translate_type(get_template_argument(type, 1).getAsType())} ); - add_type(get_template_argument(type).getAsType()); - add_type(get_template_argument(type, 1).getAsType()); - _abi.structs.insert(pair); - } - - void add_map(const clang::QualType& type) { - for (int i = 0; i < 2; ++i) { - clang::QualType ftype = get_template_argument(type, i).getAsType(); - std::string ty = translate_type(ftype); - add_type(ftype); - } - abi_struct kv; - std::string name = get_type(type); - kv.name = name.substr(0, name.length() - 2); - kv.fields.push_back( {"key", translate_type(get_template_argument(type).getAsType())} ); - kv.fields.push_back( {"value", translate_type(get_template_argument(type, 1).getAsType())} ); - add_type(get_template_argument(type).getAsType()); - add_type(get_template_argument(type, 1).getAsType()); - _abi.structs.insert(kv); - } - - void add_struct( const clang::CXXRecordDecl* decl, const std::string& rname="" ) { - abi_struct ret; - if ( decl->getNumBases() == 1 ) { - ret.base = get_type(decl->bases_begin()->getType()); - add_struct(decl->bases_begin()->getType().getTypePtr()->getAsCXXRecordDecl()); - } - std::string sub_name = ""; - for ( auto field : decl->fields() ) { - if ( field->getName() == "transaction_extensions") { - abi_struct ext; - ext.name = "extension"; - ext.fields.push_back( {"type", "uint16"} ); - ext.fields.push_back( {"data", "bytes"} ); - ret.fields.push_back( {"transaction_extensions", "extension[]"}); - _abi.structs.insert(ext); - } - else { - ret.fields.push_back({field->getName().str(), get_type(field->getType())}); - sub_name += "_" + get_type(field->getType()); - add_type(field->getType()); - } - } - if (!rname.empty()) - ret.name = rname; - else - ret.name = decl->getName().str(); - _abi.structs.insert(ret); - } - - void add_struct( const clang::CXXMethodDecl* decl ) { - abi_struct new_struct; - new_struct.name = decl->getNameAsString(); - for (auto param : decl->parameters() ) { - auto param_type = param->getType().getNonReferenceType().getUnqualifiedType(); - new_struct.fields.push_back({param->getNameAsString(), get_type(param_type)}); - add_type(param_type); - } - _abi.structs.insert(new_struct); - } - - std::string to_index_type( std::string t ) { - return "i64"; - } - - void add_table( const clang::CXXRecordDecl* decl ) { - tables.insert(decl); - abi_table t; - t.type = decl->getNameAsString(); - auto table_name = decl->getEosioTableAttr()->getName(); - if (!table_name.empty()) { - try { - validate_name( table_name.str(), error_handler ); - } catch (...) { - } - t.name = table_name.str(); - } - else { - t.name = t.type; - } - ctables.insert(t); - } - - void add_table( uint64_t name, const clang::CXXRecordDecl* decl ) { - if (!(decl->isEosioTable() && abigen::is_eosio_contract(decl, get_contract_name()))) - return; - abi_table t; - t.type = decl->getNameAsString(); - t.name = name_to_string(name); - _abi.tables.insert(t); - } - - void add_clauses( const std::vector>& clauses ) { - for ( auto clp : clauses ) { - _abi.ricardian_clauses.push_back({std::get<0>(clp), std::get<1>(clp)}); - } - } - - void add_contracts( const std::map& rc ) { - rcs = rc; - } - - void add_variant( const clang::QualType& t ) { - abi_variant var; - auto pt = llvm::dyn_cast(t.getTypePtr()); - auto tst = llvm::dyn_cast(pt ? pt->desugar().getTypePtr() : t.getTypePtr()); - var.name = get_type(t); - for (int i=0; i < tst->getNumArgs(); ++i) - var.types.push_back(translate_type(get_template_argument( t, i ).getAsType())); - _abi.variants.insert(var); - } - - void add_type( const clang::QualType& t ) { - auto type = get_ignored_type(t); - if (!is_builtin_type(translate_type(type))) { - if (is_aliasing(type)) - add_typedef(type); - else if (is_template_specialization(type, {"vector", "set", "deque", "list", "optional", "binary_extension", "ignore"})) { - add_type(get_template_argument(type).getAsType()); - } - else if (is_template_specialization(type, {"map"})) - add_map(type); - else if (is_template_specialization(type, {"pair"})) - add_pair(type); - else if (is_template_specialization(type, {"tuple"})) - add_tuple(type); - else if (is_template_specialization(type, {"variant"})) - add_variant(type); - else if (is_template_specialization(type, {})) { - add_struct(type.getTypePtr()->getAsCXXRecordDecl(), get_template_name(type)); - } - else if (type.getTypePtr()->isRecordType()) - add_struct(type.getTypePtr()->getAsCXXRecordDecl()); - } - } - - std::string generate_json_comment() { - std::stringstream ss; - ss << "This file was generated with eosio-abigen."; - ss << " DO NOT EDIT"; - return ss.str(); - } - - ojson struct_to_json( const abi_struct& s ) { - ojson o; - o["name"] = s.name; - o["base"] = s.base; - o["fields"] = ojson::array(); - for ( auto field : s.fields ) { - ojson f; - f["name"] = field.name; - f["type"] = field.type; - o["fields"].push_back(f); - } - return o; - } - - ojson variant_to_json( const abi_variant& v ) { - ojson o; - o["name"] = v.name; - o["types"] = ojson::array(); - for ( auto ty : v.types ) { - o["types"].push_back( ty ); - } - return o; - } - - ojson typedef_to_json( const abi_typedef& t ) { - ojson o; - o["new_type_name"] = t.new_type_name; - o["type"] = t.type; - return o; - } - - ojson action_to_json( const abi_action& a ) { - ojson o; - o["name"] = a.name; - o["type"] = a.type; - o["ricardian_contract"] = a.ricardian_contract; - return o; - } - - ojson clause_to_json( const abi_ricardian_clause_pair& clause ) { - ojson o; - o["id"] = clause.id; - o["body"] = clause.body; - return o; - } - - ojson table_to_json( const abi_table& t ) { - ojson o; - o["name"] = t.name; - o["type"] = t.type; - o["index_type"] = "i64"; - o["key_names"] = ojson::array(); - o["key_types"] = ojson::array(); - return o; - } - - ojson to_json() { - ojson o; - o["____comment"] = generate_json_comment(); - o["version"] = _abi.version; - o["structs"] = ojson::array(); - auto remove_suffix = [&]( std::string name ) { - int i = name.length()-1; - for (; i >= 0; i--) - if ( name[i] != '[' && name[i] != ']' && name[i] != '?' && name[i] != '$' ) - break; - return name.substr(0,i+1); - }; - - std::set set_of_tables; - for ( auto t : ctables ) { - bool has_multi_index = false; - for ( auto u : _abi.tables ) { - if (t.type == u.type) { - has_multi_index = true; - break; - } - set_of_tables.insert(u); - } - if (!has_multi_index) - set_of_tables.insert(t); - } - for ( auto t : _abi.tables ) { - set_of_tables.insert(t); - } - - auto validate_struct = [&]( abi_struct as ) { - if ( is_builtin_type(_translate_type(as.name)) ) - return false; - for ( auto s : _abi.structs ) { - for ( auto f : s.fields ) { - if (as.name == _translate_type(remove_suffix(f.type))) - return true; - } - if (s.base == as.name) - return true; - } - for ( auto a : _abi.actions ) { - if (as.name == _translate_type(a.type)) - return true; - } - for( auto t : set_of_tables ) { - if (as.name == _translate_type(t.type)) - return true; - } - for( auto td : _abi.typedefs ) { - if (as.name == _translate_type(remove_suffix(td.type))) - return true; - } - return false; - }; - - auto validate_types = [&]( abi_typedef td ) { - for ( auto as : _abi.structs ) - if (validate_struct(as)) - for ( auto f : as.fields ) - if ( remove_suffix(f.type) == td.new_type_name ) - return true; - for ( auto t : _abi.tables ) - if ( t.type == td.new_type_name ) - return true; - for ( auto a : _abi.actions ) - if ( a.type == td.new_type_name ) - return true; - for ( auto _td : _abi.typedefs ) - if ( remove_suffix(_td.type) == td.new_type_name ) - return true; - return false; - }; - - for ( auto s : _abi.structs ) { - if (validate_struct(s)) - o["structs"].push_back(struct_to_json(s)); - } - o["types"] = ojson::array(); - for ( auto t : _abi.typedefs ) { - if (validate_types(t)) - o["types"].push_back(typedef_to_json( t )); - } - o["actions"] = ojson::array(); - for ( auto a : _abi.actions ) { - o["actions"].push_back(action_to_json( a )); - } - o["tables"] = ojson::array(); - for ( auto t : set_of_tables ) { - o["tables"].push_back(table_to_json( t )); - } - o["ricardian_clauses"] = ojson::array(); - for ( auto rc : _abi.ricardian_clauses ) { - o["ricardian_clauses"].push_back(clause_to_json( rc )); - } - o["variants"] = ojson::array(); - for ( auto v : _abi.variants ) { - o["variants"].push_back(variant_to_json( v )); - } - o["abi_extensions"] = ojson::array(); - return o; - } - - abi& get_abi_ref() { return _abi; } - - - private: - abi _abi; - std::set tables; - std::set ctables; - std::map rcs; -}; - -abigen& get_abigen_ref() { - static abigen ag; - return ag; -} - -class EosioMethodMatcher : public MatchFinder::MatchCallback { - public: - virtual void run( const MatchFinder::MatchResult& res ) { - if (const clang::CXXMethodDecl* decl = res.Nodes.getNodeAs("eosio_abis")->getCanonicalDecl()) { - abi abi; - if (decl->isEosioAction() && abigen::is_eosio_contract(decl, get_abigen_ref().get_contract_name())) { - get_abigen_ref().add_struct(decl); - get_abigen_ref().add_action(decl); - auto params = decl->parameters(); - for (auto param : params) { - get_abigen_ref().add_type(param->getType()); - } - } - } - } -}; - -class EosioRecordMatcher : public MatchFinder::MatchCallback { - public: - bool has_added_clauses = false; - virtual void run( const MatchFinder::MatchResult& res ) { - if (const clang::CXXRecordDecl* decl = res.Nodes.getNodeAs("eosio_abis")) { - if (!has_added_clauses) { - get_abigen_ref().add_clauses(get_abigen_ref().parse_clauses()); - get_abigen_ref().add_contracts(get_abigen_ref().parse_contracts()); - has_added_clauses = true; - } - if (decl->isEosioAction() && abigen::is_eosio_contract(decl, get_abigen_ref().get_contract_name())) { - get_abigen_ref().add_struct(decl); - get_abigen_ref().add_action(decl); - for (auto field : decl->fields()) { - get_abigen_ref().add_type( field->getType() ); - } - } - if (decl->isEosioTable() && abigen::is_eosio_contract(decl, get_abigen_ref().get_contract_name())) { - get_abigen_ref().add_struct(decl); - get_abigen_ref().add_table(decl); - for (auto field : decl->fields()) - get_abigen_ref().add_type( field->getType() ); - } - } - - if (const clang::ClassTemplateSpecializationDecl* decl = res.Nodes.getNodeAs("eosio_abis")) { - if ( decl->getName() == "multi_index" ) { - get_abigen_ref().add_table(decl->getTemplateArgs()[0].getAsIntegral().getExtValue(), - (clang::CXXRecordDecl*)((clang::RecordType*)decl->getTemplateArgs()[1].getAsType().getTypePtr())->getDecl()); - } - } - } - - -}; - -int main(int argc, const char **argv) { - - cl::SetVersionPrinter([](llvm::raw_ostream& os) { - os << "eosio-abigen version " << "${VERSION_FULL}" << "\n"; - }); - cl::OptionCategory cat("eosio-abigen", "generates an abi from C++ project input"); - - cl::opt abidir( - "output", - cl::desc("Set the output filename and fullpath"), - cl::Required, - cl::cat(cat)); - cl::opt contract_name( - "contract", - cl::desc("Set the contract name"), - cl::Required, - cl::cat(cat)); - cl::opt def( - "default", - cl::desc(""), - cl::Hidden, - cl::cat(cat)); - cl::list resource_paths( - "R", - cl::desc("Add a resource path for inclusion"), - cl::cat(cat), - cl::Prefix, - cl::ZeroOrMore); - - std::vector options; - for (size_t i=0; i < argc; i++) { - options.push_back(argv[i]); - } - bool has_dash_dash = false; - for (auto opt : options) { - if ( opt.compare("--") == 0 ) { - has_dash_dash = true; - break; - } - } - if (!has_dash_dash) - options.push_back("--"); - options.push_back("--target=wasm32"); - options.push_back("-nostdlib"); - options.push_back("-ffreestanding"); - options.push_back("-fno-builtin"); - options.push_back("-fno-rtti"); - options.push_back("-fno-exceptions"); - options.push_back("-I${Boost_INCLUDE_DIRS}"); - options.push_back("-DBOOST_DISABLE_ASSERTS"); - options.push_back("-DBOOST_EXCEPTION_DISABLE"); - options.push_back("-Wno-everything"); - options.push_back("-std=c++17"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../include/libcxx"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../include/libc"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../include"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../../../../../libraries/libc++/libcxx/include"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../../../../../libraries/libc/musl/include"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../../../../../libraries"); - options.push_back(std::string("-I")+eosio::cdt::whereami::where()+"/../../../../../libraries/boost/include"); - - int size = options.size(); - const char** new_argv = new const char*[size]; - for (size_t i=0; i < size; i++) - new_argv[i] = options[i].c_str(); - - CommonOptionsParser opts( size, new_argv, cat, 0 ); - - ClangTool tool( opts.getCompilations(), opts.getSourcePathList()); - get_abigen_ref().set_contract_name(contract_name); - get_abigen_ref().set_resource_dirs(resource_paths); - EosioMethodMatcher eosio_method_matcher; - EosioRecordMatcher eosio_record_matcher; - MatchFinder finder; - finder.addMatcher(function_decl_matcher, &eosio_method_matcher); - finder.addMatcher(record_decl_matcher, &eosio_record_matcher); - finder.addMatcher(class_tmp_matcher, &eosio_record_matcher); - - llvm::errs() << "Warning, this tool is deprecated. Only use eosio-cpp in the future." << '\n'; - int tool_run = -1; - try { - tool_run = tool.run(newFrontendActionFactory(&finder).get()); - std::ofstream output(abidir); - output << pretty_print(get_abigen_ref().to_json()); - output.close(); - } catch (std::exception& ex) { - std::cout << ex.what() << "\n"; - tool_run = -1; - } - - return tool_run; -} diff --git a/tools/cc/eosio-cpp.cpp.in b/tools/cc/eosio-cpp.cpp.in index b87c0b3686..fb1d6bf262 100644 --- a/tools/cc/eosio-cpp.cpp.in +++ b/tools/cc/eosio-cpp.cpp.in @@ -107,7 +107,7 @@ namespace eosio { namespace cdt { }; }} // ns eosio::cdt -void generate(const std::vector& base_options, std::string input, std::string contract_name, const std::vector& resource_paths, bool abigen) { +void generate(const std::vector& base_options, std::string input, std::string contract_name, const std::vector& resource_paths, const std::pair& abi_version, bool abigen) { std::vector options; options.push_back("eosio-cpp"); options.push_back(input); // don't remove oddity of CommonOptionsParser? @@ -130,6 +130,7 @@ void generate(const std::vector& base_options, std::string input, s get_abigen_ref().set_contract_name(contract_name); get_abigen_ref().set_resource_dirs(resource_paths); + get_abigen_ref().set_abi_version(std::get<0>(abi_version), std::get<1>(abi_version)); codegen::get().set_contract_name(contract_name); EosioMethodMatcher eosio_method_matcher; @@ -189,7 +190,7 @@ int main(int argc, const char **argv) { tool_opts.erase(std::remove_if(tool_opts.begin(), tool_opts.end(), [&](const auto& opt){ return non_tool_opts.count(opt); }), tool_opts.end()); - generate(tool_opts, input, opts.abigen_contract, opts.abigen_resources, opts.abigen); + generate(tool_opts, input, opts.abigen_contract, opts.abigen_resources, opts.abi_version, opts.abigen); auto src = SmallString<64>(input); llvm::sys::path::remove_filename(src); @@ -214,10 +215,10 @@ int main(int argc, const char **argv) { new_opts.insert(new_opts.begin(), "-xc++"); if (!eosio::cdt::environment::exec_subprogram("clang-7", new_opts)) { - llvm::sys::fs::remove(tmp_file); + //llvm::sys::fs::remove(tmp_file); return -1; } - llvm::sys::fs::remove(tmp_file); + //llvm::sys::fs::remove(tmp_file); } } catch (std::runtime_error& err) { llvm::errs() << err.what() << '\n'; diff --git a/tools/include/compiler_options.hpp.in b/tools/include/compiler_options.hpp.in index b461905ac8..20ea092420 100644 --- a/tools/include/compiler_options.hpp.in +++ b/tools/include/compiler_options.hpp.in @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -19,6 +20,10 @@ static llvm::cl::OptionCategory EosioLdToolCategory("ld options"); #endif /// begin ld options +static cl::opt abi_version_opt( + "abi-version", + cl::desc("Which ABI version to generate"), + cl::cat(LD_CAT)); static cl::opt only_export_opt( "only-export", cl::desc("Export only this symbol"), @@ -364,6 +369,7 @@ struct Options { std::vector abigen_resources; bool debug; bool native; + std::pair abi_version; }; static void GetCompDefaults(std::vector& copts) { @@ -867,9 +873,19 @@ static Options CreateOptions(bool add_defaults=true) { ldopts.emplace_back("-fuse-main"); #endif + /* TODO add some way of defaulting these to the current highest version */ + int abi_version_major = 1; + int abi_version_minor = 2; + + if (!abi_version_opt.empty()) { + abi_version_major = std::stoi(abi_version_opt); + float tmp = std::stof(abi_version_opt); + abi_version_minor = ((tmp - (int)tmp)*10); + } + #ifndef ONLY_LD - return {output_fn, inputs, link, abigen, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt}; + return {output_fn, inputs, link, abigen, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt, {abi_version_major, abi_version_minor}}; #else - return {output_fn, {}, link, abigen, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt}; + return {output_fn, {}, link, abigen, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt, {abi_version_major, abi_version_minor}}; #endif } diff --git a/tools/include/eosio/abi.hpp b/tools/include/eosio/abi.hpp index 206718245a..b45c3d4c66 100644 --- a/tools/include/eosio/abi.hpp +++ b/tools/include/eosio/abi.hpp @@ -55,9 +55,17 @@ struct abi_error_message { std::string error_msg; }; +struct abi_action_result { + std::string name; + std::string type; + bool operator<(const abi_action_result& ar) const { return name < ar.name; } +}; + /// From eosio libraries/chain/include/eosio/chain/abi_def.hpp struct abi { - std::string version = "eosio::abi/1.1"; + int version_major = 1; + int version_minor = 1; + std::string version_string()const { return std::string("eosio::abi/")+std::to_string(version_major)+"."+std::to_string(version_minor); } std::set structs; std::set typedefs; std::set actions; @@ -65,11 +73,12 @@ struct abi { std::set variants; std::vector ricardian_clauses; std::vector error_messages; + std::set action_results; }; inline void dump( const abi& abi ) { - std::cout << "ABI : "; - std::cout << "\n\tversion : " << abi.version; + std::cout << "ABI : "; + std::cout << "\n\tversion : " << abi.version_string(); std::cout << "\n\tstructs : "; for (auto s : abi.structs) { std::cout << "\n\t\tstruct : "; diff --git a/tools/include/eosio/abigen.hpp b/tools/include/eosio/abigen.hpp index 02fd768b65..04f7b3b0e9 100644 --- a/tools/include/eosio/abigen.hpp +++ b/tools/include/eosio/abigen.hpp @@ -35,6 +35,12 @@ namespace eosio { namespace cdt { abigen() : generation_utils([&](){throw abigen_ex;}) { } + + void set_abi_version(int major, int minor) { + _abi.version_major = major; + _abi.version_minor = minor; + } + void add_typedef( const clang::QualType& t ) { abi_typedef ret; ret.new_type_name = get_base_type_name( t ); @@ -106,6 +112,8 @@ namespace eosio { namespace cdt { } ret.type = decl->getNameAsString(); _abi.actions.insert(ret); + if (translate_type(decl->getReturnType()) != "void") + _abi.action_results.insert({get_action_name(decl), translate_type(decl->getReturnType())}); } void add_tuple(const clang::QualType& type) { @@ -133,11 +141,11 @@ namespace eosio { namespace cdt { abi_struct pair; pair.name = get_type(type); pair.fields.push_back( {"first", translate_type(get_template_argument(type).getAsType())} ); - pair.fields.push_back( {"second", translate_type(get_template_argument(type, 1).getAsType())} ); + pair.fields.push_back( {"second", translate_type(get_template_argument(type, 1).getAsType())} ); add_type(get_template_argument(type).getAsType()); add_type(get_template_argument(type, 1).getAsType()); _abi.structs.insert(pair); - } + } void add_map(const clang::QualType& type) { for (int i = 0; i < 2; ++i) { @@ -149,7 +157,7 @@ namespace eosio { namespace cdt { std::string name = get_type(type); kv.name = name.substr(0, name.length() - 2); kv.fields.push_back( {"key", translate_type(get_template_argument(type).getAsType())} ); - kv.fields.push_back( {"value", translate_type(get_template_argument(type, 1).getAsType())} ); + kv.fields.push_back( {"value", translate_type(get_template_argument(type, 1).getAsType())} ); add_type(get_template_argument(type).getAsType()); add_type(get_template_argument(type, 1).getAsType()); _abi.structs.insert(kv); @@ -243,7 +251,7 @@ namespace eosio { namespace cdt { var.types.push_back(translate_type(get_template_argument( t, i ).getAsType())); add_type(get_template_argument( t, i ).getAsType()); } - _abi.variants.insert(var); + _abi.variants.insert(var); } void add_type( const clang::QualType& t ) { @@ -277,7 +285,7 @@ namespace eosio { namespace cdt { std::stringstream ss; ss << "This file was generated with eosio-abigen."; ss << " DO NOT EDIT "; - return ss.str(); + return ss.str(); } ojson struct_to_json( const abi_struct& s ) { @@ -335,7 +343,14 @@ namespace eosio { namespace cdt { o["key_types"] = ojson::array(); return o; } - + + ojson action_result_to_json( const abi_action_result& result ) { + ojson o; + o["name"] = result.name; + o["result_type"] = result.type; + return o; + } + bool is_empty() { std::set set_of_tables; for ( auto t : ctables ) { @@ -360,11 +375,11 @@ namespace eosio { namespace cdt { ojson to_json() { ojson o; o["____comment"] = generate_json_comment(); - o["version"] = _abi.version; + o["version"] = _abi.version_string(); o["structs"] = ojson::array(); auto remove_suffix = [&]( std::string name ) { int i = name.length()-1; - for (; i >= 0; i--) + for (; i >= 0; i--) if ( name[i] != '[' && name[i] != ']' && name[i] != '?' && name[i] != '$' ) break; return name.substr(0,i+1); @@ -481,10 +496,16 @@ namespace eosio { namespace cdt { o["variants"].push_back(variant_to_json( v )); } o["abi_extensions"] = ojson::array(); + if (_abi.version_major == 1 && _abi.version_minor == 2) { + o["action_results"] = ojson::array(); + for ( auto ar : _abi.action_results ) { + o["action_results"].push_back(action_result_to_json( ar )); + } + } return o; } - private: + private: abi _abi; std::set tables; std::set ctables; diff --git a/tools/include/eosio/abimerge.hpp b/tools/include/eosio/abimerge.hpp index d5f650ce86..1198f66768 100644 --- a/tools/include/eosio/abimerge.hpp +++ b/tools/include/eosio/abimerge.hpp @@ -25,7 +25,7 @@ class ABIMerger { } ojson merge(ojson other) { ojson ret; - ret["____comment"] = abi["____comment"]; + ret["____comment"] = abi["____comment"]; ret["version"] = merge_version(other); ret["types"] = merge_types(other); ret["structs"] = merge_structs(other); @@ -33,6 +33,10 @@ class ABIMerger { ret["tables"] = merge_tables(other); ret["ricardian_clauses"] = merge_clauses(other); ret["variants"] = merge_variants(other); + std::string vers = abi["version"].as(); + if (std::stod(vers.substr(vers.size()-3))*10 >= 12) { + ret["action_results"] = merge_action_results(other); + } return ret; } private: @@ -99,13 +103,18 @@ class ABIMerger { a["key_names"] == b["key_names"] && a["key_types"] == b["key_types"]; } - + static bool clause_is_same(ojson a, ojson b) { return a["id"] == b["id"] && a["body"] == b["body"]; - } - - template + } + + static bool action_result_is_same(ojson a, ojson b) { + return a["name"] == b["name"] && + a["result_type"] == b["result_type"]; + } + + template void add_object(ojson& ret, ojson a, ojson b, std::string type, std::string id, F&& is_same_func) { for (auto obj_a : a[type].array_range()) { ret.push_back(obj_a); @@ -125,7 +134,7 @@ class ABIMerger { ret.push_back(obj_b); } } - + ojson merge_structs(ojson b) { ojson structs = ojson::array(); add_object(structs, abi, b, "structs", "name", struct_is_same); @@ -162,6 +171,12 @@ class ABIMerger { return cls; } + ojson merge_action_results(ojson b) { + ojson res = ojson::array(); + add_object(res, abi, b, "action_results", "name", action_result_is_same); + return res; + } + ojson abi; }; #pragma GCC diagnostic pop diff --git a/tools/include/eosio/codegen.hpp b/tools/include/eosio/codegen.hpp index aedc6c0145..c2c2faeafd 100644 --- a/tools/include/eosio/codegen.hpp +++ b/tools/include/eosio/codegen.hpp @@ -241,6 +241,11 @@ namespace eosio { namespace cdt { ss << "uint32_t action_data_size();\n"; ss << "__attribute__((eosio_wasm_import))\n"; ss << "uint32_t read_action_data(void*, uint32_t);\n"; + const auto& return_ty = decl->getReturnType().getAsString(); + if (return_ty != "void") { + ss << "__attribute__((eosio_wasm_import))\n"; + ss << "void set_action_return_value(void*, size_t);\n"; + } ss << "__attribute__((weak, " << attr << "(\""; ss << get_str(decl); ss << ":"; @@ -267,13 +272,23 @@ namespace eosio { namespace cdt { ss << tn << " arg" << i << "; ds >> arg" << i << ";\n"; i++; } - ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{r},eosio::name{c},ds}." << decl->getNameAsString() << "("; - for (int i=0; i < decl->parameters().size(); i++) { - ss << "arg" << i; - if (i < decl->parameters().size()-1) - ss << ", "; + const auto& call_action = [&]() { + ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{r},eosio::name{c},ds}." << decl->getNameAsString() << "("; + for (int i=0; i < decl->parameters().size(); i++) { + ss << "arg" << i; + if (i < decl->parameters().size()-1) + ss << ", "; + } + ss << ");\n"; + }; + if (return_ty != "void") { + ss << return_ty << " result = "; + } + call_action(); + if (return_ty != "void") { + ss << "const auto& packed_result = eosio::pack(result);\n"; + ss << "set_action_return_value((void*)packed_result.data(), packed_result.size());\n"; } - ss << ");"; ss << "}}\n"; rewriter.InsertTextAfter(ci->getSourceManager().getLocForEndOfFile(main_fid), ss.str()); @@ -344,34 +359,6 @@ namespace eosio { namespace cdt { } return true; } - - /* - virtual bool VisitRecordDecl(RecordDecl* decl) { - static std::set _action_set; //used for validations - std::string rec_name = decl->getQualifiedNameAsString(); - cg.records.emplace(rec_name, decl); - return true; - } - virtual bool VisitCallExpr(CallExpr* expr) { - if (auto callee = expr->getDirectCallee()) { - if (callee->getNumParams() == 2) { - if (is_datastream(callee->getParamDecl(0)->getOriginalType())) { - cg.datastream_uses.insert(get_base_type(callee->getParamDecl(1)->getOriginalType())); - } - } - } - return true; - } - virtual bool VisitCXXRecordDecl(CXXRecordDecl* decl) { - std::string rec_name = decl->getQualifiedNameAsString(); - if (decl->isEosioAction()) { - rec_name = generation_utils::get_action_name(decl); - cg.actions.insert(rec_name); - } - cg.cxx_records.emplace(rec_name, decl); - return true; - } - */ }; class eosio_codegen_consumer : public ASTConsumer { @@ -397,7 +384,7 @@ namespace eosio { namespace cdt { visitor->TraverseDecl(Context.getTranslationUnitDecl()); for (auto ad : visitor->action_decls) visitor->create_action_dispatch(ad); - + for (auto nd : visitor->notify_decls) visitor->create_notify_dispatch(nd); diff --git a/tools/toolchain-tester/tests.py b/tools/toolchain-tester/tests.py index 6f5a2e2c73..d2734ff96f 100644 --- a/tools/toolchain-tester/tests.py +++ b/tools/toolchain-tester/tests.py @@ -97,8 +97,12 @@ def handle_expecteds(self, res: subprocess.CompletedProcess): failing_test=self, ) - if expected.get("abi"): - expected_abi = expected["abi"] + if expected.get("abi") or expected.get("abi-file"): + if expected.get("abi"): + expected_abi = expected["abi"] + else: + expected_abi_file = open(expected["abi-file"]) + expected_abi = expected_abi_file.read() with open(f"{self._name}.abi") as f: actual_abi = f.read() From c87a6d49aefc768f8dcae2d5141c273110260b09 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 11 Feb 2020 15:25:46 -0500 Subject: [PATCH 2/7] update pipeline to point to proper version of eos --- pipeline.jsonc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pipeline.jsonc b/pipeline.jsonc index 508acc722c..5a5bbecdf6 100644 --- a/pipeline.jsonc +++ b/pipeline.jsonc @@ -4,7 +4,7 @@ "pipeline-branch": "master", "dependencies": // dependencies to pull for cdt integration tests, by branch, tag, or commit hash { - "eosio": "release/1.7.x" + "eosio": "develop" } } -} \ No newline at end of file +} From 85147f9a24d52ddd9a978c6927fd75a9a396e60f Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 11 Feb 2020 15:27:45 -0500 Subject: [PATCH 3/7] add back capi test --- tests/integration/capi_tests.cpp | 6 ++++++ tests/unit/test_contracts/capi/action.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/integration/capi_tests.cpp b/tests/integration/capi_tests.cpp index 632042352b..95d904703c 100644 --- a/tests/integration/capi_tests.cpp +++ b/tests/integration/capi_tests.cpp @@ -21,6 +21,12 @@ BOOST_AUTO_TEST_SUITE(capi_tests) BOOST_FIXTURE_TEST_CASE( capi_tests, tester ) try { create_accounts( { N(test) } ); produce_block(); + + const auto& pfm = control->get_protocol_feature_manager(); + + auto d = pfm.get_builtin_digest(builtin_protocol_feature_t::action_return_value) + schedule_protocol_features( {*d} ); + set_code( N(test), contracts::capi_tests_wasm() ); set_abi( N(test), contracts::capi_tests_abi().data() ); produce_blocks(); diff --git a/tests/unit/test_contracts/capi/action.c b/tests/unit/test_contracts/capi/action.c index 956510a44d..63824f2bff 100644 --- a/tests/unit/test_contracts/capi/action.c +++ b/tests/unit/test_contracts/capi/action.c @@ -13,5 +13,5 @@ void test_action( void ) { send_context_free_inline(NULL, 0); publication_time(); current_receiver(); - //set_action_return_value(NULL, 0); + set_action_return_value(NULL, 0); } From 0d6716aaa7ed8dfce6047842e708f9e3f53e2f84 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 11 Feb 2020 15:35:57 -0500 Subject: [PATCH 4/7] revert commented out code --- tools/cc/eosio-cpp.cpp.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cc/eosio-cpp.cpp.in b/tools/cc/eosio-cpp.cpp.in index fb1d6bf262..cedc25d0b6 100644 --- a/tools/cc/eosio-cpp.cpp.in +++ b/tools/cc/eosio-cpp.cpp.in @@ -215,10 +215,10 @@ int main(int argc, const char **argv) { new_opts.insert(new_opts.begin(), "-xc++"); if (!eosio::cdt::environment::exec_subprogram("clang-7", new_opts)) { - //llvm::sys::fs::remove(tmp_file); + llvm::sys::fs::remove(tmp_file); return -1; } - //llvm::sys::fs::remove(tmp_file); + llvm::sys::fs::remove(tmp_file); } } catch (std::runtime_error& err) { llvm::errs() << err.what() << '\n'; From e983ed1c30b6dcd385d7f7b21ffe85fa09c70bd8 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 11 Feb 2020 19:16:37 -0500 Subject: [PATCH 5/7] finishing up changes --- modules/InstallCDT.cmake | 1 - tests/integration/action_results_test.cpp | 40 ++++------------------- tests/integration/capi_tests.cpp | 5 --- tools/toolchain-tester/tests.py | 1 + 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/modules/InstallCDT.cmake b/modules/InstallCDT.cmake index 36ec86f0a0..a762d4d71f 100644 --- a/modules/InstallCDT.cmake +++ b/modules/InstallCDT.cmake @@ -72,7 +72,6 @@ eosio_tool_install_and_symlink(eosio-wasm2wast eosio-wasm2wast) eosio_tool_install_and_symlink(eosio-cc eosio-cc) eosio_tool_install_and_symlink(eosio-cpp eosio-cpp) eosio_tool_install_and_symlink(eosio-ld eosio-ld) -eosio_tool_install_and_symlink(eosio-abigen eosio-abigen) eosio_tool_install_and_symlink(eosio-abidiff eosio-abidiff) eosio_tool_install_and_symlink(eosio-init eosio-init) diff --git a/tests/integration/action_results_test.cpp b/tests/integration/action_results_test.cpp index 4886b9bbda..3b9023bcb9 100644 --- a/tests/integration/action_results_test.cpp +++ b/tests/integration/action_results_test.cpp @@ -22,45 +22,19 @@ BOOST_FIXTURE_TEST_CASE( action_results_tests, tester ) try { create_accounts( { N(test) } ); produce_block(); - const auto& pfm = control->get_protocol_feature_manager(); - set_code( N(test), contracts::action_results_test_wasm() ); set_abi( N(test), contracts::action_results_test_abi().data() ); - auto d = pfm.get_builtin_digest(builtin_protocol_feature_t::action_return_value) - schedule_protocol_features( {*d} ); - - produce_blocks(); - const auto& trace = push_action(N(test), N(action1), N(test), mvo()); - - /* - BOOST_CHECK_THROW(push_action(N(test), N(test1), N(test), mvo()("nm", "notbucky")), - fc::exception); - - push_action(N(test), N(test2), N(test), - mvo() - ("arg0", 33) - ("arg1", "some string")); - BOOST_CHECK_THROW(push_action(N(test), N(test2), N(test), mvo() ("arg0", 30)("arg1", "some string")), fc::exception); - BOOST_CHECK_THROW(push_action(N(test), N(test2), N(test), mvo() ("arg0", 33)("arg1", "not some string")), fc::exception); - - set_abi( N(test), contracts::simple_wrong_abi().data() ); - produce_blocks(); - - BOOST_CHECK_THROW(push_action(N(test), N(test3), N(test), mvo() ("arg0", 33) ("arg1", "some string")), fc::exception); - set_abi( N(test), contracts::simple_abi().data() ); produce_blocks(); + auto trace = push_action(N(test), N(action1), N(test), mvo()); + // need to fix this test after Kevin fixes action_return + wdump((trace)); - push_action(N(test), N(test4), N(test), mvo() ("to", "someone")); - push_action(N(test), N(test5), N(test), mvo() ("to", "someone")); - push_action(N(test), N(testa), N(test), mvo() ("to", "someone")); - BOOST_CHECK_THROW(push_action(N(test), N(testb), N(test), mvo() ("to", "someone")), fc::exception); + trace = push_action(N(test), N(action2), N(test), mvo()); + wdump((trace)); - // test that the pre_dispatch will short circuit dispatching if false - push_action(N(test), N(testc), N(test), mvo() ("nm", "bucky")); - BOOST_CHECK_THROW(push_action(N(test), N(testc), N(test), mvo() ("nm", "someone")), fc::exception); - push_action(N(test), N(testc), N(test), mvo() ("nm", "quit")); - */ + trace = push_action(N(test), N(action3), N(test), mvo()); + wdump((trace)); } FC_LOG_AND_RETHROW() diff --git a/tests/integration/capi_tests.cpp b/tests/integration/capi_tests.cpp index 95d904703c..f33330682b 100644 --- a/tests/integration/capi_tests.cpp +++ b/tests/integration/capi_tests.cpp @@ -22,11 +22,6 @@ BOOST_FIXTURE_TEST_CASE( capi_tests, tester ) try { create_accounts( { N(test) } ); produce_block(); - const auto& pfm = control->get_protocol_feature_manager(); - - auto d = pfm.get_builtin_digest(builtin_protocol_feature_t::action_return_value) - schedule_protocol_features( {*d} ); - set_code( N(test), contracts::capi_tests_wasm() ); set_abi( N(test), contracts::capi_tests_abi().data() ); produce_blocks(); diff --git a/tools/toolchain-tester/tests.py b/tools/toolchain-tester/tests.py index d2734ff96f..a1a4873187 100644 --- a/tools/toolchain-tester/tests.py +++ b/tools/toolchain-tester/tests.py @@ -103,6 +103,7 @@ def handle_expecteds(self, res: subprocess.CompletedProcess): else: expected_abi_file = open(expected["abi-file"]) expected_abi = expected_abi_file.read() + expected_abi_file.close() with open(f"{self._name}.abi") as f: actual_abi = f.read() From 7d2efbb72d003ad4f1562496169e40a5d843ac76 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Wed, 12 Feb 2020 10:23:47 -0500 Subject: [PATCH 6/7] generate symlinks for missing tools and remove abigen --- scripts/generate_tarball.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/generate_tarball.sh b/scripts/generate_tarball.sh index cd4f25057b..2a9a56120f 100644 --- a/scripts/generate_tarball.sh +++ b/scripts/generate_tarball.sh @@ -4,7 +4,7 @@ NAME=$1 CDT_PREFIX=${PREFIX}/${SUBPREFIX} mkdir -p ${PREFIX}/bin/ mkdir -p ${PREFIX}/lib/cmake/${PROJECT} -mkdir -p ${CDT_PREFIX}/bin +mkdir -p ${CDT_PREFIX}/bin mkdir -p ${CDT_PREFIX}/include mkdir -p ${CDT_PREFIX}/lib/cmake/${PROJECT} mkdir -p ${CDT_PREFIX}/cmake @@ -13,7 +13,7 @@ mkdir -p ${CDT_PREFIX}/licenses #echo "${PREFIX} ** ${SUBPREFIX} ** ${CDT_PREFIX}" -# install binaries +# install binaries cp -R ${BUILD_DIR}/bin/* ${CDT_PREFIX}/bin || exit 1 cp -R ${BUILD_DIR}/licenses/* ${CDT_PREFIX}/licenses || exit 1 @@ -50,10 +50,16 @@ create_symlink eosio-cpp eosio-cpp create_symlink eosio-ld eosio-ld create_symlink eosio-pp eosio-pp create_symlink eosio-init eosio-init -create_symlink eosio-abigen eosio-abigen create_symlink eosio-wasm2wast eosio-wasm2wast create_symlink eosio-wast2wasm eosio-wast2wasm create_symlink eosio-ar eosio-ar +create_symlink eosio-abidiff eosio-abidiff +create_symlink eosio-nm eosio-nm +create_symlink eosio-objcopy eosio-objcopy +create_symlink eosio-objdump eosio-objdump +create_symlink eosio-ranlib eosio-ranlib +create_symlink eosio-readelf eosio-readelf +create_symlink eosio-strip eosio-strip echo "Generating Tarball $NAME.tar.gz..." tar -cvzf $NAME.tar.gz ./${PREFIX}/* || exit 1 From 2c37881904f20ea4b827089252feed35758f8570 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Wed, 12 Feb 2020 15:56:33 -0500 Subject: [PATCH 7/7] Update abigen.hpp --- tools/include/eosio/abigen.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/include/eosio/abigen.hpp b/tools/include/eosio/abigen.hpp index 04f7b3b0e9..96a28146b4 100644 --- a/tools/include/eosio/abigen.hpp +++ b/tools/include/eosio/abigen.hpp @@ -496,7 +496,7 @@ namespace eosio { namespace cdt { o["variants"].push_back(variant_to_json( v )); } o["abi_extensions"] = ojson::array(); - if (_abi.version_major == 1 && _abi.version_minor == 2) { + if (_abi.version_major == 1 && _abi.version_minor >= 2) { o["action_results"] = ojson::array(); for ( auto ar : _abi.action_results ) { o["action_results"].push_back(action_result_to_json( ar ));