diff --git a/include/evmc/helpers.h b/include/evmc/helpers.h new file mode 100644 index 0000000..66c7907 --- /dev/null +++ b/include/evmc/helpers.h @@ -0,0 +1,54 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2018 Pawel Bylica. + * Licensed under the MIT License. See the LICENSE file. + */ + +/** + * EVMC Helpers + * + * A collection of helper functions for invoking a VM instance methods. + * These are convenient for languages where invoking function pointers + * is "ugly" or impossible (such as Go). + * + * @defgroup helpers EVMC Helpers + * @{ + */ +#pragma once + +#include + +/** + * Destroys the VM instance. + * + * @see evmc_destroy_fn + */ +static inline void evmc_destroy(struct evmc_instance* instance) +{ + instance->destroy(instance); +} + +/** + * Sets the option for the VM instance, if the feature is supported by the VM. + * + * @see evmc_set_option_fn + */ +static inline int evmc_set_option(struct evmc_instance* instance, + char const* name, + char const* value) +{ + if (instance->set_option) + return instance->set_option(instance, name, value); + return 0; +} + +/** + * Releases the resources allocated to the execution result. + * + * @see evmc_release_result_fn + */ +static inline void evmc_release_result(struct evmc_result* result) +{ + result->release(result); +} + +/** @} */ diff --git a/include/evmc/instructions.h b/include/evmc/instructions.h index 1dc50b9..a86cf9d 100644 --- a/include/evmc/instructions.h +++ b/include/evmc/instructions.h @@ -3,13 +3,19 @@ * Licensed under the MIT License. See the LICENSE file. */ +/** + * EVM Instruction Tables + * + * A collection of metrics for EVM1 instruction set. + * + * @defgroup instructions EVM Instructions + * @{ + */ #pragma once #include #include -#include - #if __cplusplus extern "C" { #endif @@ -209,3 +215,5 @@ EVMC_EXPORT const char* const* evmc_get_instruction_names_table(enum evmc_revisi #if __cplusplus } #endif + +/** @} */ diff --git a/include/evmc/loader.h b/include/evmc/loader.h index b344f88..fdb0108 100644 --- a/include/evmc/loader.h +++ b/include/evmc/loader.h @@ -3,6 +3,15 @@ * Licensed under the MIT License. See the LICENSE file. */ +/** + * EVMC Loader Library + * + * The EVMC Loader Library supports loading VMs implemented as Dynamically Loaded Libraries + * (DLLs, shared objects). + * + * @defgroup loader EVMC Loader + * @{ + */ #pragma once #if __cplusplus @@ -15,10 +24,23 @@ typedef struct evmc_instance* (*evmc_create_fn)(void); /** Error codes for the EVMC loader. */ enum evmc_loader_error_code { + /** The loader succeeded. */ EVMC_LOADER_SUCCESS = 0, - EVMC_LOADER_CANNOT_OPEN, - EVMC_LOADER_SYMBOL_NOT_FOUND, - EVMC_LOADER_INVALID_ARGUMENT, + + /** The loader cannot open the given file name. */ + EVMC_LOADER_CANNOT_OPEN = 1, + + /** The VM create function not found. */ + EVMC_LOADER_SYMBOL_NOT_FOUND = 2, + + /** The invalid argument value provided. */ + EVMC_LOADER_INVALID_ARGUMENT = 3, + + /** The creation of a VM instance has failed. */ + EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4, + + /** The ABI version of the VM instance has mismatched. */ + EVMC_LOADER_ABI_VERSION_MISMATCH = 5, }; /** @@ -27,7 +49,7 @@ enum evmc_loader_error_code * This function tries to open a DLL at the given `filename`. On UNIX-like systems dlopen() function * is used. On Windows LoadLibrary() function is used. * - * If the file does not exist or is not a valid shared library the ::EVMC_ERRC_CANNOT_OPEN error + * If the file does not exist or is not a valid shared library the ::EVMC_LOADER_CANNOT_OPEN error * code is signaled and NULL is returned. * * After the DLL is successfully loaded the function tries to find the EVM create function in the @@ -48,18 +70,38 @@ enum evmc_loader_error_code * "evmc_create_interpreter". * * If the create function is found in the library, the pointer to the function is returned. - * Otherwise, the ::EVMC_ERRC_SYMBOL_NOT_FOUND error code is signaled and NULL is returned. + * Otherwise, the ::EVMC_LOADER_SYMBOL_NOT_FOUND error code is signaled and NULL is returned. + * + * It is safe to call this function with the same filename argument multiple times + * (the DLL is not going to be loaded multiple times). * * @param filename The null terminated path (absolute or relative) to the shared library * containing the EVM implementation. If the value is NULL, an empty C-string - * or longer than the path maximum length the ::EVMC_ERRC_INVALID_ARGUMENT is + * or longer than the path maximum length the ::EVMC_LOADER_INVALID_ARGUMENT is * signaled. * @param error_code The pointer to the error code. If not NULL the value is set to - * ::EVMC_ERRC_SUCCESS on success or any other error code as described above. + * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. * @return The pointer to the EVM create function or NULL. */ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code); +/** + * Dynamically loads the VM DLL and creates the VM instance. + * + * This is a macro for creating the VM instance with the function returned from evmc_load(). + * The function signals the same errors as evmc_load() and additionally: + * - ::EVMC_LOADER_INSTANCE_CREATION_FAILURE when the create function returns NULL, + * - ::EVMC_LOADER_ABI_VERSION_MISMATCH when the created VM instance has ABI version different + * from the ABI version of this library (::EVMC_ABI_VERSION). + * + * It is safe to call this function with the same filename argument multiple times: + * the DLL is not going to be loaded multiple times, but the function will return new VM instance + * each time. + */ +struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code); + #if __cplusplus } #endif + +/** @} */ diff --git a/lib/loader/loader.c b/lib/loader/loader.c index c365c9a..3156f2f 100644 --- a/lib/loader/loader.c +++ b/lib/loader/loader.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -121,3 +122,26 @@ exit: *error_code = ec; return create_fn; } + +struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code) +{ + evmc_create_fn create_fn = evmc_load(filename, error_code); + + if (!create_fn) + return NULL; + + struct evmc_instance* instance = create_fn(); + if (!instance) + { + *error_code = EVMC_LOADER_INSTANCE_CREATION_FAILURE; + return NULL; + } + + if (instance->abi_version != EVMC_ABI_VERSION) + { + *error_code = EVMC_LOADER_ABI_VERSION_MISMATCH; + return NULL; + } + + return instance; +} diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 898a5d6..7115693 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E ${cmd} $ _ COMMAND ${CMAKE_COMMAND} -E ${cmd} $ lib_.so COMMAND ${CMAKE_COMMAND} -E ${cmd} $ ../aaa.evm + COMMAND ${CMAKE_COMMAND} -E ${cmd} $ failure.vm + COMMAND ${CMAKE_COMMAND} -E ${cmd} $ abi42.vm COMMAND ${CMAKE_COMMAND} -E touch empty.file ) diff --git a/test/unittests/test_loader.cpp b/test/unittests/test_loader.cpp index 7c455a5..8245f54 100644 --- a/test/unittests/test_loader.cpp +++ b/test/unittests/test_loader.cpp @@ -244,3 +244,19 @@ TEST(loader, lib_) x = evmc_load(path, nullptr); EXPECT_EQ(x, nullptr); } + +TEST(loader, load_and_create_failure) +{ + evmc_loader_error_code ec; + auto vm = evmc_load_and_create("unittests/failure.vm", &ec); + EXPECT_EQ(vm, nullptr); + EXPECT_EQ(ec, EVMC_LOADER_INSTANCE_CREATION_FAILURE); +} + +TEST(loader, load_and_create_abi_mismatch) +{ + evmc_loader_error_code ec; + auto vm = evmc_load_and_create("unittests/abi42.vm", &ec); + EXPECT_EQ(vm, nullptr); + EXPECT_EQ(ec, EVMC_LOADER_ABI_VERSION_MISMATCH); +} diff --git a/test/unittests/vm_mock.c b/test/unittests/vm_mock.c index 4ab78b7..a0cb194 100644 --- a/test/unittests/vm_mock.c +++ b/test/unittests/vm_mock.c @@ -3,6 +3,7 @@ * Licensed under the MIT License. See the LICENSE file. */ +#include #include EVMC_EXPORT void* evmc_create_aaa() @@ -14,3 +15,16 @@ EVMC_EXPORT void* evmc_create_eee_bbb() { return (void*)0xeeebbb; } + +EVMC_EXPORT void* evmc_create_failure() +{ + return NULL; +} + +EVMC_EXPORT struct evmc_instance* evmc_create_abi42() +{ + static struct evmc_instance instance = { + .abi_version = 42, + }; + return &instance; +}