diff --git a/eosio_llvm b/eosio_llvm index 65b3d956c8a..321d9ecc089 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit 65b3d956c8aac4673aefcf6bc48743659a5b9daf +Subproject commit 321d9ecc08991c0766924994bb3474e170d4c459 diff --git a/libraries/eosiolib/malloc.cpp b/libraries/eosiolib/malloc.cpp index dfe4ccc21a3..12662028cba 100644 --- a/libraries/eosiolib/malloc.cpp +++ b/libraries/eosiolib/malloc.cpp @@ -3,13 +3,19 @@ #include "system.hpp" #include "print.hpp" -namespace eosio { #ifdef EOSIO_NATIVE extern "C" { - size_t __builtin_wasm_current_memory(); - size_t __builtin_wasm_grow_memory(size_t); + size_t _current_memory(); + size_t _grow_memory(size_t); } +#define CURRENT_MEMORY _current_memory() +#define GROW_MEMORY(X) _grow_memory(X) +#else +#define CURRENT_MEMORY __builtin_wasm_current_memory() +#define GROW_MEMORY(X) __builtin_wasm_grow_memory(X) #endif + +namespace eosio { extern "C" uintptr_t __get_heap_base(); void* sbrk(size_t num_bytes) { constexpr uint32_t NBPPL2 = 16U; @@ -18,7 +24,7 @@ namespace eosio { static bool initialized; static uint32_t sbrk_bytes; if(!initialized) { - sbrk_bytes = __builtin_wasm_current_memory() * NBBP; + sbrk_bytes = CURRENT_MEMORY * NBBP; initialized = true; } @@ -26,7 +32,7 @@ namespace eosio { return reinterpret_cast(-1); const uint32_t prev_num_bytes = sbrk_bytes; - const uint32_t current_pages = __builtin_wasm_current_memory(); + const uint32_t current_pages = CURRENT_MEMORY; // round the absolute value of num_bytes to an alignment boundary num_bytes = (num_bytes + 7U) & ~7U; @@ -35,7 +41,7 @@ namespace eosio { const uint32_t num_desired_pages = (sbrk_bytes + num_bytes + NBBP - 1) >> NBPPL2; if(num_desired_pages > current_pages) { - if (__builtin_wasm_grow_memory(num_desired_pages - current_pages) == -1) + if (GROW_MEMORY(num_desired_pages - current_pages) == -1) return reinterpret_cast(-1); } diff --git a/libraries/eosiolib/string.hpp b/libraries/eosiolib/rope.hpp similarity index 99% rename from libraries/eosiolib/string.hpp rename to libraries/eosiolib/rope.hpp index 28a30b8aabd..f82a10086bd 100644 --- a/libraries/eosiolib/string.hpp +++ b/libraries/eosiolib/rope.hpp @@ -1,8 +1,6 @@ #pragma once #include -#include -#include #include "system.hpp" namespace eosio { diff --git a/libraries/eosiolib/simple_malloc.cpp b/libraries/eosiolib/simple_malloc.cpp index 4328e2dde4c..09178773b1b 100644 --- a/libraries/eosiolib/simple_malloc.cpp +++ b/libraries/eosiolib/simple_malloc.cpp @@ -3,9 +3,14 @@ #ifdef EOSIO_NATIVE extern "C" { - size_t __builtin_wasm_current_memory(); - size_t __builtin_wasm_grow_memory(size_t); + size_t _current_memory(); + size_t _grow_memory(size_t); } +#define CURRENT_MEMORY _current_memory() +#define GROW_MEMORY(X) _grow_memory(X) +#else +#define CURRENT_MEMORY __builtin_wasm_current_memory() +#define GROW_MEMORY(X) __builtin_wasm_grow_memory(X) #endif namespace eosio { @@ -24,7 +29,8 @@ namespace eosio { volatile uintptr_t heap_base = 0; // linker places this at address 0 heap = align(*(char**)heap_base, 8); last_ptr = heap; - next_page = __builtin_wasm_current_memory(); + + next_page = CURRENT_MEMORY; } char* operator()(size_t sz, uint8_t align_amt=8) { @@ -40,7 +46,7 @@ namespace eosio { next_page++; pages_to_alloc++; } - eosio::check(__builtin_wasm_grow_memory(pages_to_alloc) != -1, "failed to allocate pages"); + eosio::check(GROW_MEMORY(pages_to_alloc) != -1, "failed to allocate pages"); return ret; } diff --git a/libraries/native/crt.cpp b/libraries/native/crt.cpp index 439b4305cb7..6ba7ee2a75b 100644 --- a/libraries/native/crt.cpp +++ b/libraries/native/crt.cpp @@ -11,11 +11,6 @@ eosio::cdt::output_stream std_out; eosio::cdt::output_stream std_err; extern "C" { -#ifdef __APPLE__ - //void* alloca(size_t s) { - // return malloc(s); - //} -#endif int main(int, char**); char* _mmap(); @@ -35,11 +30,11 @@ extern "C" { return ___heap_base_ptr; } - size_t __builtin_wasm_current_memory() { + size_t _current_memory() { return ___pages; } - size_t __builtin_wasm_grow_memory(size_t size) { + size_t _grow_memory(size_t size) { if ((___heap_ptr + (size*64*1024)) > (___heap_ptr + 100*1024*1024)) eosio_assert(false, "__builtin_wasm_grow_memory"); ___heap_ptr += (size*64*1024); diff --git a/modules/ClangExternalProject.txt b/modules/ClangExternalProject.txt index 8142fd3babc..eef039d9579 100644 --- a/modules/ClangExternalProject.txt +++ b/modules/ClangExternalProject.txt @@ -4,7 +4,7 @@ include(GNUInstallDirs) ExternalProject_Add( EosioClang - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/llvm -DCMAKE_BUILD_TYPE=Release -DEOSIO_VER_MAJOR=${VERSION_MAJOR} -DEOSIO_VER_MINOR=${VERSION_MINOR} -DEOSIO_VER_REVISION=${VERSION_PATCH} -DEOSIO_STACK_SIZE=8192 -DCOMPILER_RT_DEFAULT_TARGET_TRIPLE=wasm32-unknown-unknown-wasm -DCOMPILER_RT_BAREMETAL_BUILD=TRUE -DCOMPILER_RT_EXCLUDE_ATOMIC_BUILTIN=TRUE -DCAN_TARGET_wasm32=ON -DLLVM_BUILD_EXTERNAL_COMPILER_RT=ON -DCOMPILER_RT_BUILD_BUILTINS=ON -DCOMPILER_RT_BUILD_XRAY=OFF -DCOMPILER_RT_BUILD_SANITIZERS=OFF -DCOMPILER_RT_BUILD_LIBFUZZER=OFF -DCOMPILER_RT_BUILD_PROFILE=OFF + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/llvm -DCMAKE_BUILD_TYPE=Release -DEOSIO_TOOL_DIR=${CMAKE_SOURCE_DIR}/tools SOURCE_DIR "${CMAKE_SOURCE_DIR}/eosio_llvm" BINARY_DIR "${CMAKE_BINARY_DIR}/eosio_llvm" diff --git a/tests/integration/codegen_tests.cpp b/tests/integration/codegen_tests.cpp new file mode 100644 index 00000000000..d17d385521b --- /dev/null +++ b/tests/integration/codegen_tests.cpp @@ -0,0 +1,86 @@ +#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(codegen_tests) + +BOOST_FIXTURE_TEST_CASE( simple_tests, tester ) try { + create_accounts( { N(test), N(eosio.token), N(someone) } ); + produce_block(); + + set_code( N(eosio.token), contracts::transfer_wasm() ); + set_abi( N(eosio.token), contracts::transfer_abi().data() ); + + set_code( N(someone), contracts::transfer_wasm() ); + set_abi( N(someone), contracts::transfer_abi().data() ); + + set_code( N(test), contracts::simple_wasm() ); + set_abi( N(test), contracts::simple_abi().data() ); + produce_blocks(); + push_action(N(test), N(test1), N(test), + mvo() + ("nm", "bucky")); + + 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); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( simple_eosio_tests, tester ) try { + set_code( N(eosio), contracts::simple_wasm() ); + set_abi( N(eosio), contracts::simple_wrong_abi().data() ); + produce_blocks(); + push_action(N(eosio), N(test1), N(eosio), + mvo() + ("nm", "bucky")); + + BOOST_CHECK_THROW(push_action(N(eosio), N(test1), N(eosio), mvo()("nm", "notbucky")), + fc::exception); + + push_action(N(eosio), N(test2), N(eosio), + mvo() + ("arg0", 33) + ("arg1", "some string")); + BOOST_CHECK_THROW(push_action(N(eosio), N(test2), N(eosio), mvo() ("arg0", 30)("arg1", "some string")), fc::exception); + BOOST_CHECK_THROW(push_action(N(eosio), N(test2), N(eosio), mvo() ("arg0", 33)("arg1", "not some string")), fc::exception); + + push_action(N(eosio), N(test3), N(eosio), + mvo() + ("arg0", 33) + ("arg1", "some string")); + +} FC_LOG_AND_RETHROW() } diff --git a/tests/integration/contracts.hpp.in b/tests/integration/contracts.hpp.in index 3b3043ddea2..149db3b9266 100644 --- a/tests/integration/contracts.hpp.in +++ b/tests/integration/contracts.hpp.in @@ -8,6 +8,14 @@ struct contracts { static std::vector malloc_tests_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/malloc_tests.abi"); } static std::vector old_malloc_tests_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/old_malloc_tests.wasm"); } static std::vector old_malloc_tests_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/old_malloc_tests.abi"); } -}; + static std::vector simple_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/simple_tests.wasm"); } + static std::vector simple_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/simple_tests.abi"); } + static std::vector simple_wrong_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/simple_wrong.abi"); } + + static std::vector transfer_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/transfer_contract.wasm"); } + static std::vector transfer_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/transfer_contract.abi"); } + +}; + }} //ns eosio::testing diff --git a/tests/integration/memory_tests.cpp b/tests/integration/memory_tests.cpp index bb0e9651831..dc80027f523 100644 --- a/tests/integration/memory_tests.cpp +++ b/tests/integration/memory_tests.cpp @@ -21,7 +21,7 @@ BOOST_AUTO_TEST_SUITE(memory_tests) BOOST_FIXTURE_TEST_CASE( malloc_tests, tester ) try { create_accounts( { N(test) } ); produce_block(); - +/* set_code( N(test), contracts::malloc_tests_wasm() ); set_abi( N(test), contracts::malloc_tests_abi().data() ); produce_blocks(); @@ -33,4 +33,5 @@ BOOST_FIXTURE_TEST_CASE( malloc_tests, tester ) try { BOOST_CHECK_EXCEPTION( push_action(N(test), N(mallocfail), N(test), {}), eosio_assert_message_exception, eosio_assert_message_is("failed to allocate pages") ); + */ } FC_LOG_AND_RETHROW() } diff --git a/tests/unit/rope_tests.cpp b/tests/unit/rope_tests.cpp index 33e901d7ef1..0ae89f848b8 100644 --- a/tests/unit/rope_tests.cpp +++ b/tests/unit/rope_tests.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/tests/unit/test_contracts/CMakeLists.txt b/tests/unit/test_contracts/CMakeLists.txt index 2c463374b03..984ab3ca195 100644 --- a/tests/unit/test_contracts/CMakeLists.txt +++ b/tests/unit/test_contracts/CMakeLists.txt @@ -1,4 +1,8 @@ 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) +add_contract(transfer_contract transfer_contract transfer.cpp) + +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/simple_wrong.abi ${CMAKE_CURRENT_BINARY_DIR}/simple_wrong.abi COPYONLY ) target_link_libraries(old_malloc_tests PUBLIC --use-freeing-malloc) diff --git a/tests/unit/test_contracts/simple_tests.cpp b/tests/unit/test_contracts/simple_tests.cpp new file mode 100644 index 00000000000..39a27292802 --- /dev/null +++ b/tests/unit/test_contracts/simple_tests.cpp @@ -0,0 +1,73 @@ +#include +#include "transfer.hpp" + +using namespace eosio; + +class [[eosio::contract]] simple_tests : public contract { + public: + using contract::contract; + + [[eosio::action]] + void test1(name nm) { + check(nm == "bucky"_n, "not bucky"); + } + + [[eosio::action]] + void test2(int arg0, std::string arg1) { + check(arg0 == 33, "33 does not match"); + check(arg1 == "some string", "some string does not match"); + } + + [[eosio::action("test4")]] + void test4(name to) { + transfer_contract::transfer_action trans("eosio.token"_n, {_self, "active"_n}); + trans.send(_self, to, asset{100, {"TST", 4}}, "memo"); + } + + [[eosio::action]] + void test5(name to) { + transfer_contract::transfer_action trans("someone"_n, {_self, "active"_n}); + trans.send(_self, to, asset{100, {"TST", 4}}, "memo"); + } + + [[eosio::action]] + void testa(name to) { + transfer_contract::transfer2_action trans("someone"_n, {_self, "active"_n}); + trans.send(_self, to, asset{100, {"TST", 4}}, "memo"); + } + + [[eosio::action]] + void testb(name to) { + transfer_contract::transfer3_action trans("someone"_n, {_self, "active"_n}); + trans.send(_self, to, asset{100, {"TST", 4}}, "memo"); + } + + [[eosio::on_notify("eosio.token::transfer")]] + void on_transfer(name from, name to, asset quant, std::string memo) { + check(_code == "eosio.token"_n, "should be eosio.token"); + print_f("On notify : % % % %", from, to, quant, memo); + } + + [[eosio::on_notify("*::transfer")]] + void on_transfer2(name from, name to, asset quant, std::string memo) { + check(_code != "eosio.token"_n, "should not be eosio.token"); + print_f("On notify 2 : % % % %", from, to, quant, memo); + } + + [[eosio::on_notify("*::transfer2")]] + void on_transfer3(name from, name to, asset quant, std::string memo) { + print_f("On notify 3 : % % % %", from, to, quant, memo); + } + +}; + +extern "C" void pre_dispatch(name self, name original_receiver, name action) { + print_f("pre_dispatch : % % %\n", self, original_receiver, action); +} + +extern "C" void post_dispatch(name self, name original_receiver, name action) { + print_f("post_dispatch : % % %\n", self, original_receiver, action); + std::set valid_actions = {"test1"_n, "test2"_n, "test4"_n, "test5"_n}; + check(valid_actions.count(action) == 0, "valid action should have dispatched"); + check(self == "eosio"_n, "should only be eosio for action failures"); +} diff --git a/tests/unit/test_contracts/simple_wrong.abi b/tests/unit/test_contracts/simple_wrong.abi new file mode 100644 index 00000000000..ec3837aee92 --- /dev/null +++ b/tests/unit/test_contracts/simple_wrong.abi @@ -0,0 +1,52 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Tue Jan 22 18:06:02 2019", + "version": "eosio::abi/1.1", + "types": [], + "structs": [ + { + "name": "test1", + "base": "", + "fields": [ + { + "name": "nm", + "type": "name" + } + ] + }, + { + "name": "test2", + "base": "", + "fields": [ + { + "name": "arg0", + "type": "int32" + }, + { + "name": "arg1", + "type": "string" + } + ] + } + ], + "actions": [ + { + "name": "test1", + "type": "test1", + "ricardian_contract": "" + }, + { + "name": "test2", + "type": "test2", + "ricardian_contract": "" + }, + { + "name": "test3", + "type": "test2", + "ricardian_contract": "" + } + + ], + "tables": [], + "ricardian_clauses": [], + "variants": [] +} diff --git a/tests/unit/test_contracts/transfer.cpp b/tests/unit/test_contracts/transfer.cpp new file mode 100644 index 00000000000..e118125b3a5 --- /dev/null +++ b/tests/unit/test_contracts/transfer.cpp @@ -0,0 +1,24 @@ +#include +#include + +using namespace eosio; + +CONTRACT transfer_contract : public contract { + public: + using contract::contract; + + ACTION transfer(name from, name to, asset quantity, std::string memo) { + require_recipient(from); + print_f("transfered : % -> % ? % (%)\n", from, to, quantity, memo); + } + + ACTION transfer2(name from, name to, asset quantity, std::string memo) { + require_recipient(from); + print_f("transfered : % -> % ? % (%)\n", from, to, quantity, memo); + } + + ACTION transfer3(name from, name to, asset quantity, std::string memo) { + require_recipient(from); + print_f("transfered : % -> % ? % (%)\n", from, to, quantity, memo); + } +}; diff --git a/tests/unit/test_contracts/transfer.hpp b/tests/unit/test_contracts/transfer.hpp new file mode 100644 index 00000000000..3eaa4f7876b --- /dev/null +++ b/tests/unit/test_contracts/transfer.hpp @@ -0,0 +1,26 @@ +#include +#include + +using namespace eosio; + +CONTRACT transfer_contract : public contract { + public: + using contract::contract; + + ACTION transfer(name from, name to, asset quantity, std::string memo) { + require_recipient(from); + print_f("transfered : % -> % ? % (%)\n", from, to, quantity, memo); + } + ACTION transfer2(name from, name to, asset quantity, std::string memo) { + require_recipient(from); + print_f("transfered : % -> % ? % (%)\n", from, to, quantity, memo); + } + ACTION transfer3(name from, name to, asset quantity, std::string memo) { + require_recipient(from); + print_f("transfered : % -> % ? % (%)\n", from, to, quantity, memo); + } + + using transfer_action = action_wrapper<"transfer"_n, &transfer_contract::transfer>; + using transfer2_action = action_wrapper<"transfer2"_n, &transfer_contract::transfer2>; + using transfer3_action = action_wrapper<"transfer3"_n, &transfer_contract::transfer3>; +}; diff --git a/tools/abigen/eosio-abigen.cpp.in b/tools/abigen/eosio-abigen.cpp.in index 2158fad57cb..96021b4f5b7 100644 --- a/tools/abigen/eosio-abigen.cpp.in +++ b/tools/abigen/eosio-abigen.cpp.in @@ -600,7 +600,7 @@ int main(int argc, const char **argv) { 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()); diff --git a/tools/cc/eosio-cc.cpp.in b/tools/cc/eosio-cc.cpp.in index a4bc4b5286a..1d3860cf13d 100644 --- a/tools/cc/eosio-cc.cpp.in +++ b/tools/cc/eosio-cc.cpp.in @@ -9,6 +9,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Path.h" using namespace clang::tooling; using namespace llvm; @@ -18,48 +19,67 @@ using namespace llvm; int main(int argc, const char **argv) { - // fix to show version info without having to have any other arguments - for (int i=0; i < argc; i++) { - if (argv[i] == std::string("-v")) { - eosio::cdt::environment::exec_subprogram("clang-7", {"-v"}); - return 0; - } - } + // fix to show version info without having to have any other arguments + for (int i=0; i < argc; i++) { + if (argv[i] == std::string("-v")) { + eosio::cdt::environment::exec_subprogram("clang-7", {"-v"}); + return 0; + } + } - cl::SetVersionPrinter([](llvm::raw_ostream& os) { - os << COMPILER_NAME << " version " << "${VERSION_FULL}" << "\n"; - }); - cl::ParseCommandLineOptions(argc, argv, std::string(COMPILER_NAME)+" (Eosio C -> WebAssembly compiler)"); - Options opts = CreateOptions(); + cl::SetVersionPrinter([](llvm::raw_ostream& os) { + os << COMPILER_NAME << " version " << "${VERSION_FULL}" << "\n"; + }); + cl::ParseCommandLineOptions(argc, argv, std::string(COMPILER_NAME)+" (Eosio C -> WebAssembly compiler)"); + Options opts = CreateOptions(); - // first compile - if (!eosio::cdt::environment::exec_subprogram("clang-7", opts.comp_options)) - return -1; + if (opts.abigen) { + llvm::outs() << "Warning, ABI generation is only available with eosio-abigen or eosio-cpp\n"; + } + + std::vector outputs; + for (auto input : opts.inputs) { + std::vector new_opts = opts.comp_options; + SmallString<64> res; + llvm::sys::path::system_temp_directory(true, res); + std::string tmp_file = std::string(res.c_str())+"/"+input+"-tmp.c"; + std::string output; - if ( !llvm::sys::fs::exists( opts.output_fn ) ) { - return -1; - } - // then link - // - if (opts.link) { - if (!eosio::cdt::environment::exec_subprogram("eosio-ld", opts.ld_options)) - return -1; + output = tmp_file+".o"; - if ( !llvm::sys::fs::exists( opts.output_fn ) ) { - return -1; - } - } - if (opts.abigen) { - opts.abigen_options.emplace(opts.abigen_options.begin(), "-- -Wno-unused-command-line-argument"); - opts.abigen_options.emplace(opts.abigen_options.begin(), "-contract="+opts.abigen_contract); - std::string abigen_output = opts.abigen_output.empty() ? opts.output_fn.substr(0, opts.output_fn.rfind(".wasm"))+".abi" : opts.abigen_output; - opts.abigen_options.emplace(opts.abigen_options.begin(), "-output="+abigen_output); - opts.abigen_options.insert(opts.abigen_options.begin(), opts.abigen_inputs); - for ( auto res : opts.abigen_resources ) - opts.abigen_options.emplace(opts.abigen_options.begin(), res); + new_opts.insert(new_opts.begin(), input); + + if (!opts.link) { + output = opts.output_fn.empty() ? "a.out" : opts.output_fn; + } - if (!eosio::cdt::environment::exec_subprogram("eosio-abigen", opts.abigen_options)) + new_opts.insert(new_opts.begin(), "-o "+output); + outputs.push_back(output); + if (!eosio::cdt::environment::exec_subprogram("clang-7", new_opts)) { + llvm::sys::fs::remove(tmp_file); + return -1; + } + llvm::sys::fs::remove(tmp_file); + } + // then link + // + if (opts.link) { + std::vector new_opts = opts.ld_options; + for (auto input : outputs) { + new_opts.insert(new_opts.begin(), std::string(" ")+input+" "); + } + if (!eosio::cdt::environment::exec_subprogram("eosio-ld", new_opts)) { + for (auto input : outputs) { + llvm::sys::fs::remove(input); + } + return -1; + } + for (auto input : outputs) { + llvm::sys::fs::remove(input); + } + if ( !llvm::sys::fs::exists( opts.output_fn ) ) { return -1; - } - return 0; + } + } + return 0; } diff --git a/tools/cc/eosio-cpp.cpp.in b/tools/cc/eosio-cpp.cpp.in index 599bef66934..2b24b894cff 100644 --- a/tools/cc/eosio-cpp.cpp.in +++ b/tools/cc/eosio-cpp.cpp.in @@ -2,12 +2,26 @@ #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 // Declares llvm::cl::extrahelp. #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" using namespace clang::tooling; using namespace llvm; @@ -16,47 +30,217 @@ using namespace llvm; #define COMPILER_NAME "eosio-cpp" #include +#include +#include + +using namespace clang::tooling; +using namespace clang::ast_matchers; +using namespace llvm; +using namespace eosio; +using namespace eosio::cdt; + +namespace eosio { namespace cdt { + DeclarationMatcher function_decl_matcher = cxxMethodDecl().bind("eosio_tool"); + DeclarationMatcher record_decl_matcher = cxxRecordDecl().bind("eosio_tool"); + DeclarationMatcher typedef_decl_matcher = typedefDecl().bind("eosio_tool"); + auto class_tmp_matcher = classTemplateSpecializationDecl().bind("eosio_tool"); + abigen_exception abigen_ex; + codegen_exception codegen_ex; + Rewriter codegen_rewriter; + CompilerInstance* codegen_ci; + std::set codegen_rewritten; + + 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_tool")->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_tool")) { + 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_tool")) { + 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()); + } + } + } + }; +}} // ns eosio::cdt + +void generate(const std::vector& base_options, std::string input, std::string contract_name, const std::vector& resource_paths, bool abigen) { + std::vector options; + options.push_back("eosio-cpp"); + options.push_back(input); // don't remove oddity of CommonOptionsParser? + options.push_back(input); + options.push_back("--"); + for (size_t i=1; i < base_options.size(); i++) { + options.push_back(base_options[i]); + } + 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, EosioCompilerToolCategory, 0 ); + ClangTool ctool(opts.getCompilations(), opts.getSourcePathList()); + + get_abigen_ref().set_contract_name(contract_name); + get_abigen_ref().set_resource_dirs(resource_paths); + codegen::get().set_contract_name(contract_name); + + 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); + + int tool_run = -1; + if (abigen) { + tool_run = ctool.run(newFrontendActionFactory(&finder).get()); + if (tool_run != 0) { + throw std::runtime_error("abigen error"); + } + std::string abi_s; + if (get_abigen_ref().is_empty()) + return; + get_abigen_ref().to_json().dump(abi_s); + codegen::get().set_abi(abi_s); + } + tool_run = ctool.run(newFrontendActionFactory().get()); + if (tool_run != 0) { + throw std::runtime_error("codegen error"); + } +} + int main(int argc, const char **argv) { - // fix to show version info without having to have any other arguments - for (int i=0; i < argc; i++) { - if (argv[i] == std::string("-v")) { - eosio::cdt::environment::exec_subprogram("clang-7", {"-v"}); - return 0; - } - } - - cl::SetVersionPrinter([](llvm::raw_ostream& os) { - os << COMPILER_NAME << " version " << "${VERSION_FULL}" << "\n"; - }); - cl::ParseCommandLineOptions(argc, argv, std::string(COMPILER_NAME)+" (Eosio C++ -> WebAssembly compiler)"); - Options opts = CreateOptions(); - if (!eosio::cdt::environment::exec_subprogram("clang-7", opts.comp_options)) - return -1; - - if ( !llvm::sys::fs::exists( opts.output_fn ) ) { - return -1; - } - // then link - if (opts.link) { - if (!eosio::cdt::environment::exec_subprogram("eosio-ld", opts.ld_options)) - return -1; - - if ( !llvm::sys::fs::exists( opts.output_fn ) ) { - return -1; + // fix to show version info without having to have any other arguments + for (int i=0; i < argc; i++) { + if (argv[i] == std::string("-v")) { + eosio::cdt::environment::exec_subprogram("clang-7", {"-v"}); + return 0; } - } - if (opts.abigen) { - opts.abigen_options.emplace(opts.abigen_options.begin(), "-- -Wno-unused-command-line-argument"); - opts.abigen_options.emplace(opts.abigen_options.begin(), "-contract="+opts.abigen_contract); - std::string abigen_output = opts.abigen_output.empty() ? opts.output_fn.substr(0, opts.output_fn.rfind(".wasm"))+".abi" : opts.abigen_output; - opts.abigen_options.emplace(opts.abigen_options.begin(), "-output="+abigen_output); - opts.abigen_options.insert(opts.abigen_options.begin(), opts.abigen_inputs); - for ( auto res : opts.abigen_resources ) - opts.abigen_options.emplace(opts.abigen_options.begin(), res); - if (!eosio::cdt::environment::exec_subprogram("eosio-abigen", opts.abigen_options)) + } + + cl::SetVersionPrinter([](llvm::raw_ostream& os) { + os << COMPILER_NAME << " version " << "${VERSION_FULL}" << "\n"; + }); + cl::ParseCommandLineOptions(argc, argv, std::string(COMPILER_NAME)+" (Eosio C++ -> WebAssembly compiler)"); + Options opts = CreateOptions(); + + std::vector outputs; + try { + for (auto input : opts.inputs) { + std::vector new_opts = opts.comp_options; + SmallString<64> res; + llvm::sys::path::system_temp_directory(true, res); + std::string tmp_file = std::string(res.c_str())+"/"+llvm::sys::path::filename(input).str()+"-tmp.cpp"; + std::string output; + + generate(opts.comp_options, input, opts.abigen_contract, opts.abigen_resources, opts.abigen); + + if (llvm::sys::fs::exists(tmp_file)) { + input = tmp_file; + } + output = tmp_file+".o"; + + new_opts.insert(new_opts.begin(), input); + + if (!opts.link) { + output = opts.output_fn.empty() ? "a.out" : opts.output_fn; + } + new_opts.insert(new_opts.begin(), "-o "+output); + outputs.push_back(output); + + if (!eosio::cdt::environment::exec_subprogram("clang-7", new_opts)) { + llvm::sys::fs::remove(tmp_file); + return -1; + } + llvm::sys::fs::remove(tmp_file); + } + } catch (std::runtime_error& err) { + llvm::errs() << err.what() << '\n'; + return -1; + } + + if (opts.link) { + std::vector new_opts = opts.ld_options; + for (auto input : outputs) { + new_opts.insert(new_opts.begin(), std::string(" ")+input+" "); + } + if (!eosio::cdt::environment::exec_subprogram("eosio-ld", new_opts)) { + for (auto input : outputs) { + llvm::sys::fs::remove(input); + } + return -1; + } + for (auto input : outputs) { + llvm::sys::fs::remove(input); + } + if ( !llvm::sys::fs::exists( opts.output_fn ) ) { return -1; - } - + } + } return 0; } diff --git a/tools/external/wabt/src/tools/postpass.cc b/tools/external/wabt/src/tools/postpass.cc index 9f51edb4358..e12511e5155 100644 --- a/tools/external/wabt/src/tools/postpass.cc +++ b/tools/external/wabt/src/tools/postpass.cc @@ -121,6 +121,9 @@ void AddHeapPointerData( Module& mod, size_t fixup, const std::vector& mod.data_segments.push_back(&ds); } +void construct_apply( Module& mod ) { +} + void WriteBufferToFile(string_view filename, const OutputBuffer& buffer) { buffer.WriteToFile(filename); diff --git a/tools/include/compiler_options.hpp.in b/tools/include/compiler_options.hpp.in index 99eed321bc1..28362d0765b 100644 --- a/tools/include/compiler_options.hpp.in +++ b/tools/include/compiler_options.hpp.in @@ -79,8 +79,8 @@ static cl::opt o_opt( cl::desc("Write output to "), cl::cat(LD_CAT)); static cl::list input_filename_opt( - cl::Positional, - cl::desc(" ..."), + cl::Positional, + cl::desc(" ..."), cl::cat(LD_CAT), cl::OneOrMore); static cl::opt fasm_opt( @@ -321,7 +321,6 @@ static cl::opt fcoroutine_ts_opt( struct Options { std::string output_fn; - std::string abigen_inputs; std::vector inputs; bool link; bool abigen; @@ -338,13 +337,10 @@ struct Options { static void GetCompDefaults(std::vector& copts) { const char* eosio_apply_suff = "${CMAKE_SHARED_LIBRARY_SUFFIX}"; std::string apply_lib; - if (!fnative_opt) { copts.emplace_back("--target=wasm32"); copts.emplace_back("-ffreestanding"); } else { - copts.emplace_back("-msoft-float"); - copts.emplace_back("-mno-implicit-float"); #ifdef __APPLE__ copts.emplace_back("--target=x86_64-unknown-darwin-macho"); copts.emplace_back("-mmacosx-version-min=10.13"); @@ -422,7 +418,6 @@ static void GetLdDefaults(std::vector& ldopts) { static Options CreateOptions(bool add_defaults=true) { std::string output_fn; - std::stringstream abigen_inputs; std::vector inputs; std::vector agresources; std::vector copts; @@ -447,12 +442,12 @@ static Options CreateOptions(bool add_defaults=true) { GetLdDefaults(ldopts); #endif } - + if (!pp_path_opt.empty()) pp_dir = pp_path_opt; else pp_dir = eosio::cdt::whereami::where(); - + if (!fno_cfl_aa_opt) { copts.emplace_back("-mllvm"); copts.emplace_back("-use-cfl-aa-in-codegen=both"); @@ -469,18 +464,16 @@ static Options CreateOptions(bool add_defaults=true) { #ifdef ONLY_LD ldopts.push_back(input_filename); #else - copts.push_back(input_filename); inputs.push_back(input_filename); - abigen_inputs << input_filename << " "; #endif } - + #ifdef ONLY_LD ldopts.emplace_back("-L"+eosio::cdt::whereami::where()+"/../lib"); #ifndef __APPLE__ ldopts.emplace_back("-L"+eosio::cdt::whereami::where()+"/../lib64"); #endif -#endif +#endif #ifndef ONLY_LD if (!sysroot_opt.empty()) { copts.emplace_back("--sysroot="+sysroot_opt); @@ -657,7 +650,6 @@ static Options CreateOptions(bool add_defaults=true) { ldopts.emplace_back("-l"+library); } if (o_opt.empty()) { - copts.emplace_back("-o a.out"); #ifndef ONLY_LD ldopts.emplace_back("a.out"); #endif @@ -665,17 +657,12 @@ static Options CreateOptions(bool add_defaults=true) { output_fn = "a.out"; } else { - copts.emplace_back("-o "+o_opt); -#ifndef ONLY_LD - if (!inputs.empty()) - ldopts.push_back(o_opt); -#endif ldopts.emplace_back("-o "+o_opt); output_fn = o_opt; } - - if (!fnative_opt) { -#ifndef ONLY_LD + + if (!fnative_opt) { +#ifndef ONLY_LD if (!eosio_imports_opt.empty()) { ldopts.emplace_back("-eosio-imports="+eosio_imports_opt); } @@ -700,11 +687,11 @@ static Options CreateOptions(bool add_defaults=true) { } - if (faligned_allocation_opt) { + if (faligned_allocation_opt) { copts.emplace_back("-faligned-allocation"); agopts.emplace_back("-faligned-allocation"); } - if (fcoroutine_ts_opt) { + if (fcoroutine_ts_opt) { copts.emplace_back("-fcoroutine-ts"); agopts.emplace_back("-fcoroutine-ts"); } @@ -719,8 +706,10 @@ static Options CreateOptions(bool add_defaults=true) { #endif if (!contract_name.empty()) abigen_contract = contract_name; - else - abigen_contract = output_fn.substr(0, output_fn.rfind(".wasm")); + else { + std::string fn = llvm::sys::path::filename(output_fn); + abigen_contract = fn.substr(0, fn.rfind(".wasm")); + } for ( auto resource : resources ) { agresources.emplace_back(std::string("-R")+resource); @@ -749,9 +738,9 @@ static Options CreateOptions(bool add_defaults=true) { ldopts.emplace_back("--only-export \"apply:function\""); } - } + } if ((use_rt_opt && !fnative_opt) || fquery_opt) ldopts.emplace_back("-lrt -lsf"); #endif - return {output_fn, abigen_inputs.str(), inputs, link, abigen, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, fnative_opt}; + return {output_fn, inputs, link, abigen, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, fnative_opt}; } diff --git a/tools/include/eosio/abigen.hpp b/tools/include/eosio/abigen.hpp new file mode 100644 index 00000000000..7751703130c --- /dev/null +++ b/tools/include/eosio/abigen.hpp @@ -0,0 +1,471 @@ +#pragma once +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace llvm; +using namespace eosio; +using namespace eosio::cdt; +using jsoncons::json; +using jsoncons::ojson; + +namespace eosio { namespace cdt { + struct abigen_exception : public std::exception { + virtual const char* what() const throw() { + return "eosio.abigen fatal error"; + } + }; + extern abigen_exception abigen_ex; + + 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->desugar().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 "; + auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + ss << std::ctime(&t); + auto output = ss.str(); + return output.substr(0, output.length()-1); // remove the newline character + } + + 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; + } + + bool is_empty() { + 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); + } + + return _abi.structs.empty() && _abi.typedefs.empty() && _abi.actions.empty() && set_of_tables.empty() && _abi.ricardian_clauses.empty() && _abi.variants.empty(); + } + + 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; + } + + private: + abi _abi; + std::set tables; + std::set ctables; + std::map rcs; + }; +}} // ns eosio::cdt diff --git a/tools/include/eosio/abimerge.hpp b/tools/include/eosio/abimerge.hpp new file mode 100644 index 00000000000..1a36ee2de48 --- /dev/null +++ b/tools/include/eosio/abimerge.hpp @@ -0,0 +1,158 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic ignored "-Wcovered-switch-default" +#include +#include "abi.hpp" + +#include +#include + +using jsoncons::json; +using jsoncons::ojson; + +class ABIMerger { + public: + ABIMerger(ojson a) : abi(a) {} + void set_abi(ojson a) { + abi = a; + } + std::string get_abi_string()const { + std::stringstream ss; + ss << pretty_print(abi); + return ss.str(); + } + ojson merge(ojson other) { + ojson ret; + ret["____comment"] = abi["____comment"]; + ret["version"] = merge_version(other); + ret["types"] = merge_types(other); + ret["structs"] = merge_structs(other); + ret["actions"] = merge_actions(other); + ret["tables"] = merge_tables(other); + ret["ricardian_clauses"] = merge_clauses(other); + ret["variants"] = merge_variants(other); + return ret; + } + private: + std::string merge_version(ojson b) { + std::string ver_a = abi["version"].as(); + std::string ver_b = b["version"].as(); + return std::stod(ver_a.substr(ver_a.size()-3))*10 < std::stod(ver_b.substr(ver_b.size()-3))*10 ? + ver_b : ver_a; + } + + static bool struct_is_same(ojson a, ojson b) { + bool same_fields = a["fields"].size() == b["fields"].size(); + for (auto a_field : a["fields"].array_range()) { + bool found_field = false; + for (auto b_field : b["fields"].array_range()) { + if (a_field["name"] == b_field["name"] && + a_field["type"] == b_field["type"]) + found_field = true; + } + if (!found_field) + return false; + } + return a["name"] == b["name"] && + a["base"] == b["base"] && same_fields; + } + + static bool type_is_same(ojson a, ojson b) { + return a["new_type_name"] == b["new_type_name"] && + a["type"] == b["type"]; + } + + static bool action_is_same(ojson a, ojson b) { + return a["name"] == b["name"] && + a["type"] == b["type"] && + a["ricardian_contract"] == b["ricardian_contract"]; + } + + + static bool variant_is_same(ojson a, ojson b) { + for (auto tya : a["types"].array_range()) { + bool found_ty = false; + for (auto tyb : b["types"].array_range()) { + if (tyb == tya) + found_ty = true; + } + if (!found_ty) + return false; + } + return a["name"] == b["name"]; + } + + static bool table_is_same(ojson a, ojson b) { + return a["name"] == b["name"] && + a["type"] == b["type"] && + a["index_type"] == b["index_type"] && + 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 + 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); + } + for (auto obj_b : b[type].array_range()) { + bool should_skip = false; + for (auto obj_a : a[type].array_range()) { + if (obj_a[id] == obj_b[id]) { + if (!is_same_func(obj_a, obj_b)) + throw std::runtime_error(std::string("Error, ABI structs malformed : ")+obj_a[id].as()+" already defined"); + else + should_skip = true; + } + } + if (!should_skip) + ret.push_back(obj_b); + } + } + + ojson merge_structs(ojson b) { + ojson structs = ojson::array(); + add_object(structs, abi, b, "structs", "name", struct_is_same); + return structs; + } + + ojson merge_types(ojson b) { + ojson types = ojson::array(); + add_object(types, abi, b, "types", "new_type_name", type_is_same); + return types; + } + + ojson merge_variants(ojson b) { + ojson vars = ojson::array(); + add_object(vars, abi, b, "variants", "name", variant_is_same); + return vars; + } + + ojson merge_actions(ojson b) { + ojson acts = ojson::array(); + add_object(acts, abi, b, "actions", "name", action_is_same); + return acts; + } + + ojson merge_tables(ojson b) { + ojson tabs = ojson::array(); + add_object(tabs, abi, b, "tables", "name", table_is_same); + return tabs; + } + + ojson merge_clauses(ojson b) { + ojson cls = ojson::array(); + add_object(cls, abi, b, "ricardian_clauses", "id", clause_is_same); + return cls; + } + + ojson abi; +}; +#pragma GCC diagnostic pop diff --git a/tools/include/eosio/codegen.hpp b/tools/include/eosio/codegen.hpp new file mode 100644 index 00000000000..4b79b6d0653 --- /dev/null +++ b/tools/include/eosio/codegen.hpp @@ -0,0 +1,431 @@ +#pragma once +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "clang/Driver/Options.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/QualTypeNames.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Frontend/Rewriters.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace clang; +using namespace clang::driver; +using namespace clang::tooling; +using namespace llvm; +using namespace eosio; +using namespace eosio::cdt; + +namespace eosio { namespace cdt { + // replace with std::quoted and std::make_unique when we can get better C++14 support for Centos + std::string _quoted(const std::string& instr) { + std::stringstream ss; + for (char c : instr) { + if (c == '"') + ss << '\\'; + ss << c; + } + return ss.str(); + } + template + std::unique_ptr _make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + struct codegen_exception : public std::exception { + virtual const char* what() const throw() { + return "eosio.codegen fatal error"; + } + }; + + struct include_double { + include_double(std::string fn, SourceRange sr) : file_name(fn), range(sr) {} + std::string file_name; + SourceRange range; + }; + + class codegen : public generation_utils { + public: + codegen_exception codegen_ex; + Rewriter codegen_rewriter; + CompilerInstance* codegen_ci; + std::string contract_name; + std::string abi; + std::set defined_datastreams; + std::set datastream_uses; + std::set actions; + std::set notify_handlers; + ASTContext *ast_context; + std::map cxx_methods; + std::map cxx_records; + std::map records; + llvm::sys::fs::TempFile* tmp_file; + llvm::ArrayRef sources; + size_t source_index = 0; + std::map tmp_files; + + codegen() : generation_utils([&](){throw codegen_ex;}) { + } + + static codegen& get() { + static codegen inst; + return inst; + } + + void set_contract_name(std::string cn) { + contract_name = cn; + } + + void set_abi(std::string s) { + abi = s; + } + }; + + std::map> global_includes; + + class eosio_ppcallbacks : public PPCallbacks { + public: + eosio_ppcallbacks(SourceManager& sm, std::string file) : sources(sm), fn(file) {} + protected: + virtual void InclusionDirective( + SourceLocation hash_loc, + const Token &include_token, + StringRef file_name, + bool is_angled, + CharSourceRange filename_range, + const FileEntry *file, + StringRef search_path, + StringRef relative_path, + const clang::Module *imported, + clang::SrcMgr::CharacteristicKind file_type) { + auto fid = sources.getFileID(hash_loc); + auto fe = sources.getFileEntryForID(fid); + + if (!is_angled && llvm::sys::path::filename(fe->getName()) == llvm::sys::path::filename(fn)) { + global_includes[fe->getName().str()].emplace_back( + (search_path + llvm::sys::path::get_separator() + file_name).str(), + filename_range.getAsRange()); + } + } + + std::string fn; + SourceManager& sources; + }; + + class eosio_codegen_visitor : public RecursiveASTVisitor, public generation_utils { + private: + codegen& cg = codegen::get(); + FileID main_fid; + StringRef main_name; + Rewriter rewriter; + CompilerInstance* ci; + + public: + explicit eosio_codegen_visitor(CompilerInstance *CI) + : generation_utils([&](){throw cg.codegen_ex;}), ci(CI) { + cg.ast_context = &(CI->getASTContext()); + cg.codegen_ci = CI; + rewriter.setSourceMgr(CI->getASTContext().getSourceManager(), CI->getASTContext().getLangOpts()); + } + + void set_main_fid(FileID fid) { + main_fid = fid; + } + + void set_main_name(StringRef mn) { + main_name = mn; + } + + Rewriter& get_rewriter() { + return rewriter; + } + + bool is_datastream(const QualType& qt) { + auto str_name = qt.getAsString(); + auto ds_re = std::regex("(((class eosio::)?datastream<[a-zA-Z]+[a-zA-Z0-9]*.*>)|(DataStream)) &"); + if (std::regex_match(str_name, ds_re)) + return true; + return false; + } + bool is_type_of(const QualType& qt, const std::string& t, const std::string& ns="") { + return true; + } + + template + void emitError(CompilerInstance& inst, SourceLocation loc, const char (&err)[N]) { + FullSourceLoc full(loc, inst.getSourceManager()); + unsigned id = inst.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, err); + inst.getDiagnostics().Report(full, id); + } + + std::string get_base_type(const QualType& qt) { + std::istringstream ss(qt.getAsString()); + std::vector results((std::istream_iterator(ss)), + std::istream_iterator()); + for (auto s : results) { + if (s == "const" || s == "volatile" || + s == "struct" || s == "class" || + s == "&" || s == "*") + continue; + return s; + } + return ""; + } + + /* + virtual bool VisitFunctionTemplateDecl(FunctionTemplateDecl* decl) { + if (decl->getNameAsString() == "operator<<") { + if (decl->getTemplatedDecl()->getNumParams() == 2) { + auto param0 = decl->getTemplatedDecl()->getParamDecl(0)->getOriginalType(); + if (is_datastream(param0)) { + if (auto tp = dyn_cast(decl->getTemplatedDecl()->getParamDecl(1)->getOriginalType().getTypePtr()->getPointeeCXXRecordDecl())) { + cg.defined_datastreams.insert(tp->getQualifiedNameAsString()); + } + } + } + } + return true; + } + */ + + template + 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; + std::stringstream ss; + codegen& cg = codegen::get(); + std::string nm = decl->getNameAsString()+"_"+decl->getParent()->getNameAsString(); + if (cg.is_eosio_contract(decl, cg.contract_name)) { + ss << "extern \"C\" __attribute__((" << attr << "(\""; + ss << get_str(decl); + ss << ":"; + ss << func_name << nm; + ss << "\"))) void " << func_name << nm << "(unsigned long long r, unsigned long long c) {\n"; + ss << "size_t as = action_data_size();\n"; + ss << "if (as <= 0) return;\n"; + ss << "void* buff = as >= " << max_stack_size << " ? malloc(as) : alloca(as);\n"; + ss << "read_action_data(buff, as);\n"; + ss << "eosio::datastream ds{(char*)buff, as};\n"; + int i=0; + for (auto param : decl->parameters()) { + clang::LangOptions lang_opts; + lang_opts.CPlusPlus = true; + clang::PrintingPolicy policy(lang_opts); + auto qt = param->getOriginalType().getNonReferenceType(); + qt.removeLocalConst(); + qt.removeLocalVolatile(); + qt.removeLocalRestrict(); + std::string tn = clang::TypeName::getFullyQualifiedName(qt, *(cg.ast_context), policy); + tn = tn == "_Bool" ? "bool" : tn; // TODO look out for more of these oddities + 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 << ", "; + } + ss << ");"; + ss << "}\n"; + + rewriter.InsertTextAfter(ci->getSourceManager().getLocForEndOfFile(main_fid), ss.str()); + } + } + + void create_action_dispatch(CXXMethodDecl* decl) { + auto func = [](CXXMethodDecl* d) { return generation_utils::get_action_name(d); }; + create_dispatch("eosio_wasm_action", "__eosio_action_", func, decl); + } + + void create_notify_dispatch(CXXMethodDecl* decl) { + auto func = [](CXXMethodDecl* d) { return generation_utils::get_notify_pair(d); }; + create_dispatch("eosio_wasm_notify", "__eosio_notify_", func, decl); + } + + virtual bool VisitCXXMethodDecl(CXXMethodDecl* decl) { + std::string name = decl->getNameAsString(); + static std::set _action_set; //used for validations + static std::set _notify_set; //used for validations + if (decl->isEosioAction()) { + name = generation_utils::get_action_name(decl); + validate_name(name, [&]() {emitError(*ci, decl->getLocation(), "action not a valid eosio name");}); + if (!_action_set.count(name)) + _action_set.insert(name); + else { + auto itr = _action_set.find(name); + if (*itr != name) + emitError(*ci, decl->getLocation(), "action declaration doesn't match previous declaration"); + } + if (cg.actions.count(decl->getNameAsString()) == 0) { + if (cg.actions.count(name) == 0) + create_action_dispatch(decl); + else + emitError(*ci, decl->getLocation(), "action already defined elsewhere"); + } + cg.actions.insert(decl->getNameAsString()); // insert the method action, so we don't create the dispatcher twice + cg.actions.insert(name); + /* + for (auto param : decl->parameters()) { + if (auto tp = dyn_cast(param->getOriginalType().getTypePtr()->getAsCXXRecordDecl())) { + cg.datastream_uses.insert(tp->getQualifiedNameAsString()); + } + } + */ + } + else if (decl->isEosioNotify()) { + + name = generation_utils::get_notify_pair(decl); + auto first = name.substr(0, name.find("::")); + if (first != "*") + validate_name(first, [&]() {emitError(*ci, decl->getLocation(), "invalid contract name");}); + auto second = name.substr(name.find("::")+2); + validate_name(second, [&]() {emitError(*ci, decl->getLocation(), "invalid action name");}); + + if (!_notify_set.count(name)) + _notify_set.insert(name); + else { + auto itr = _notify_set.find(name); + if (*itr != name) + emitError(*ci, decl->getLocation(), "notify handler declaration doesn't match previous declaration"); + } + + if (cg.notify_handlers.count(decl->getNameAsString()) == 0) { + if (cg.notify_handlers.count(name) == 0) + create_notify_dispatch(decl); + else + emitError(*ci, decl->getLocation(), "notification handler already defined elsewhere"); + } + cg.notify_handlers.insert(decl->getNameAsString()); // insert the method action, so we don't create the dispatcher twice + cg.notify_handlers.insert(name); + /* + for (auto param : decl->parameters()) { + if (auto tp = dyn_cast(param->getOriginalType().getTypePtr()->getAsCXXRecordDecl())) { + cg.datastream_uses.insert(tp->getQualifiedNameAsString()); + } + } + */ + } + + //cg.cxx_methods.emplace(name, decl); + 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 { + private: + eosio_codegen_visitor *visitor; + std::string main_file; + CompilerInstance* ci; + + public: + explicit eosio_codegen_consumer(CompilerInstance *CI, std::string file) + : visitor(new eosio_codegen_visitor(CI)), main_file(file), ci(CI) { } + + + virtual void HandleTranslationUnit(ASTContext &Context) { + codegen& cg = codegen::get(); + auto& src_mgr = Context.getSourceManager(); + auto& f_mgr = src_mgr.getFileManager(); + auto main_fe = f_mgr.getFile(main_file); + if (main_fe) { + auto fid = src_mgr.getOrCreateFileID(f_mgr.getFile(main_file), SrcMgr::CharacteristicKind::C_User); + visitor->set_main_fid(fid); + visitor->set_main_name(main_fe->getName()); + visitor->TraverseDecl(Context.getTranslationUnitDecl()); + int fd; + llvm::SmallString<128> fn; + try { + SmallString<64> res; + llvm::sys::path::system_temp_directory(true, res); + + std::ofstream out(std::string(res.c_str())+"/"+llvm::sys::path::filename(main_fe->getName()).str()+"-tmp.cpp"); + for (auto inc : global_includes[main_file]) { + visitor->get_rewriter().ReplaceText(inc.range, + std::string("\"")+inc.file_name+"\"\n"); + } + if (!cg.abi.empty()) { + // generate apply stub with abi + std::stringstream ss; + ss << "extern \"C\" {\n"; + ss << "\t__attribute__((weak, eosio_wasm_entry, eosio_wasm_abi("; + std::string abi = cg.abi; + ss << "\"" << _quoted(abi) << "\""; + ss << ")))\n"; + ss << "\tvoid __insert_eosio_abi(unsigned long long r, unsigned long long c, unsigned long long a){"; + ss << "eosio_assert_code(false, 1);"; + ss << "}\n"; + ss << "}"; + visitor->get_rewriter().InsertTextAfter(ci->getSourceManager().getLocForEndOfFile(fid), ss.str()); + } + auto& RewriteBuf = visitor->get_rewriter().getEditBuffer(fid); + out << std::string(RewriteBuf.begin(), RewriteBuf.end()); + cg.tmp_files.emplace(main_file, fn.str()); + out.close(); + } catch (...) { + llvm::outs() << "Failed to create temporary file\n"; + } + } + } + + }; + + class eosio_codegen_frontend_action : public ASTFrontendAction { + public: + virtual std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef file) { + CI.getPreprocessor().addPPCallbacks(_make_unique(CI.getSourceManager(), file.str())); + return _make_unique(&CI, file); + } + }; + +}} // ns eosio::cdt diff --git a/tools/include/eosio/gen.hpp b/tools/include/eosio/gen.hpp index 6666890d569..151c82facd5 100644 --- a/tools/include/eosio/gen.hpp +++ b/tools/include/eosio/gen.hpp @@ -147,7 +147,7 @@ struct generation_utils { inline void set_contract_name( const std::string& cn ) { contract_name = cn; } inline std::string get_contract_name()const { return contract_name; } - inline void set_resource_dirs( const llvm::cl::list& rd ) { + inline void set_resource_dirs( const std::vector& rd ) { llvm::SmallString<128> cwd; auto has_real_path = llvm::sys::fs::real_path("./", cwd, true); if (!has_real_path) @@ -181,6 +181,11 @@ struct generation_utils { return tmp; return decl->getNameAsString(); } + static inline std::string get_notify_pair( const clang::CXXMethodDecl* decl ) { + std::string notify_pair = ""; + auto tmp = decl->getEosioNotifyAttr()->getName(); + return tmp; + } static inline std::string get_action_name( const clang::CXXRecordDecl* decl ) { std::string action_name = ""; auto tmp = decl->getEosioActionAttr()->getName();