diff --git a/contracts/eosio.token/eosio.token.cpp b/contracts/eosio.token/eosio.token.cpp index d85c2362084..351d81c47c5 100644 --- a/contracts/eosio.token/eosio.token.cpp +++ b/contracts/eosio.token/eosio.token.cpp @@ -3,7 +3,7 @@ * @copyright defined in eos/LICENSE.txt */ -#include +#include "eosio.token.hpp" namespace eosio { diff --git a/libraries/abi_generator/abi_generator.cpp b/libraries/abi_generator/abi_generator.cpp index eccc50ad835..873f319ac9e 100644 --- a/libraries/abi_generator/abi_generator.cpp +++ b/libraries/abi_generator/abi_generator.cpp @@ -83,6 +83,7 @@ string abi_generator::translate_type(const string& type_name) { else if (type_name == "long" || type_name == "int32_t") built_in_type = "int32"; else if (type_name == "short" || type_name == "int16_t") built_in_type = "int16"; else if (type_name == "char" || type_name == "int8_t") built_in_type = "int8"; + else if (type_name == "double") built_in_type = "float64"; else { static auto types = eosio::chain::common_type_defs(); auto itr = std::find_if( types.begin(), types.end(), @@ -100,9 +101,6 @@ bool abi_generator::inspect_type_methods_for_actions(const Decl* decl) { try { const auto* rec_decl = dyn_cast(decl); if(rec_decl == nullptr) return false; - if( rec_decl->getName().str() != target_contract ) - return false; - const auto* type = rec_decl->getTypeForDecl(); ABI_ASSERT(type != nullptr); @@ -112,9 +110,24 @@ bool abi_generator::inspect_type_methods_for_actions(const Decl* decl) { try { auto method_name = method->getNameAsString(); + // Try to get "action" annotation from method comment + bool raw_comment_is_action = false; + const RawComment* raw_comment = ast_context->getRawCommentForDeclNoCache(method); + if(raw_comment != nullptr) { + SourceManager& source_manager = ast_context->getSourceManager(); + string raw_text = raw_comment->getRawText(source_manager); + regex r(R"(@abi (action)((?: [a-z0-9]+)*))"); + smatch smatch; + regex_search(raw_text, smatch, r); + raw_comment_is_action = smatch.size() == 3; + } - if( std::find(target_actions.begin(), target_actions.end(), method_name) == target_actions.end() ) + // Check if current method is listed the EOSIO_ABI macro + bool is_action_from_macro = rec_decl->getName().str() == target_contract && std::find(target_actions.begin(), target_actions.end(), method_name) != target_actions.end(); + + if(!raw_comment_is_action && !is_action_from_macro) { return; + } ABI_ASSERT(find_struct(method_name) == nullptr, "action already exists ${method_name}", ("method_name",method_name)); @@ -189,18 +202,11 @@ void abi_generator::handle_decl(const Decl* decl) { try { return; } - // If EOSIO_ABI macro was found, check if the current declaration - // is of the type specified in the macro and export their methods (actions). - bool type_has_actions = false; - if( target_contract.size() ) { - type_has_actions = inspect_type_methods_for_actions(decl); - } - - // The current Decl was the type referenced in EOSIO_ABI macro + // Check if the current declaration has actions (EOSIO_ABI, or explicit) + bool type_has_actions = inspect_type_methods_for_actions(decl); if( type_has_actions ) return; - // The current Decl was not the type referenced in EOSIO_ABI macro - // so we try to see if it has comments attached to the declaration + // The current Decl doesn't have actions const RawComment* raw_comment = ast_context->getRawCommentForDeclNoCache(decl); if(raw_comment == nullptr) { return; @@ -275,9 +281,10 @@ void abi_generator::handle_decl(const Decl* decl) { try { table.name = params[0]; } - if(params.size() >= 2) + if(params.size() >= 2) { table.index_type = params[1]; - else { try { + ABI_ASSERT(table.index_type == "i64", "Only i64 index is supported. ${index_type}",("index_type",table.index_type)); + } else { try { guess_index_type(table, *s); } FC_CAPTURE_AND_RETHROW( (type_name) ) } @@ -324,34 +331,14 @@ void abi_generator::get_all_fields(const struct_def& s, vector& field } } -bool abi_generator::is_i64i64i64_index(const vector& fields) { - return fields.size() >= 3 && is_64bit(fields[0].type) && is_64bit(fields[1].type) && is_64bit(fields[2].type); -} - bool abi_generator::is_i64_index(const vector& fields) { return fields.size() >= 1 && is_64bit(fields[0].type); } -bool abi_generator::is_i128i128_index(const vector& fields) { - return fields.size() >= 2 && is_128bit(fields[0].type) && is_128bit(fields[1].type); -} - -bool abi_generator::is_str_index(const vector& fields) { - return fields.size() == 2 && is_string(fields[0].type); -} - void abi_generator::guess_index_type(table_def& table, const struct_def s) { - vector fields; get_all_fields(s, fields); - - if( is_str_index(fields) ) { - table.index_type = "str"; - } else if ( is_i64i64i64_index(fields) ) { - table.index_type = "i64i64i64"; - } else if( is_i128i128_index(fields) ) { - table.index_type = "i128i128"; - } else if( is_i64_index(fields) ) { + if( is_i64_index(fields) ) { table.index_type = "i64"; } else { ABI_ASSERT(false, "Unable to guess index type"); @@ -363,8 +350,7 @@ void abi_generator::guess_key_names(table_def& table, const struct_def s) { vector fields; get_all_fields(s, fields); - if( table.index_type == "i64i64i64" || table.index_type == "i128i128" - || table.index_type == "i64") { + if( table.index_type == "i64") { table.key_names.clear(); table.key_types.clear(); @@ -376,18 +362,13 @@ void abi_generator::guess_key_names(table_def& table, const struct_def s) { table.key_types.emplace_back(f.type); key_size += type_size[f.type]/8; - if((table.index_type == "i64i64i64" && key_size >= sizeof(uint64_t)*3) || - (table.index_type == "i64" && key_size >= sizeof(uint64_t)) || - (table.index_type == "i128i128" && key_size >= sizeof(__int128)*2)) { + if(table.index_type == "i64" && key_size >= sizeof(uint64_t)) { valid_key = true; break; } } ABI_ASSERT(valid_key, "Unable to guess key names"); - } else if( table.index_type == "str" && is_str_index(fields) ) { - table.key_names = vector{fields[0].name}; - table.key_types = vector{fields[0].type}; } else { ABI_ASSERT(false, "Unable to guess key names"); } diff --git a/libraries/abi_generator/include/eosio/abi_generator/abi_generator.hpp b/libraries/abi_generator/include/eosio/abi_generator/abi_generator.hpp index cdb5d0ffe3e..8e9e6494085 100644 --- a/libraries/abi_generator/include/eosio/abi_generator/abi_generator.hpp +++ b/libraries/abi_generator/include/eosio/abi_generator/abi_generator.hpp @@ -343,6 +343,25 @@ namespace eosio { callback_handler(CompilerInstance& compiler_instance, find_eosio_abi_macro_action& act) : compiler_instance(compiler_instance), act(act) {} + string remove_namespace(const string& full_name) { + int i = full_name.size(); + int on_spec = 0; + int colons = 0; + while( --i >= 0 ) { + if( full_name[i] == '>' ) { + ++on_spec; colons=0; + } else if( full_name[i] == '<' ) { + --on_spec; colons=0; + } else if( full_name[i] == ':' && !on_spec) { + if (++colons == 2) + return full_name.substr(i+2); + } else { + colons = 0; + } + } + return full_name; + } + void MacroExpands (const Token &token, const MacroDefinition &md, SourceRange range, const MacroArgs *args) override { auto* id = token.getIdentifierInfo(); @@ -366,7 +385,7 @@ namespace eosio { auto res = regex_search(macrostr, smatch, r); ABI_ASSERT( res ); - act.contract = smatch[1].str(); + act.contract = remove_namespace(smatch[1].str()); auto actions_str = smatch[2].str(); boost::trim(actions_str); diff --git a/unittests/abi_tests.cpp b/unittests/abi_tests.cpp index e4c7b5ea5c2..d6b3bd81c8c 100644 --- a/unittests/abi_tests.cpp +++ b/unittests/abi_tests.cpp @@ -536,20 +536,23 @@ struct abi_gen_helper { std::string contract; std::vector actions; - - bool res = runToolOnCodeWithArgs(new find_eosio_abi_macro_action(contract, actions, ""), source, - {"-fparse-all-comments", "--std=c++14", "--target=wasm32", "-ffreestanding", "-nostdlib", + + auto extra_args = std::vector{"-fparse-all-comments", "--std=c++14", "--target=wasm32", "-ffreestanding", "-nostdlib", "-nostdlibinc", "-fno-threadsafe-statics", "-fno-rtti", "-fno-exceptions", include_param, boost_include_param, stdcpp_include_param, - stdc_include_param, pfr_include_param } + stdc_include_param, pfr_include_param }; + + bool res = runToolOnCodeWithArgs( + new find_eosio_abi_macro_action(contract, actions, ""), + source, + extra_args ); FC_ASSERT(res == true); - res = runToolOnCodeWithArgs(new generate_abi_action(false, opt_sfs, "", output, contract, actions), source, - {"-fparse-all-comments", "--std=c++14", "--target=wasm32", "-ffreestanding", "-nostdlib", - "-nostdlibinc", "-fno-threadsafe-statics", "-fno-rtti", "-fno-exceptions", - include_param, boost_include_param, stdcpp_include_param, - stdc_include_param, pfr_include_param } + res = runToolOnCodeWithArgs( + new generate_abi_action(false, opt_sfs, "", output, contract, actions), + source, + extra_args ); FC_ASSERT(res == true); @@ -840,138 +843,38 @@ BOOST_FIXTURE_TEST_CASE(abigen_all_indexes, abi_gen_helper) uint64_t field1; }; - //@abi table - struct table2 { - uint128_t field1; - uint128_t field2; - }; - - //@abi table - struct table3 { - uint64_t field1; - uint64_t field2; - uint64_t field3; - }; - - struct my_complex_value { - uint64_t a; - name b; - }; - - //@abi table - struct table4 { - std::string key; - my_complex_value value; - }; - )====="; const char* all_indexes_abi = R"=====( { - "types": [], - "structs": [{ - "name" : "table1", - "base" : "", - "fields" : [{ - "name" : "field1", - "type" : "uint64" - }] - },{ - "name" : "table2", - "base" : "", - "fields" : [{ - "name" : "field1", - "type" : "uint128" - },{ - "name" : "field2", - "type" : "uint128" - }] - },{ - "name" : "table3", - "base" : "", - "fields" : [{ - "name" : "field1", - "type" : "uint64" - },{ - "name" : "field2", - "type" : "uint64" - },{ - "name" : "field3", - "type" : "uint64" - }] - },{ - "name" : "my_complex_value", - "base" : "", - "fields" : [{ - "name" : "a", - "type" : "uint64" - },{ - "name" : "b", - "type" : "name" - }] - },{ - "name" : "table4", - "base" : "", - "fields" : [{ - "name" : "key", - "type" : "string" - },{ - "name" : "value", - "type" : "my_complex_value" - }] - }], - "actions": [], - "tables": [ - { - "name": "table1", - "type": "table1", - "index_type": "i64", - "key_names": [ - "field1" - ], - "key_types": [ - "uint64" - ] - },{ - "name": "table2", - "type": "table2", - "index_type": "i128i128", - "key_names": [ - "field1", - "field2" - ], - "key_types": [ - "uint128", - "uint128" - ] - },{ - "name": "table3", - "type": "table3", - "index_type": "i64i64i64", - "key_names": [ - "field1", - "field2", - "field3" - ], - "key_types": [ - "uint64", - "uint64", - "uint64" - ] - },{ - "name": "table4", - "type": "table4", - "index_type": "str", - "key_names": [ - "key", - ], - "key_types": [ - "string", - ] - }, - - ], - "ricardian_clauses": [] + "version": "eosio::abi/1.0", + "types": [], + "structs": [{ + "name": "table1", + "base": "", + "fields": [{ + "name": "field1", + "type": "uint64" + } + ] + } + ], + "actions": [], + "tables": [{ + "name": "table1", + "index_type": "i64", + "key_names": [ + "field1" + ], + "key_types": [ + "uint64" + ], + "type": "table1" + } + ], + "ricardian_clauses": [], + "error_messages": [], + "abi_extensions": [] } )====="; @@ -1137,57 +1040,6 @@ BOOST_FIXTURE_TEST_CASE(abigen_full_table_decl, abi_gen_helper) } FC_LOG_AND_RETHROW() } -BOOST_FIXTURE_TEST_CASE(abigen_str_table_decl, abi_gen_helper) -{ try { - - const char* str_table_decl = R"=====( - #include - #include - - //@abi table - class table1 { - public: - std::string name; - uint32_t age; - }; - - )====="; - - const char* str_table_decl_abi = R"=====( - { - "types": [], - "structs": [{ - "name": "table1", - "base": "", - "fields": [{ - "name" : "name", - "type" : "string" - },{ - "name" : "age", - "type" : "uint32" - }] - } - ], - "actions": [], - "tables": [{ - "name": "table1", - "index_type": "str", - "key_names": [ - "name" - ], - "key_types": [ - "string" - ], - "type": "table1" - } - ], - "ricardian_clauses": [] - } - )====="; - - BOOST_TEST( generate_abi(str_table_decl, str_table_decl_abi) == true ); -} FC_LOG_AND_RETHROW() } - BOOST_FIXTURE_TEST_CASE(abigen_union_table, abi_gen_helper) { try { @@ -1857,6 +1709,91 @@ BOOST_FIXTURE_TEST_CASE(abigen_contract_inheritance, abi_gen_helper) } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE(abigen_no_eosioabi_macro, abi_gen_helper) +{ try { + + const char* abigen_no_eosioabi_macro = R"=====( + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpointer-bool-conversion" + #include + #include + #pragma GCC diagnostic pop + + using namespace eosio; + + struct hello : public eosio::contract { + public: + using contract::contract; + + //@abi action + void hi( name user ) { + print( "Hello, ", name{user} ); + } + + //@abi action + void bye( name user ) { + print( "Bye, ", name{user} ); + } + + void apply( account_name contract, account_name act ) { + auto& thiscontract = *this; + switch( act ) { + EOSIO_API( hello, (hi)(bye)) + }; + } + }; + + extern "C" { + [[noreturn]] void apply( uint64_t receiver, uint64_t code, uint64_t action ) { + hello h( receiver ); + h.apply( code, action ); + eosio_exit(0); + } + } + )====="; + + const char* abigen_no_eosioabi_macro_abi = R"=====( + { + "version": "eosio::abi/1.0", + "types": [], + "structs": [{ + "name": "hi", + "base": "", + "fields": [{ + "name": "user", + "type": "name" + } + ] + },{ + "name": "bye", + "base": "", + "fields": [{ + "name": "user", + "type": "name" + } + ] + } + ], + "actions": [{ + "name": "hi", + "type": "hi", + "ricardian_contract": "" + },{ + "name": "bye", + "type": "bye", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "error_messages": [], + "abi_extensions": [] + } + )====="; + + BOOST_TEST( generate_abi(abigen_no_eosioabi_macro, abigen_no_eosioabi_macro_abi) == true ); + +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE(general) { try {