From e9969921c0e5a40776f01fd2efcec01f845df937 Mon Sep 17 00:00:00 2001 From: Paolo Molaro Date: Mon, 21 Jun 2004 13:32:25 +0000 Subject: [PATCH] More embedding samples. svn path=/trunk/mono/; revision=30034 --- samples/Makefile.am | 4 +- samples/embed/invoke.cs | 50 +++++ samples/embed/test-invoke.c | 356 ++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 samples/embed/invoke.cs create mode 100644 samples/embed/test-invoke.c diff --git a/samples/Makefile.am b/samples/Makefile.am index 0d535d35449..d6769d8d70a 100644 --- a/samples/Makefile.am +++ b/samples/Makefile.am @@ -3,4 +3,6 @@ dist-hook: mkdir $(distdir)/embed mkdir $(distdir)/profiler cp $(srcdir)/profiler/sample.c $(distdir)/profiler - cp $(srcdir)/embed/test.cs $(srcdir)/embed/teste.c $(distdir)/embed \ No newline at end of file + cp $(srcdir)/embed/test.cs $(srcdir)/embed/teste.c $(distdir)/embed + cp $(srcdir)/embed/test-metadata.c $(distdir)/embed + cp $(srcdir)/embed/test-invoke.c $(srcdir)/embed/invoke.cs $(distdir)/embed diff --git a/samples/embed/invoke.cs b/samples/embed/invoke.cs new file mode 100644 index 00000000000..19e2775d3a5 --- /dev/null +++ b/samples/embed/invoke.cs @@ -0,0 +1,50 @@ +using System; + +namespace Embed { + class MyType { + int val = 5; + string str = "hello"; + + MyType () { + Console.WriteLine ("In ctor val is: {0}", val); + Console.WriteLine ("In ctor str is: {0}", str); + } + + MyType (int v, byte[] array) { + Console.WriteLine ("In ctor (int, byte[]) got value: {0}, array len: {1}", v, array.Length); + } + + void method () { + Console.WriteLine ("In method val is {0}", val); + Console.WriteLine ("In method str is: {0}", str); + } + + int Value { + get { + return val; + } + } + + string Message { + get { + return str; + } + } + + void Values (ref int v, ref string s) { + Console.WriteLine ("In Values () v is {0}", v); + Console.WriteLine ("In Values () s is: {0}", s); + v = val; + s = str; + } + + static void Fail () { + throw new Exception (); + } + + static void Main () { + /* we do nothing here... */ + } + } +} + diff --git a/samples/embed/test-invoke.c b/samples/embed/test-invoke.c new file mode 100644 index 00000000000..899a6b228a8 --- /dev/null +++ b/samples/embed/test-invoke.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include +#include + +/* + * Simple mono embedding example. + * We show how to create objects and invoke methods and set fields in them. + * Compile with: + * gcc -Wall -o test-invoke test-invoke.c `pkg-config --cflags --libs mono` -lm + * mcs invoke.cs + * Run with: + * ./test-invoke invoke.exe + */ + +typedef struct +{ + MonoDomain *domain; + const char *file; + int argc; + char **argv; +} MainThreadArgs; + +static void +access_valuetype_field (MonoObject *obj) +{ + MonoClass *klass; + MonoClassField *field; + int val; + + klass = mono_object_get_class (obj); + + /* Now we'll change the value of the 'val' field (see invoke.cs) */ + field = mono_class_get_field_from_name (klass, "val"); + + /* This time we also add a bit of error checking... */ + if (!field) { + fprintf (stderr, "Can't find field val in MyType\n"); + exit (1); + } + /* Check that val is an int (if you're paranoid or if you need to + * show how this API is used) + */ + if (mono_type_get_type (mono_field_get_type (field)) != MONO_TYPE_I4) { + fprintf (stderr, "Field val is not a 32 bit integer\n"); + exit (1); + } + + /* Note we pass a pointer to the value */ + mono_field_get_value (obj, field, &val); + printf ("Value of field is: %d\n", val); + val = 10; + + /* Note we pass a pointer to the value here as well */ + mono_field_set_value (obj, field, &val); + +} + +static void +access_reference_field (MonoObject *obj) +{ + MonoClass *klass; + MonoDomain *domain; + MonoClassField *str; + MonoString *strval; + char *p; + + klass = mono_object_get_class (obj); + domain = mono_object_get_domain (obj); + + /* Now we'll see that a reference type is handled slightly differently. + * First, get the MonoClassField representing it. + */ + str = mono_class_get_field_from_name (klass, "str"); + + /* No change here, we always pass a pointer */ + mono_field_get_value (obj, str, &strval); + + /* get the string in UTF-8 encoding to print it */ + p = mono_string_to_utf8 (strval); + printf ("Value of str is: %s\n", p); + /* we need to free the result from mono_string_to_utf8 () */ + g_free (p); + + /* string are immutable, so we need to create a different string */ + strval = mono_string_new (domain, "hello from the embedding API"); + + /* Here is the slight difference: for reference types we pass + * the pointer directly, instead of a pointer to the value. + */ + mono_field_set_value (obj, str, strval); + +} + +/* Demostrate how to call methods */ +static void +call_methods (MonoObject *obj) +{ + MonoClass *klass; + MonoDomain *domain; + MonoMethod *method = NULL, *m = NULL, *ctor = NULL, *fail = NULL, *mvalues; + MonoProperty *prop; + MonoObject *result, *exception; + MonoString *str; + char *p; + gpointer iter; + gpointer args [2]; + int val; + + klass = mono_object_get_class (obj); + domain = mono_object_get_domain (obj); + + /* retrieve all the methods we need */ + iter = NULL; + while ((m = mono_class_get_methods (klass, &iter))) { + if (strcmp (mono_method_get_name (m), "method") == 0) { + method = m; + } else if (strcmp (mono_method_get_name (m), "Fail") == 0) { + fail = m; + } else if (strcmp (mono_method_get_name (m), "Values") == 0) { + mvalues = m; + } else if (strcmp (mono_method_get_name (m), ".ctor") == 0) { + /* Check it's the ctor that takes two args: + * as you see a contrsuctor is a method like any other. + */ + MonoMethodSignature * sig = mono_method_signature (m); + if (mono_signature_get_param_count (sig) == 2) { + ctor = m; + } + } + } + /* Now we'll call method () on obj: since it takes no arguments + * we can pass NULL as the third argument to mono_runtime_invoke (). + * The method will print the updated value. + */ + mono_runtime_invoke (method, obj, NULL, NULL); + + /* mono_object_new () doesn't call any constructor: this means that + * we'll have to invoke the constructor if needed ourselves. Note: + * invoking a constructor is no different than calling any other method, + * so we'll still call mono_runtime_invoke (). This also means that we + * can invoke a constructor at any time, like now. + * First, setup the array of arguments and their values. + */ + + /* As usual, we use the address of the data for valuetype arguments */ + val = 7; + args [0] = &val; + /* and the pointer for reference types: mono_array_new () returns a MonoArray* */ + args [1] = mono_array_new (domain, mono_get_byte_class (), 256); + mono_runtime_invoke (ctor, obj, args, NULL); + + /* A property exists only as a metadata entity, so getting or setting the value + * is nothing more than calling mono_runtime_invoke () on the getter or setter method. + */ + prop = mono_class_get_property_from_name (klass, "Value"); + method = mono_property_get_get_method (prop); + result = mono_runtime_invoke (method, obj, NULL, NULL); + /* mono_runtime_invoke () always boxes the return value if it's a valuetype */ + val = *(int*)mono_object_unbox (result); + + printf ("Value of val from property is: %d\n", val); + + /* we also have an helper method: note that reference types are returned as is */ + prop = mono_class_get_property_from_name (klass, "Message"); + str = (MonoString*)mono_property_get_value (prop, obj, NULL, NULL); + /* get the string in UTF-8 encoding to print it */ + p = mono_string_to_utf8 (str); + printf ("Value of str from property is: %s\n", p); + /* we need to free the result from mono_string_to_utf8 () */ + g_free (p); + + /* Now we'll show two things: + * 1) static methods are invoked with mono_runtime_invoke () as well, + * we just pass NULL as the second argument. + * 2) we can catch exceptions thrown by the called method. + * Note: fail is declared as static void Fail () in invoke.cs. + * We first set result to NULL: if after the invocation it will have + * a different value, it will be the exception that was thrown from + * the Fail () method. Note that if an exception was thrown, the return + * value (if any) is undefined and can't be used in any way (yes, the above + * invocations don't have this type of error checking to make things simpler). + */ + exception = NULL; + mono_runtime_invoke (fail, NULL, NULL, &exception); + if (exception) { + printf ("An exception was thrown in Fail ()\n"); + } + + /* Now let's see how to handle methods that take by ref arguments: + * Valuetypes continue to be passed as pointers to the data. + * Reference arguments passed by ref (ref or out is the same) + * are handled the same way: a pointer to the pointer is used + * (so that the result can be read back). + * Small note: in this case (a System.Int32 valuetype) we can just + * use &val where val is a C 32 bit integer. In the general case + * unmanaged code doesn't know the size of a valuetype, since the + * runtime may decide to lay it out in what it thinks is a better way + * (unless ExplicitLayout is set). To avoid issues, the best thing is to + * create an object of the valuetype's class and retrieve the pointer + * to the data with the mono_object_unbox () function. + */ + val = 100; + str = mono_string_new (domain, "another string"); + args [0] = &val; + args [1] = &str; + mono_runtime_invoke (mvalues, obj, args, NULL); + /* get the string in UTF-8 encoding to print it */ + p = mono_string_to_utf8 (str); + printf ("Values of str/val from Values () are: %s/%d\n", p, val); + /* we need to free the result from mono_string_to_utf8 () */ + g_free (p); +} + +static void +more_methods (MonoDomain *domain) +{ + MonoClass *klass; + MonoMethodDesc* mdesc; + MonoMethod *method, *vtmethod; + MonoString *str; + MonoObject *obj; + char *p; + int val; + + /* Now let's call an instance method on a valuetype. There are two + * different case: + * 1) calling a virtual method defined in a base class, like ToString (): + * we need to pass the value boxed in an object + * 2) calling a normal instance method: in this case + * we pass the address to the valuetype as the second argument + * instead of an object. + * First some initialization. + */ + val = 25; + klass = mono_get_int32_class (); + obj = mono_value_box (domain, klass, &val); + + /* A different way to search for a method */ + mdesc = mono_method_desc_new (":ToString()", FALSE); + vtmethod = mono_method_desc_search_in_class (mdesc, klass); + + str = (MonoString*)mono_runtime_invoke (vtmethod, &val, NULL, NULL); + /* get the string in UTF-8 encoding to print it */ + p = mono_string_to_utf8 (str); + printf ("25.ToString (): %s\n", p); + /* we need to free the result from mono_string_to_utf8 () */ + g_free (p); + + /* Now: see how the result is different if we search for the ToString () + * method in System.Object: mono_runtime_invoke () doesn't do any sort of + * virtual method invocation: it calls the exact method that it was given + * to execute. If a virtual call is needed, mono_object_get_virtual_method () + * can be called. + */ + method = mono_method_desc_search_in_class (mdesc, mono_get_object_class ()); + str = (MonoString*)mono_runtime_invoke (method, obj, NULL, NULL); + /* get the string in UTF-8 encoding to print it */ + p = mono_string_to_utf8 (str); + printf ("25.ToString (), from System.Object: %s\n", p); + /* we need to free the result from mono_string_to_utf8 () */ + g_free (p); + + /* Now get the method that overrides ToString () in obj */ + vtmethod = mono_object_get_virtual_method (obj, method); + if (mono_class_is_valuetype (mono_method_get_class (vtmethod))) { + printf ("Need to unbox this for call to virtual ToString () for %s\n", mono_class_get_name (klass)); + } + + mono_method_desc_free (mdesc); +} + +static void +create_object (MonoDomain *domain, MonoImage *image) +{ + MonoClass *klass; + MonoObject *obj; + + klass = mono_class_from_name (image, "Embed", "MyType"); + if (!klass) { + fprintf (stderr, "Can't find MyType in assembly %s\n", mono_image_get_filename (image)); + exit (1); + } + + obj = mono_object_new (domain, klass); + /* mono_object_new () only allocates the storage: + * it doesn't run any constructor. Tell the runtime to run + * the default argumentless constructor. + */ + mono_runtime_object_init (obj); + + access_valuetype_field (obj); + access_reference_field (obj); + + call_methods (obj); + more_methods (domain); +} + +static void main_thread_handler (gpointer user_data) +{ + MainThreadArgs *main_args=(MainThreadArgs *)user_data; + MonoAssembly *assembly; + + /* Loading an assembly makes the runtime setup everything + * needed to execute it. If we're just interested in the metadata + * we'd use mono_image_load (), instead and we'd get a MonoImage*. + */ + assembly = mono_domain_assembly_open (main_args->domain, + main_args->file); + if (!assembly) + exit (2); + /* + * mono_jit_exec() will run the Main() method in the assembly. + * The return value needs to be looked up from + * System.Environment.ExitCode. + */ + mono_jit_exec (main_args->domain, assembly, main_args->argc, + main_args->argv); + + create_object (main_args->domain, mono_assembly_get_image (assembly)); +} + +int +main (int argc, char* argv[]) { + MonoDomain *domain; + const char *file; + int retval; + MainThreadArgs main_args; + + if (argc < 2){ + fprintf (stderr, "Please provide an assembly to load\n"); + return 1; + } + file = argv [1]; + /* + * mono_jit_init() creates a domain: each assembly is + * loaded and run in a MonoDomain. + */ + domain = mono_jit_init (file); + + main_args.domain=domain; + main_args.file=file; + main_args.argc=argc-1; + main_args.argv=argv+1; + + mono_runtime_exec_managed_code (domain, main_thread_handler, + &main_args); + + retval=mono_environment_exitcode_get (); + + mono_jit_cleanup (domain); + return retval; +} + -- 2.25.1