diff --git a/CHANGELOG.md b/CHANGELOG.md index e058137..2fb192d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Changed: [[#293](https://github.com/ethereum/evmc/pull/293)] In C++ API `evmc::result::raw()` renamed to `evmc::result::release_raw()`. +- Changed: [[#311](https://github.com/ethereum/evmc/pull/311)] + In `evmc_load_and_create()` the `error_code` is optional (can be `NULL`). - Fixed: [[#261](https://github.com/ethereum/evmc/issues/261), [#263](https://github.com/ethereum/evmc/pull/263)] diff --git a/include/evmc/loader.h b/include/evmc/loader.h index a6eca6c..d8531af 100644 --- a/include/evmc/loader.h +++ b/include/evmc/loader.h @@ -44,10 +44,10 @@ enum evmc_loader_error_code }; /** - * Dynamically loads the shared object (DLL) with an EVM implementation. + * Dynamically loads the EVMC module with a VM implementation. * - * 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. + * This function tries to open a dynamically loaded library (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_LOADER_CANNOT_OPEN error * code is signaled and NULL is returned. @@ -77,28 +77,36 @@ enum evmc_loader_error_code * 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_LOADER_INVALID_ARGUMENT is - * signaled. + * @param filename The null terminated path (absolute or relative) to an EVMC module + * (dynamically loaded library) containing the VM implementation. + * If the value is NULL, an empty C-string 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_LOADER_SUCCESS on success or any other error code as described above. - * @return The pointer to the EVM create function or NULL. + * @return The pointer to the EVM create function or NULL in case of error. */ 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. + * Dynamically loads the EVMC module 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). + * - ::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. + * + * @param filename The null terminated path (absolute or relative) to an EVMC module + * (dynamically loaded library) containing the VM implementation. + * If the value is NULL, an empty C-string 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_LOADER_SUCCESS on success or any other error code as described above. + * @return The pointer to the created VM or NULL in case of error. */ struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code); diff --git a/lib/loader/loader.c b/lib/loader/loader.c index 72b2afa..51d7a85 100644 --- a/lib/loader/loader.c +++ b/lib/loader/loader.c @@ -212,22 +212,29 @@ struct evmc_instance* evmc_load_and_create(const char* filename, if (!create_fn) return NULL; + enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; + struct evmc_instance* instance = create_fn(); if (!instance) { - *error_code = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, - "creating EVMC instance of %s has failed", filename); - return NULL; + ec = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, + "creating EVMC instance of %s has failed", filename); + goto exit; } if (!evmc_is_abi_compatible(instance)) { + ec = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH, + "EVMC ABI version %d of %s mismatches the expected version %d", + instance->abi_version, filename, EVMC_ABI_VERSION); evmc_destroy(instance); - *error_code = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH, - "EVMC ABI version %d of %s mismatches the expected version %d", - instance->abi_version, filename, EVMC_ABI_VERSION); - return NULL; + instance = NULL; + goto exit; } +exit: + if (error_code) + *error_code = ec; + return instance; } diff --git a/test/unittests/test_loader.cpp b/test/unittests/test_loader.cpp index 010cbd6..903fc35 100644 --- a/test/unittests/test_loader.cpp +++ b/test/unittests/test_loader.cpp @@ -301,6 +301,11 @@ TEST_F(loader, load_and_create_failure) EXPECT_TRUE(vm == nullptr); EXPECT_EQ(ec, EVMC_LOADER_INSTANCE_CREATION_FAILURE); EXPECT_STREQ(evmc_last_error_msg(), "creating EVMC instance of failure.vm has failed"); + EXPECT_TRUE(evmc_last_error_msg() == nullptr); + + vm = evmc_load_and_create(evmc_test_library_path, nullptr); + EXPECT_TRUE(vm == nullptr); + EXPECT_STREQ(evmc_last_error_msg(), "creating EVMC instance of failure.vm has failed"); } TEST_F(loader, load_and_create_abi_mismatch) @@ -315,5 +320,11 @@ TEST_F(loader, load_and_create_abi_mismatch) "EVMC ABI version 1985 of abi1985.vm mismatches the expected version " + std::to_string(EVMC_ABI_VERSION); EXPECT_EQ(evmc_last_error_msg(), expected_error_msg); + EXPECT_TRUE(evmc_last_error_msg() == nullptr); + EXPECT_EQ(destroy_count, create_count); + + vm = evmc_load_and_create(evmc_test_library_path, nullptr); + EXPECT_TRUE(vm == nullptr); + EXPECT_EQ(evmc_last_error_msg(), expected_error_msg); EXPECT_EQ(destroy_count, create_count); }