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