Add new Mono embedding API to consume binary runtimeconfig format (#49740)
authorFan Yang <52458914+fanyang-mono@users.noreply.github.com>
Mon, 22 Mar 2021 15:55:36 +0000 (11:55 -0400)
committerGitHub <noreply@github.com>
Mon, 22 Mar 2021 15:55:36 +0000 (11:55 -0400)
* Add new Mono embedding API to consume binary runtimeconfig format

* Merge properties

* Remove redundant macro

* Move string process to the install function

* Check if runtime_config_arg exist

* Move file processing to install function

* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Update src/mono/mono/metadata/appdomain.c

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
* Remove redundant intermediate copy

* Remove extra memory allocation

* Update src/mono/mono/mini/mono-private-unstable.h

Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>
Co-authored-by: Ryan Lucia <ryan@luciaonline.net>
src/mono/mono/metadata/appdomain.c
src/mono/mono/metadata/domain-internals.h
src/mono/mono/mini/mono-private-unstable.h
src/mono/mono/mini/monovm.c

index 170560c..a8a7c59 100644 (file)
@@ -92,8 +92,12 @@ typedef struct
 static gboolean no_exec = FALSE;
 
 static int n_appctx_props;
-static gunichar2 **appctx_keys;
-static gunichar2 **appctx_values;
+static char **appctx_keys;
+static char **appctx_values;
+
+static MonovmRuntimeConfigArguments *runtime_config_arg;
+static MonovmRuntimeConfigArgumentsCleanup runtime_config_cleanup_fn;
+static gpointer runtime_config_user_data;
 
 static const char *
 mono_check_corlib_version_internal (void);
@@ -122,6 +126,12 @@ add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
 static void
 add_assembly_to_alc (MonoAssemblyLoadContext *alc, MonoAssembly *ass);
 
+static const char *
+runtimeconfig_json_get_buffer (MonovmRuntimeConfigArguments *arg, MonoFileMap **file_map, gpointer *buf_handle);
+
+static void
+runtimeconfig_json_read_props (const char *ptr, const char **endp, int nprops, gunichar2 **dest_keys, gunichar2 **dest_values);
+
 static MonoLoadFunc load_function = NULL;
 
 /* Lazy class loading functions */
@@ -1239,15 +1249,23 @@ void
 mono_runtime_register_appctx_properties (int nprops, const char **keys,  const char **values)
 {
        n_appctx_props = nprops;
-       appctx_keys = g_new0 (gunichar2*, nprops);
-       appctx_values = g_new0 (gunichar2*, nprops);
+       appctx_keys = g_new0 (char *, n_appctx_props);
+       appctx_values = g_new0 (char *, n_appctx_props);
 
        for (int i = 0; i < nprops; ++i) {
-               appctx_keys [i] = g_utf8_to_utf16 (keys [i], strlen (keys [i]), NULL, NULL, NULL);
-               appctx_values [i] = g_utf8_to_utf16 (values [i], strlen (values [i]), NULL, NULL, NULL);
+               appctx_keys [i] = g_strdup (keys [i]);
+               appctx_values [i] = g_strdup (values [i]);
        }
 }
 
+void
+mono_runtime_register_runtimeconfig_json_properties (MonovmRuntimeConfigArguments *arg, MonovmRuntimeConfigArgumentsCleanup cleanup_fn, void *user_data)
+{
+       runtime_config_arg = arg;
+       runtime_config_cleanup_fn = cleanup_fn;
+       runtime_config_user_data = user_data;
+}
+
 static GENERATE_GET_CLASS_WITH_CACHE (appctx, "System", "AppContext")
 
 /* Install properties into AppContext */
@@ -1256,27 +1274,121 @@ mono_runtime_install_appctx_properties (void)
 {
        ERROR_DECL (error);
        gpointer args [3];
+       int n_runtimeconfig_json_props = 0;
+       int n_combined_props;
+       gunichar2 **combined_keys;
+       gunichar2 **combined_values;
+       MonoFileMap *runtimeconfig_json_map = NULL;
+       gpointer runtimeconfig_json_map_handle = NULL;
+       const char *buffer_start = runtimeconfig_json_get_buffer (runtime_config_arg, &runtimeconfig_json_map, &runtimeconfig_json_map_handle);
+       const char *buffer = buffer_start;
 
        MonoMethod *setup = mono_class_get_method_from_name_checked (mono_class_get_appctx_class (), "Setup", 3, 0, error);
        g_assert (setup);
 
        // FIXME: TRUSTED_PLATFORM_ASSEMBLIES is very large
 
+       // Combine and convert properties
+       if (buffer)
+               n_runtimeconfig_json_props = mono_metadata_decode_value (buffer, &buffer);
+
+       n_combined_props = n_appctx_props + n_runtimeconfig_json_props;
+       combined_keys = g_new0 (gunichar2 *, n_combined_props);
+       combined_values = g_new0 (gunichar2 *, n_combined_props);
+
+       for (int i = 0; i < n_appctx_props; ++i) {
+               combined_keys [i] = g_utf8_to_utf16 (appctx_keys [i], -1, NULL, NULL, NULL);
+               combined_values [i] = g_utf8_to_utf16 (appctx_values [i], -1, NULL, NULL, NULL);
+       }
+
+       runtimeconfig_json_read_props (buffer, &buffer, n_runtimeconfig_json_props, combined_keys + n_appctx_props, combined_values + n_appctx_props);
+
        /* internal static unsafe void Setup(char** pNames, char** pValues, int count) */
-       args [0] = appctx_keys;
-       args [1] = appctx_values;
-       args [2] = &n_appctx_props;
+       args [0] = combined_keys;
+       args [1] = combined_values;
+       args [2] = &n_combined_props;
 
        mono_runtime_invoke_checked (setup, NULL, args, error);
        mono_error_assert_ok (error);
 
+       if (runtimeconfig_json_map != NULL) {
+               mono_file_unmap ((gpointer)buffer_start, runtimeconfig_json_map_handle);
+               mono_file_map_close (runtimeconfig_json_map);
+       }
+
+       // Call user defined cleanup function
+       if (runtime_config_cleanup_fn)
+               (*runtime_config_cleanup_fn) (runtime_config_arg, runtime_config_user_data);
+
        /* No longer needed */
+       for (int i = 0; i < n_combined_props; ++i) {
+               g_free (combined_keys [i]);
+               g_free (combined_values [i]);
+       }
+       g_free (combined_keys);
+       g_free (combined_values);
        for (int i = 0; i < n_appctx_props; ++i) {
                g_free (appctx_keys [i]);
                g_free (appctx_values [i]);
        }
        g_free (appctx_keys);
        g_free (appctx_values);
+
        appctx_keys = NULL;
        appctx_values = NULL;
+       if (runtime_config_arg) {
+               runtime_config_arg = NULL;
+               runtime_config_cleanup_fn = NULL;
+               runtime_config_user_data = NULL;
+       }
+}
+
+static const char *
+runtimeconfig_json_get_buffer (MonovmRuntimeConfigArguments *arg, MonoFileMap **file_map, gpointer *buf_handle)
+{
+       if (arg != NULL) {
+               switch (arg->kind) {
+               case 0: {
+                       char *buffer = NULL;
+                       guint64 file_len = 0;
+
+                       *file_map = mono_file_map_open (arg->runtimeconfig.name.path);
+                       g_assert (*file_map);
+                       file_len = mono_file_map_size (*file_map);
+                       g_assert (file_len > 0);
+                       buffer = (char *)mono_file_map (file_len, MONO_MMAP_READ|MONO_MMAP_PRIVATE, mono_file_map_fd (*file_map), 0, buf_handle);
+                       g_assert (buffer);
+                       return buffer;
+               }
+               case 1: {
+                       *file_map = NULL;
+                       *buf_handle = NULL;
+                       return arg->runtimeconfig.data.data;
+               }
+               default:
+                       g_assert_not_reached ();
+               }
+       }
+
+       *file_map = NULL;
+       *buf_handle = NULL;
+       return NULL;    
+}
+
+static void
+runtimeconfig_json_read_props (const char *ptr, const char **endp, int nprops, gunichar2 **dest_keys, gunichar2 **dest_values)
+{
+       for (int i = 0; i < nprops; ++i) {
+               int str_len;
+
+               str_len = mono_metadata_decode_value (ptr, &ptr);
+               dest_keys [i] = g_utf8_to_utf16 (ptr, str_len, NULL, NULL, NULL);
+               ptr += str_len;
+
+               str_len = mono_metadata_decode_value (ptr, &ptr);
+               dest_values [i] = g_utf8_to_utf16 (ptr, str_len, NULL, NULL, NULL);
+               ptr += str_len;
+       }
+
+       *endp = ptr;
 }
index 9521b9c..fb17d9f 100644 (file)
@@ -20,6 +20,7 @@
 #include <mono/metadata/loader-internals.h>
 #include <mono/metadata/mempool-internals.h>
 #include <mono/metadata/handle-decl.h>
+#include <mono/mini/mono-private-unstable.h>
 
 G_BEGIN_DECLS
 
@@ -450,6 +451,9 @@ void
 mono_runtime_register_appctx_properties (int nprops, const char **keys,  const char **values);
 
 void
+mono_runtime_register_runtimeconfig_json_properties (MonovmRuntimeConfigArguments *arg, MonovmRuntimeConfigArgumentsCleanup cleanup_fn, void *user_data);
+
+void
 mono_runtime_install_appctx_properties (void);
 
 gboolean 
index 4d746dd..b8e6724 100644 (file)
 #include <mono/utils/mono-publib.h>
 #include <mono/metadata/image.h>
 
+typedef struct {
+       uint32_t kind; // 0 = Path of runtimeconfig.blob, 1 = pointer to image data, >= 2 undefined
+       union {
+               struct {
+                       const char *path;
+               } name;
+               struct {
+                       const char *data;
+                       uint32_t data_len;
+               } data;
+       } runtimeconfig;
+} MonovmRuntimeConfigArguments;
+
+typedef void (*MonovmRuntimeConfigArgumentsCleanup)          (MonovmRuntimeConfigArguments *args, void *user_data);
+
 /* These are used to load the AOT data for aot images compiled with MONO_AOT_FILE_FLAG_SEPARATE_DATA */
 /*
  * Return the AOT data for ASSEMBLY. SIZE is the size of the data. OUT_HANDLE should be set to a handle which is later
@@ -29,6 +44,9 @@ mono_install_load_aot_data_hook (MonoLoadAotDataFunc load_func, MonoFreeAotDataF
 MONO_API int
 monovm_initialize (int propertyCount, const char **propertyKeys, const char **propertyValues);
 
+MONO_API int 
+monovm_runtimeconfig_initialize (MonovmRuntimeConfigArguments *arg, MonovmRuntimeConfigArgumentsCleanup cleanup_fn, void *user_data);
+
 //#ifdef HOST_WASM
 typedef void* (*MonoWasmGetNativeToInterpTramp) (MonoMethod *method, void *extra_arg);
 
index ad6be1c..a993480 100644 (file)
@@ -226,6 +226,14 @@ monovm_initialize (int propertyCount, const char **propertyKeys, const char **pr
        return 0;
 }
 
+// Initialize monovm with properties set by runtimeconfig.json. Primarily used by mobile targets.
+int
+monovm_runtimeconfig_initialize (MonovmRuntimeConfigArguments *arg, MonovmRuntimeConfigArgumentsCleanup cleanup_fn, void *user_data)
+{
+       mono_runtime_register_runtimeconfig_json_properties (arg, cleanup_fn, user_data);
+       return 0;
+}
+
 int
 monovm_execute_assembly (int argc, const char **argv, const char *managedAssemblyPath, unsigned int *exitCode)
 {