Cleanup examples (removed extra thread execution).
[mono.git] / samples / embed / test-invoke.c
1 #include <mono/jit/jit.h>
2 #include <mono/metadata/environment.h>
3 #include <mono/metadata/assembly.h>
4 #include <mono/metadata/debug-helpers.h>
5 #include <string.h>
6 #include <stdlib.h>
7
8 /*
9  * Simple mono embedding example.
10  * We show how to create objects and invoke methods and set fields in them.
11  * Compile with: 
12  *      gcc -Wall -o test-invoke test-invoke.c `pkg-config --cflags --libs mono` -lm
13  *      mcs invoke.cs
14  * Run with:
15  *      ./test-invoke invoke.exe
16  */
17
18 static void
19 access_valuetype_field (MonoObject *obj)
20 {
21         MonoClass *klass;
22         MonoClassField *field;
23         int val;
24
25         klass = mono_object_get_class (obj);
26
27         /* Now we'll change the value of the 'val' field (see invoke.cs) */
28         field = mono_class_get_field_from_name (klass, "val");
29
30         /* This time we also add a bit of error checking... */
31         if (!field) {
32                 fprintf (stderr, "Can't find field val in MyType\n");
33                 exit (1);
34         }
35         /* Check that val is an int (if you're paranoid or if you need to 
36          * show how this API is used) 
37          */
38         if (mono_type_get_type (mono_field_get_type (field)) != MONO_TYPE_I4) {
39                 fprintf (stderr, "Field val is not a 32 bit integer\n");
40                 exit (1);
41         }
42         
43         /* Note we pass a pointer to the value */
44         mono_field_get_value (obj, field, &val);
45         printf ("Value of field is: %d\n", val);
46         val = 10;
47
48         /* Note we pass a pointer to the value here as well */
49         mono_field_set_value (obj, field, &val);
50
51 }
52
53 static void
54 access_reference_field (MonoObject *obj)
55 {
56         MonoClass *klass;
57         MonoDomain *domain;
58         MonoClassField *str;
59         MonoString *strval;
60         char *p;
61
62         klass = mono_object_get_class (obj);
63         domain = mono_object_get_domain (obj);
64
65         /* Now we'll see that a reference type is handled slightly differently.
66          * First, get the MonoClassField representing it.
67          */
68         str = mono_class_get_field_from_name (klass, "str");
69         
70         /* No change here, we always pass a pointer */
71         mono_field_get_value (obj, str, &strval);
72         
73         /* get the string in UTF-8 encoding to print it */
74         p = mono_string_to_utf8 (strval);
75         printf ("Value of str is: %s\n", p);
76         /* we need to free the result from mono_string_to_utf8 () */
77         g_free (p);
78
79         /* string are immutable, so we need to create a different string */
80         strval = mono_string_new (domain, "hello from the embedding API");
81
82         /* Here is the slight difference: for reference types we pass 
83          * the pointer directly, instead of a pointer to the value.
84          */
85         mono_field_set_value (obj, str, strval);
86
87 }
88
89 /* Demostrate how to call methods */
90 static void
91 call_methods (MonoObject *obj)
92 {
93         MonoClass *klass;
94         MonoDomain *domain;
95         MonoMethod *method = NULL, *m = NULL, *ctor = NULL, *fail = NULL, *mvalues;
96         MonoProperty *prop;
97         MonoObject *result, *exception;
98         MonoString *str;
99         char *p;
100         gpointer iter;
101         gpointer args [2];
102         int val;
103
104         klass = mono_object_get_class (obj);
105         domain = mono_object_get_domain (obj);
106
107         /* retrieve all the methods we need */
108         iter = NULL;
109         while ((m = mono_class_get_methods (klass, &iter))) {
110                 if (strcmp (mono_method_get_name (m), "method") == 0) {
111                         method = m;
112                 } else if (strcmp (mono_method_get_name (m), "Fail") == 0) {
113                         fail = m;
114                 } else if (strcmp (mono_method_get_name (m), "Values") == 0) {
115                         mvalues = m;
116                 } else if (strcmp (mono_method_get_name (m), ".ctor") == 0) {
117                         /* Check it's the ctor that takes two args:
118                          * as you see a contrsuctor is a method like any other.
119                          */
120                         MonoMethodSignature * sig = mono_method_signature (m);
121                         if (mono_signature_get_param_count (sig) == 2) {
122                                 ctor = m;
123                         }
124                 }
125         }
126         /* Now we'll call method () on obj: since it takes no arguments 
127          * we can pass NULL as the third argument to mono_runtime_invoke ().
128          * The method will print the updated value.
129          */
130         mono_runtime_invoke (method, obj, NULL, NULL);
131
132         /* mono_object_new () doesn't call any constructor: this means that
133          * we'll have to invoke the constructor if needed ourselves. Note:
134          * invoking a constructor is no different than calling any other method,
135          * so we'll still call mono_runtime_invoke (). This also means that we 
136          * can invoke a constructor at any time, like now.
137          * First, setup the array of arguments and their values.
138          */
139
140         /* As usual, we use the address of the data for valuetype arguments */
141         val = 7;
142         args [0] = &val;
143         /* and the pointer for reference types: mono_array_new () returns a MonoArray* */
144         args [1] = mono_array_new (domain, mono_get_byte_class (), 256);
145         mono_runtime_invoke (ctor, obj, args, NULL);
146
147         /* A property exists only as a metadata entity, so getting or setting the value
148          * is nothing more than calling mono_runtime_invoke () on the getter or setter method.
149          */
150         prop = mono_class_get_property_from_name (klass, "Value");
151         method = mono_property_get_get_method (prop);
152         result = mono_runtime_invoke (method, obj, NULL, NULL);
153         /* mono_runtime_invoke () always boxes the return value if it's a valuetype */
154         val = *(int*)mono_object_unbox (result);
155         
156         printf ("Value of val from property is: %d\n", val);
157         
158         /* we also have an helper method: note that reference types are returned as is */
159         prop = mono_class_get_property_from_name (klass, "Message");
160         str = (MonoString*)mono_property_get_value (prop, obj, NULL, NULL);
161         /* get the string in UTF-8 encoding to print it */
162         p = mono_string_to_utf8 (str);
163         printf ("Value of str from property is: %s\n", p);
164         /* we need to free the result from mono_string_to_utf8 () */
165         g_free (p);
166
167         /* Now we'll show two things:
168          * 1) static methods are invoked with mono_runtime_invoke () as well,
169          * we just pass NULL as the second argument.
170          * 2) we can catch exceptions thrown by the called method.
171          * Note: fail is declared as static void Fail () in invoke.cs.
172          * We first set result to NULL: if after the invocation it will have
173          * a different value, it will be the exception that was thrown from 
174          * the Fail () method. Note that if an exception was thrown, the return 
175          * value (if any) is undefined and can't be used in any way (yes, the above 
176          * invocations don't have this type of error checking to make things simpler).
177          */
178         exception = NULL;
179         mono_runtime_invoke (fail, NULL, NULL, &exception);
180         if (exception) {
181                 printf ("An exception was thrown in Fail ()\n");
182         }
183
184         /* Now let's see how to handle methods that take by ref arguments:
185          * Valuetypes continue to be passed as pointers to the data.
186          * Reference arguments passed by ref (ref or out is the same)
187          * are handled the same way: a pointer to the pointer is used
188          * (so that the result can be read back).
189          * Small note: in this case (a System.Int32 valuetype) we can just
190          * use &val where val is a C 32 bit integer. In the general case 
191          * unmanaged code doesn't know the size of a valuetype, since the 
192          * runtime may decide to lay it out in what it thinks is a better way 
193          * (unless ExplicitLayout is set). To avoid issues, the best thing is to
194          * create an object of the valuetype's class and retrieve the pointer 
195          * to the data with the mono_object_unbox () function.
196          */
197         val = 100;
198         str = mono_string_new (domain, "another string");
199         args [0] = &val;
200         args [1] = &str;
201         mono_runtime_invoke (mvalues, obj, args, NULL);
202         /* get the string in UTF-8 encoding to print it */
203         p = mono_string_to_utf8 (str);
204         printf ("Values of str/val from Values () are: %s/%d\n", p, val);
205         /* we need to free the result from mono_string_to_utf8 () */
206         g_free (p);
207 }
208
209 static void
210 more_methods (MonoDomain *domain)
211 {
212         MonoClass *klass;
213         MonoMethodDesc* mdesc;
214         MonoMethod *method, *vtmethod;
215         MonoString *str;
216         MonoObject *obj;
217         char *p;
218         int val;
219
220         /* Now let's call an instance method on a valuetype. There are two
221          * different case:
222          * 1) calling a virtual method defined in a base class, like ToString (): 
223          * we need to pass the value boxed in an object
224          * 2) calling a normal instance method: in this case
225          * we pass the address to the valuetype as the second argument 
226          * instead of an object.
227          * First some initialization.
228          */
229         val = 25;
230         klass = mono_get_int32_class ();
231         obj = mono_value_box (domain, klass, &val);
232
233         /* A different way to search for a method */
234         mdesc = mono_method_desc_new (":ToString()", FALSE);
235         vtmethod = mono_method_desc_search_in_class (mdesc, klass);
236
237         str = (MonoString*)mono_runtime_invoke (vtmethod, &val, NULL, NULL);
238         /* get the string in UTF-8 encoding to print it */
239         p = mono_string_to_utf8 (str);
240         printf ("25.ToString (): %s\n", p);
241         /* we need to free the result from mono_string_to_utf8 () */
242         g_free (p);
243
244         /* Now: see how the result is different if we search for the ToString ()
245          * method in System.Object: mono_runtime_invoke () doesn't do any sort of
246          * virtual method invocation: it calls the exact method that it was given 
247          * to execute. If a virtual call is needed, mono_object_get_virtual_method ()
248          * can be called.
249          */
250         method = mono_method_desc_search_in_class (mdesc, mono_get_object_class ());
251         str = (MonoString*)mono_runtime_invoke (method, obj, NULL, NULL);
252         /* get the string in UTF-8 encoding to print it */
253         p = mono_string_to_utf8 (str);
254         printf ("25.ToString (), from System.Object: %s\n", p);
255         /* we need to free the result from mono_string_to_utf8 () */
256         g_free (p);
257
258         /* Now get the method that overrides ToString () in obj */
259         vtmethod = mono_object_get_virtual_method (obj, method);
260         if (mono_class_is_valuetype (mono_method_get_class (vtmethod))) {
261                 printf ("Need to unbox this for call to virtual ToString () for %s\n", mono_class_get_name (klass));
262         }
263
264         mono_method_desc_free (mdesc);
265 }
266
267 static void
268 create_object (MonoDomain *domain, MonoImage *image)
269 {
270         MonoClass *klass;
271         MonoObject *obj;
272
273         klass = mono_class_from_name (image, "Embed", "MyType");
274         if (!klass) {
275                 fprintf (stderr, "Can't find MyType in assembly %s\n", mono_image_get_filename (image));
276                 exit (1);
277         }
278
279         obj = mono_object_new (domain, klass);
280         /* mono_object_new () only allocates the storage: 
281          * it doesn't run any constructor. Tell the runtime to run
282          * the default argumentless constructor.
283          */
284         mono_runtime_object_init (obj);
285
286         access_valuetype_field (obj);
287         access_reference_field (obj);
288
289         call_methods (obj);
290         more_methods (domain);
291 }
292
293 static void main_function (MonoDomain *domain, const char *file, int argc, char **argv)
294 {
295         MonoAssembly *assembly;
296
297         /* Loading an assembly makes the runtime setup everything
298          * needed to execute it. If we're just interested in the metadata
299          * we'd use mono_image_load (), instead and we'd get a MonoImage*.
300          */
301         assembly = mono_domain_assembly_open (domain, file);
302         if (!assembly)
303                 exit (2);
304         /*
305          * mono_jit_exec() will run the Main() method in the assembly.
306          * The return value needs to be looked up from
307          * System.Environment.ExitCode.
308          */
309         mono_jit_exec (domain, assembly, argc, argv);
310
311         create_object (domain, mono_assembly_get_image (assembly));
312 }
313
314 int 
315 main (int argc, char* argv[]) {
316         MonoDomain *domain;
317         const char *file;
318         int retval;
319         
320         if (argc < 2){
321                 fprintf (stderr, "Please provide an assembly to load\n");
322                 return 1;
323         }
324         file = argv [1];
325         /*
326          * mono_jit_init() creates a domain: each assembly is
327          * loaded and run in a MonoDomain.
328          */
329         domain = mono_jit_init (file);
330
331         main_function (domain, file, argc - 1, argv + 1);
332
333         retval = mono_environment_exitcode_get ();
334         
335         mono_jit_cleanup (domain);
336         return retval;
337 }
338