/** * Copyright (C) 2018 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #pragma once #include #include "mongo/base/init.h" #include "mongo/config.h" /** * The `SHIM` mechanism allows for the creation of "weak-symbol-like" functions which can have their * actual implementation injected in the final binary without creating a link dependency upon any * actual implementation. One uses it like this: * * In a header: * ``` * class MyClass { * public: * static MONGO_DECLARE_SHIM((int)->std::string) helloWorldFunction; * }; * ``` * * In the corresponding C++ file (which is a link dependency): * ``` * MONGO_DEFINE_SHIM(MyClass::helloWorldFunction); * ``` * * And in any number of implementation files: * ``` * MONGO_REGISTER_SHIM(MyClass::helloWorldFunction)(int value)->std::string { * if (value == 42) { * return "Hello World"; * } else { * return "No way!"; * } * } * ``` * * This can be useful for making auto-registering and auto-constructing mock and release class * factories, among other useful things */ namespace mongo { template struct PrivateCall; /** * When declaring shim functions that should be private, they really need to be public; however, * this type can be used as a parameter to permit the function to only be called by the type * specified in the template parameter. */ template struct PrivateTo { private: friend PrivateCall; PrivateTo() = default; }; /** * When calling shim functions that should be private, you pass an immediately created instance of * the type `PrivateCall< T >`, where `T` is the type that `PrivateTo` requires as a template * parameter. */ template struct PrivateCall { private: friend T; PrivateCall() {} public: operator PrivateTo() { return {}; } }; } // namespace mongo namespace shim_detail { /** * This type, `storage`, is used as a workaround for needing C++17 `inline` variables. The template * static member is effectively `inline` already. */ template struct storage { static T data; }; template T storage::data = {}; } // namespace shim_detail #define MONGO_SHIM_DEPENDENTS ("ShimHooks") namespace mongo { #ifdef MONGO_CONFIG_CHECK_SHIM_DEPENDENCIES const bool checkShimsViaTUHook = true; #define MONGO_SHIM_TU_HOOK(name) \ name {} #else const bool checkShimsViaTUHook = false; #define MONGO_SHIM_TU_HOOK(name) #endif } // namespace mongo /** * Declare a shimmable function with signature `SHIM_SIGNATURE`. Declare such constructs in a C++ * header as static members of a class. */ #define MONGO_DECLARE_SHIM(/*SHIM_SIGNATURE*/...) MONGO_DECLARE_SHIM_1(__LINE__, __VA_ARGS__) #define MONGO_DECLARE_SHIM_1(LN, ...) MONGO_DECLARE_SHIM_2(LN, __VA_ARGS__) #define MONGO_DECLARE_SHIM_2(LN, ...) \ const struct ShimBasis_##LN { \ ShimBasis_##LN() = default; \ struct MongoShimImplGuts { \ template \ struct AbiCheckType { \ AbiCheckType() = default; \ }; \ using AbiCheck = AbiCheckType<>; \ struct LibTUHookTypeBase { \ LibTUHookTypeBase(); \ }; \ template \ struct LibTUHookType : LibTUHookTypeBase {}; \ using LibTUHook = LibTUHookType<>; \ struct ImplTUHookTypeBase { \ ImplTUHookTypeBase(); \ }; \ template \ struct ImplTUHookType : ImplTUHookTypeBase {}; \ using ImplTUHook = ImplTUHookType<>; \ \ static auto functionTypeHelper __VA_ARGS__; \ /* Workaround for Microsoft -- by taking the address of this function pointer, we \ * avoid the problems that their compiler has with default * arguments in deduced \ * typedefs. */ \ using function_type_pointer = decltype(&MongoShimImplGuts::functionTypeHelper); \ using function_type = std::remove_pointer_t; \ MongoShimImplGuts* abi(const AbiCheck* const) { \ return this; \ } \ MongoShimImplGuts* lib(const LibTUHook* const) { \ LibTUHook{}; \ return this; \ } \ MongoShimImplGuts* impl(const ImplTUHook* const) { \ MONGO_SHIM_TU_HOOK(ImplTUHook); \ return this; \ } \ virtual auto implementation __VA_ARGS__ = 0; \ \ using tag = \ std::tuple; \ }; \ \ using storage = shim_detail::storage; \ \ /* TODO: When the dependency graph is fixed, add the `impl()->` call to the call chain */ \ template \ auto operator()(Args&&... args) const \ noexcept(noexcept(storage::data->abi(nullptr)->lib(nullptr)->implementation( \ std::forward(args)...))) \ -> decltype(storage::data->abi(nullptr)->lib(nullptr)->implementation( \ std::forward(args)...)) { \ return storage::data->abi(nullptr)->lib(nullptr)->implementation( \ std::forward(args)...); \ } \ } /** * Evaluates to a string which represents the `MONGO_INITIALIZER` step name in which this specific * shim is registered. This can be useful to make sure that some initializers depend upon a shim's * execution and presence in a binary. */ #define MONGO_SHIM_DEPENDENCY(...) MONGO_SHIM_EVIL_STRINGIFY_(__VA_ARGS__) #define MONGO_SHIM_EVIL_STRINGIFY_(args) #args /** * Define a shimmable function with name `SHIM_NAME`, returning a value of type `RETURN_TYPE`, with * any arguments. This shim definition macro should go in the associated C++ file to the header * where a SHIM was defined. This macro does not emit a function definition, only the customization * point's machinery. */ #define MONGO_DEFINE_SHIM(/*SHIM_NAME*/...) MONGO_DEFINE_SHIM_1(__LINE__, __VA_ARGS__) #define MONGO_DEFINE_SHIM_1(LN, ...) MONGO_DEFINE_SHIM_2(LN, __VA_ARGS__) #define MONGO_DEFINE_SHIM_2(LN, ...) \ namespace { \ namespace shim_namespace##LN { \ using ShimType = decltype(__VA_ARGS__); \ ::mongo::Status initializerGroupStartup(::mongo::InitializerContext*) { \ return Status::OK(); \ } \ ::mongo::GlobalInitializerRegisterer _mongoInitializerRegisterer( \ std::string(MONGO_SHIM_DEPENDENCY(__VA_ARGS__)), \ {}, \ {MONGO_SHIM_DEPENDENTS}, \ mongo::InitializerFunction(initializerGroupStartup)); \ } /*namespace shim_namespace*/ \ } /*namespace*/ \ shim_namespace##LN::ShimType::MongoShimImplGuts::LibTUHookTypeBase::LibTUHookTypeBase() = \ default; \ shim_namespace##LN::ShimType __VA_ARGS__{}; /** * Define an implementation of a shimmable function with name `SHIM_NAME`. The compiler will check * supplied parameters for correctness. This shim registration macro should go in the associated * C++ implementation file to the header where a SHIM was defined. Such a file would be a mock * implementation or a real implementation, for example. */ #define MONGO_REGISTER_SHIM(/*SHIM_NAME*/...) MONGO_REGISTER_SHIM_1(__LINE__, __VA_ARGS__) #define MONGO_REGISTER_SHIM_1(LN, ...) MONGO_REGISTER_SHIM_2(LN, __VA_ARGS__) #define MONGO_REGISTER_SHIM_2(LN, ...) \ namespace { \ namespace shim_namespace##LN { \ using ShimType = decltype(__VA_ARGS__); \ \ class Implementation final : public ShimType::MongoShimImplGuts { \ /* Some compilers don't work well with the trailing `override` in this kind of \ * function declaration. */ \ ShimType::MongoShimImplGuts::function_type implementation; /* override */ \ }; \ \ ::mongo::Status createInitializerRegistration(::mongo::InitializerContext* const) { \ static Implementation impl; \ ShimType::storage::data = &impl; \ return Status::OK(); \ } \ \ const ::mongo::GlobalInitializerRegisterer registrationHook{ \ std::string(MONGO_SHIM_DEPENDENCY(__VA_ARGS__) "_registration"), \ {}, \ {MONGO_SHIM_DEPENDENCY(__VA_ARGS__), MONGO_SHIM_DEPENDENTS}, \ mongo::InitializerFunction(createInitializerRegistration)}; \ } /*namespace shim_namespace*/ \ } /*namespace*/ \ \ shim_namespace##LN::ShimType::MongoShimImplGuts::ImplTUHookTypeBase::ImplTUHookTypeBase() = \ default; \ \ auto shim_namespace##LN::Implementation::implementation /* After this point someone just \ writes the signature's arguments \ and return value (using arrow \ notation). Then they write the \ body. */ /** * Define an overriding implementation of a shimmable function with `SHIM_NAME`. The compiler will * check the supplied parameters for correctness. This shim override macro should go in the * associated C++ implementation file to the header where a SHIM was defined. Such a file * specifying an override would be a C++ implementation used by a mongodb extension module. * This creates a runtime dependency upon the original registration being linked in. */ #define MONGO_OVERRIDE_SHIM(/*SHIM_NAME*/...) MONGO_OVERRIDE_SHIM_1(__LINE__, __VA_ARGS__) #define MONGO_OVERRIDE_SHIM_1(LN, ...) MONGO_OVERRIDE_SHIM_2(LN, __VA_ARGS__) #define MONGO_OVERRIDE_SHIM_2(LN, ...) \ namespace { \ namespace shim_namespace##LN { \ using ShimType = decltype(__VA_ARGS__); \ \ class OverrideImplementation final : public ShimType::MongoShimImplGuts { \ /* Some compilers don't work well with the trailing `override` in this kind of \ * function declaration. */ \ ShimType::MongoShimImplGuts::function_type implementation; /* override */ \ }; \ \ ::mongo::Status createInitializerOverride(::mongo::InitializerContext* const) { \ static OverrideImplementation overrideImpl; \ ShimType::storage::data = &overrideImpl; \ return Status::OK(); \ } \ \ const ::mongo::GlobalInitializerRegisterer overrideHook{ \ std::string(MONGO_SHIM_DEPENDENCY(__VA_ARGS__) "_override"), \ {MONGO_SHIM_DEPENDENCY( \ __VA_ARGS__) "_registration"}, /* Override happens after first registration */ \ {MONGO_SHIM_DEPENDENCY(__VA_ARGS__), /* Provides impl for this shim */ \ MONGO_SHIM_DEPENDENTS}, /* Still a shim registration */ \ mongo::InitializerFunction(createInitializerOverride)}; \ } /*namespace shim_namespace*/ \ } /*namespace*/ \ \ auto shim_namespace##LN::OverrideImplementation:: \ implementation /* After this point someone just writes the signature's arguments and \ return value (using arrow notation). Then they write the body. */