From bcbe42ab23ce8cb7016d66f40c95bb3fa85dbddc Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 29 May 2019 21:57:03 -0400 Subject: [PATCH] Add API version checking for modules. Checking now actively implemented for dynamic modules in a fully backwards compatible way. --- CHANGELOG.md | 4 ++++ src/core/capi.c | 13 +++++++------ src/core/corelib.c | 28 +++++++++++++++++++++++++++- src/include/janet.h | 40 +++++++++++++++++++++++----------------- src/include/janetconf.h | 6 +++++- 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 181271b6..d50d101a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## Unreleased +- Add API compatibility checking for modules. This will let native modules not load + when the host program is not of a compatible version or configuration. + ## 0.6.0 - 2019-05-29 - `file/close` returns exit code when closing file opened with `file/popen`. - Add `os/rename` diff --git a/src/core/capi.c b/src/core/capi.c index 941ebd55..33a09792 100644 --- a/src/core/capi.c +++ b/src/core/capi.c @@ -26,14 +26,15 @@ #include "fiber.h" #endif -static JanetBuildConfig *api_build_config = &(JanetBuildConfig) { - .api_version = JANET_API_VERSION, - .single_threaded = JANET_SINGLE_THREADED_BIT, - .nanbox = JANET_NANBOX_BIT +static const JanetBuildConfig api_build_config = (JanetBuildConfig) { + JANET_VERSION_MAJOR, + JANET_VERSION_MINOR, + JANET_VERSION_PATCH, + JANET_CURRENT_CONFIG_BITS }; -const JanetBuildConfig *janet_build_config() { - return api_build_config; +const JanetBuildConfig *janet_config_host(void) { + return &api_build_config; } void janet_panicv(Janet message) { diff --git a/src/core/corelib.c b/src/core/corelib.c index 6b4423e2..ec603e2c 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -63,7 +63,30 @@ JanetModule janet_native(const char *name, const uint8_t **error) { } init = (JanetModule) symbol_clib(lib, "_janet_init"); if (!init) { - *error = janet_cstring("could not find _janet_init symbol"); + *error = janet_cstring("could not find the _janet_init symbol"); + return NULL; + } + JanetBuildConfig(*modconf_getter)(void) = symbol_clib(lib, "_janet_mod_config"); + if (!modconf_getter) { + *error = janet_cstring("could not find the _janet_mod_config symbol"); + return NULL; + } + JanetBuildConfig modconf = modconf_getter(); + JanetBuildConfig host = janet_config_current(); + if (host.major != modconf.major || + host.minor < modconf.minor || + host.bits != modconf.bits) { + char errbuf[128]; + sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)", + host.major, + host.minor, + host.patch, + host.bits, + modconf.major, + modconf.minor, + modconf.patch, + modconf.bits); + *error = janet_cstring(errbuf); return NULL; } return init; @@ -830,6 +853,9 @@ JanetTable *janet_core_env(JanetTable *replacements) { JDOC("The version number of the running janet program.")); janet_def(env, "janet/build", janet_cstringv(JANET_BUILD), JDOC("The build identifier of the running janet program.")); + janet_def(env, "janet/config-bits", janet_wrap_integer(JANET_CURRENT_CONFIG_BITS), + JDOC("The flag set of config options from janetconf.h which is used to check " + "if native modules are compatible with the host program.")); /* Allow references to the environment */ janet_def(env, "_env", janet_wrap_table(env), JDOC("The environment table for the current scope.")); diff --git a/src/include/janet.h b/src/include/janet.h index a83b9ab5..3ac4dd9a 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -31,8 +31,6 @@ extern "C" { #include "janetconf.h" -#define JANET_API_VERSION 1 - #ifndef JANET_VERSION #define JANET_VERSION "latest" #endif @@ -186,26 +184,38 @@ extern "C" { #endif #endif - /* Runtime config constants */ #ifdef JANET_NO_NANBOX #define JANET_NANBOX_BIT 0 #else -#define JANET_NANBOX_BIT 1 +#define JANET_NANBOX_BIT 0x1 #endif #ifdef JANET_SINGLE_THREADED -#define JANET_SINGLE_THREADED_BIT 1 +#define JANET_SINGLE_THREADED_BIT 0x2 #else #define JANET_SINGLE_THREADED_BIT 0 #endif +#define JANET_CURRENT_CONFIG_BITS \ + (JANET_SINGLE_THREADED_BIT | \ + JANET_NANBOX_BIT) + +/* Represents the settings used to compile Janet, as well as the version */ typedef struct { - int api_version; - int single_threaded : 1; - int nanbox : 1; + unsigned major; + unsigned minor; + unsigned patch; + unsigned bits; } JanetBuildConfig; +/* Get config of current compilation unit. */ +#define janet_config_current() ((JanetBuildConfig){ \ + JANET_VERSION_MAJOR, \ + JANET_VERSION_MINOR, \ + JANET_VERSION_PATCH, \ + JANET_CURRENT_CONFIG_BITS }) + /***** END SECTION CONFIG *****/ /***** START SECTION TYPES *****/ @@ -1276,15 +1286,11 @@ JANET_API void janet_register(const char *name, JanetCFunction cfun); /* New C API */ -#define JANET_MODULE_ENTRY JANET_API void _janet_init - -JANET_API int janet_api_version(); -JANET_API const JanetBuildConfig *janet_build_config(); - -#define janet_api_compatible() \ - ((janet_api_build_config()->api_version == JANET_API_VERSION) \ - && (janet_api_build_config()->nanbox == JANET_NANBOX_BIT) \ - && (janet_api_build_config()->single_threaded == JANET_SINGLE_THREADED_BIT)) +#define JANET_MODULE_ENTRY \ + JANET_API JanetBuildConfig _janet_mod_config(void) { \ + return janet_config_current(); \ + } \ + JANET_API void _janet_init JANET_API void janet_panicv(Janet message); JANET_API void janet_panic(const char *message); diff --git a/src/include/janetconf.h b/src/include/janetconf.h index 09d6cbe5..12eff37e 100644 --- a/src/include/janetconf.h +++ b/src/include/janetconf.h @@ -25,7 +25,11 @@ #ifndef JANETCONF_H #define JANETCONF_H -#define JANET_VERSION "0.6.0" +#define JANET_VERSION_MAJOR 0 +#define JANET_VERSION_MINOR 6 +#define JANET_VERSION_PATCH 0 +#define JANET_VERSION_EXTRA "-dev" +#define JANET_VERSION "0.6.0-dev" /* #define JANET_BUILD "local" */