Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move output stream operators to evmc::evmc_cpp #599

Merged
merged 4 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions include/evmc/evmc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <functional>
#include <initializer_list>
#include <ostream>
#include <utility>

/// EVMC C++ API - wrappers and bindings for C++
Expand Down Expand Up @@ -337,6 +338,20 @@ constexpr bytes32 operator""_bytes32() noexcept

using namespace literals;


/// @copydoc evmc_status_code_to_string
inline const char* to_string(evmc_status_code status_code) noexcept
{
return evmc_status_code_to_string(status_code);
}

/// @copydoc evmc_revision_to_string
inline const char* to_string(evmc_revision rev) noexcept
{
return evmc_revision_to_string(rev);
}


/// Alias for evmc_make_result().
constexpr auto make_result = evmc_make_result;

Expand Down Expand Up @@ -852,6 +867,24 @@ inline const evmc_host_interface& Host::get_interface() noexcept
} // namespace evmc


/// "Stream out" operator implementation for ::evmc_status_code.
///
/// @note This is defined in global namespace to match ::evmc_status_code definition and allow
/// convenient operator overloading usage.
inline std::ostream& operator<<(std::ostream& os, evmc_status_code status_code)
{
return os << evmc::to_string(status_code);
}

/// "Stream out" operator implementation for ::evmc_revision.
///
/// @note This is defined in global namespace to match ::evmc_revision definition and allow
/// convenient operator overloading usage.
inline std::ostream& operator<<(std::ostream& os, evmc_revision rev)
{
return os << evmc::to_string(rev);
}

namespace std
{
/// Hash operator template specialization for evmc::address. Needed for unordered containers.
Expand Down
80 changes: 80 additions & 0 deletions include/evmc/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,84 @@ static inline const union evmc_result_optional_storage* evmc_get_const_optional_

/** @} */

/** Returns text representation of the ::evmc_status_code. */
static inline const char* evmc_status_code_to_string(enum evmc_status_code status_code)
{
switch (status_code)
{
case EVMC_SUCCESS:
return "success";
case EVMC_FAILURE:
return "failure";
case EVMC_REVERT:
return "revert";
case EVMC_OUT_OF_GAS:
return "out of gas";
case EVMC_INVALID_INSTRUCTION:
return "invalid instruction";
case EVMC_UNDEFINED_INSTRUCTION:
return "undefined instruction";
case EVMC_STACK_OVERFLOW:
return "stack overflow";
case EVMC_STACK_UNDERFLOW:
return "stack underflow";
case EVMC_BAD_JUMP_DESTINATION:
return "bad jump destination";
case EVMC_INVALID_MEMORY_ACCESS:
return "invalid memory access";
case EVMC_CALL_DEPTH_EXCEEDED:
return "call depth exceeded";
case EVMC_STATIC_MODE_VIOLATION:
return "static mode violation";
case EVMC_PRECOMPILE_FAILURE:
return "precompile failure";
case EVMC_CONTRACT_VALIDATION_FAILURE:
return "contract validation failure";
case EVMC_ARGUMENT_OUT_OF_RANGE:
return "argument out of range";
case EVMC_WASM_UNREACHABLE_INSTRUCTION:
return "wasm unreachable instruction";
case EVMC_WASM_TRAP:
return "wasm trap";
case EVMC_INSUFFICIENT_BALANCE:
return "insufficient balance";
case EVMC_INTERNAL_ERROR:
return "internal error";
case EVMC_REJECTED:
return "rejected";
case EVMC_OUT_OF_MEMORY:
return "out of memory";
}
return "<unknown>";
}

/** Returns the name of the ::evmc_revision. */
static inline const char* evmc_revision_to_string(enum evmc_revision rev)
{
switch (rev)
{
case EVMC_FRONTIER:
return "Frontier";
case EVMC_HOMESTEAD:
return "Homestead";
case EVMC_TANGERINE_WHISTLE:
return "Tangerine Whistle";
case EVMC_SPURIOUS_DRAGON:
return "Spurious Dragon";
case EVMC_BYZANTIUM:
return "Byzantium";
case EVMC_CONSTANTINOPLE:
return "Constantinople";
case EVMC_PETERSBURG:
return "Petersburg";
case EVMC_ISTANBUL:
return "Istanbul";
case EVMC_BERLIN:
return "Berlin";
case EVMC_LONDON:
return "London";
}
return "<unknown>";
}

/** @} */
1 change: 0 additions & 1 deletion test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ target_link_libraries(
evmc::instructions
evmc::evmc_cpp
evmc::tool-commands
evmc::tool-utils
evmc::hex
GTest::gtest_main
)
Expand Down
144 changes: 144 additions & 0 deletions test/unittests/cpp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <evmc/mocked_host.hpp>
#include <gtest/gtest.h>
#include <array>
#include <cctype>
#include <cstring>
#include <map>
#include <unordered_map>
Expand Down Expand Up @@ -775,3 +776,146 @@ TEST(cpp, result_create)
EXPECT_TRUE(std::memcmp(c.output_data, r.output_data, c.output_size) == 0);
c.release(&c);
}

