File: test-invoke.c

package info (click to toggle)
mono 5.18.0.240+dfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 1,253,216 kB
  • sloc: cs: 10,925,936; xml: 2,804,987; ansic: 643,970; cpp: 120,384; perl: 59,272; asm: 21,383; sh: 20,162; makefile: 18,157; python: 4,715; pascal: 924; sql: 859; sed: 16; php: 1
file content (343 lines) | stat: -rw-r--r-- 11,243 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#include <mono/jit/jit.h>
#include <mono/metadata/object.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <string.h>
#include <stdlib.h>

#ifndef FALSE
#define FALSE 0
#endif

/*
 * 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-2` -lm
 * 	mcs invoke.cs
 * Run with:
 * 	./test-invoke invoke.exe
 */

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 () */
	mono_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;
	void* iter;
	void* 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 () */
	mono_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 () */
	mono_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 () */
	mono_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 () */
	mono_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_function (MonoDomain *domain, const char *file, int argc, char **argv)
{
	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 (domain, 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 (domain, assembly, argc, argv);

	create_object (domain, mono_assembly_get_image (assembly));
}

int 
main (int argc, char* argv[]) {
	MonoDomain *domain;
	const char *file;
	int retval;
	
	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_function (domain, file, argc - 1, argv + 1);

	retval = mono_environment_exitcode_get ();
	
	mono_jit_cleanup (domain);
	return retval;
}