TEST(cpp, status_code_to_string)
{
struct TestCase
{
evmc_status_code status_code;
std::string_view str;
};

#define TEST_CASE(STATUS_CODE) \
TestCase { STATUS_CODE, #STATUS_CODE }
constexpr TestCase test_cases[]{
TEST_CASE(EVMC_SUCCESS),
TEST_CASE(EVMC_FAILURE),
TEST_CASE(EVMC_REVERT),
TEST_CASE(EVMC_OUT_OF_GAS),
TEST_CASE(EVMC_INVALID_INSTRUCTION),
TEST_CASE(EVMC_UNDEFINED_INSTRUCTION),
TEST_CASE(EVMC_STACK_OVERFLOW),
TEST_CASE(EVMC_STACK_UNDERFLOW),
TEST_CASE(EVMC_BAD_JUMP_DESTINATION),
TEST_CASE(EVMC_INVALID_MEMORY_ACCESS),
TEST_CASE(EVMC_CALL_DEPTH_EXCEEDED),
TEST_CASE(EVMC_STATIC_MODE_VIOLATION),
TEST_CASE(EVMC_PRECOMPILE_FAILURE),
TEST_CASE(EVMC_CONTRACT_VALIDATION_FAILURE),
TEST_CASE(EVMC_ARGUMENT_OUT_OF_RANGE),
TEST_CASE(EVMC_WASM_UNREACHABLE_INSTRUCTION),
TEST_CASE(EVMC_WASM_TRAP),
TEST_CASE(EVMC_INSUFFICIENT_BALANCE),
TEST_CASE(EVMC_INTERNAL_ERROR),
TEST_CASE(EVMC_REJECTED),
TEST_CASE(EVMC_OUT_OF_MEMORY),
};
#undef TEST_CASE

std::ostringstream os;
for (const auto& t : test_cases)
{
std::string expected;
std::transform(std::cbegin(t.str) + std::strlen("EVMC_"), std::cend(t.str),
std::back_inserter(expected), [](char c) -> char {
return (c == '_') ? ' ' : static_cast<char>(std::tolower(c));
});
EXPECT_EQ(evmc::to_string(t.status_code), expected);
os << t.status_code;
EXPECT_EQ(os.str(), expected);
os.str({});
}
}

TEST(cpp, revision_to_string)
{
struct TestCase
{
evmc_revision rev;
std::string_view str;
};

#define TEST_CASE(STATUS_CODE) \
TestCase { STATUS_CODE, #STATUS_CODE }
constexpr TestCase test_cases[]{
TEST_CASE(EVMC_FRONTIER),
TEST_CASE(EVMC_HOMESTEAD),
TEST_CASE(EVMC_TANGERINE_WHISTLE),
TEST_CASE(EVMC_SPURIOUS_DRAGON),
TEST_CASE(EVMC_BYZANTIUM),
TEST_CASE(EVMC_CONSTANTINOPLE),
TEST_CASE(EVMC_PETERSBURG),
TEST_CASE(EVMC_ISTANBUL),
TEST_CASE(EVMC_BERLIN),
TEST_CASE(EVMC_LONDON),
};
#undef TEST_CASE

std::ostringstream os;
ASSERT_EQ(std::size(test_cases), size_t{EVMC_MAX_REVISION + 1});
for (size_t i = 0; i < std::size(test_cases); ++i)
{
const auto& t = test_cases[i];
EXPECT_EQ(t.rev, static_cast<int>(i));
std::string expected;
std::transform(std::cbegin(t.str) + std::strlen("EVMC_"), std::cend(t.str),
std::back_inserter(expected), [skip = true](char c) mutable -> char {
if (skip)
{
skip = false;
return c;
}
else if (c == '_')
{
skip = true;
return ' ';
}
else
return static_cast<char>(std::tolower(c));
});
EXPECT_EQ(evmc::to_string(t.rev), expected);
os << t.rev;
EXPECT_EQ(os.str(), expected);
os.str({});
}
}


#ifdef __GNUC__
extern "C" [[gnu::weak]] void __ubsan_handle_builtin_unreachable(void*);
#endif

static bool has_ubsan() noexcept
{
#ifdef __GNUC__
return (__ubsan_handle_builtin_unreachable != nullptr);
#else
return false;
#endif
}

TEST(cpp, status_code_to_string_invalid)
{
if (!has_ubsan())
{
std::ostringstream os;
int value = 99;
const auto invalid = static_cast<evmc_status_code>(value);
EXPECT_STREQ(evmc::to_string(invalid), "<unknown>");
os << invalid;
EXPECT_EQ(os.str(), "<unknown>");
}
}

TEST(cpp, revision_to_string_invalid)
{
if (!has_ubsan())
{
std::ostringstream os;
int value = 99;
const auto invalid = static_cast<evmc_revision>(value);
EXPECT_STREQ(evmc::to_string(invalid), "<unknown>");
os << invalid;
EXPECT_EQ(os.str(), "<unknown>");
}
}
1 change: 0 additions & 1 deletion tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@

add_subdirectory(commands)
add_subdirectory(evmc)
add_subdirectory(utils)
add_subdirectory(vmtester)
2 changes: 1 addition & 1 deletion tools/commands/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ add_library(tool-commands STATIC commands.hpp run.cpp)
add_library(evmc::tool-commands ALIAS tool-commands)
target_compile_features(tool-commands PUBLIC cxx_std_17)
target_include_directories(tool-commands PUBLIC ${PROJECT_SOURCE_DIR})
target_link_libraries(tool-commands PRIVATE evmc::tool-utils evmc::mocked_host evmc::hex)
target_link_libraries(tool-commands PRIVATE evmc::evmc_cpp evmc::mocked_host evmc::hex)
2 changes: 1 addition & 1 deletion tools/commands/run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.

#include "tools/commands/commands.hpp"
#include "tools/utils/utils.hpp"
#include <evmc/evmc.hpp>
#include <evmc/hex.hpp>
#include <evmc/mocked_host.hpp>
#include <chrono>
Expand Down
9 changes: 0 additions & 9 deletions tools/utils/CMakeLists.txt

This file was deleted.

Loading