Merge pull request #853 from echampet/onclick
[mono.git] / mono / metadata / seq-points-data.c
1 /*
2  * seq-points-data.c: Sequence Points functions
3  *
4  * Authors:
5  *   Marcos Henrich (marcos.henrich@xamarin.com)
6  *
7  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
8  */
9
10 #include "seq-points-data.h"
11
12 typedef struct {
13         guint8 *data;
14         int len;
15         /* When has_debug_data is set to false only il and native deltas are saved */
16         gboolean has_debug_data;
17         /* When alloc_data is set to true data allocation/deallocation is managed by this structure */
18         gboolean alloc_data;
19 } SeqPointInfoInflated;
20
21 static int
22 encode_var_int (guint8 *buf, guint8 **out_buf, int val)
23 {
24         guint8 size = 0;
25
26         do {
27                 guint8 byte = val & 0x7f;
28                 g_assert (size < 4 && "value has more than 28 bits");
29                 val >>= 7;
30                 if(val) byte |= 0x80;
31                 *(buf++) = byte;
32                 size++;
33         } while (val);
34
35         if (out_buf)
36                 *out_buf = buf;
37
38         return size;
39 }
40
41 static int
42 decode_var_int (guint8* buf, guint8 **out_buf)
43 {
44         guint8* p = buf;
45
46         int low;
47         int b;
48         b = *(p++); low   = (b & 0x7f)      ; if(!(b & 0x80)) goto done;
49         b = *(p++); low  |= (b & 0x7f) <<  7; if(!(b & 0x80)) goto done;
50         b = *(p++); low  |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
51         b = *(p++); low  |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
52
53         g_assert (FALSE && "value has more than 28 bits");
54
55 done:
56
57         if (out_buf)
58                 *out_buf = p;
59
60         return low;
61 }
62
63 static guint32
64 encode_zig_zag (int val)
65 {
66         return (val << 1) ^ (val >> 31);
67 }
68
69 static int
70 decode_zig_zag (guint32 val)
71 {
72         int n = val;
73         return (n >> 1) ^ (-(n & 1));
74 }
75
76 static SeqPointInfoInflated
77 seq_point_info_inflate (MonoSeqPointInfo *info)
78 {
79         SeqPointInfoInflated info_inflated;
80         guint8 *ptr = (guint8*) info;
81         int value;
82
83         value = decode_var_int (ptr, &ptr);
84
85         info_inflated.len = value >> 2;
86         info_inflated.has_debug_data = (value & 1) != 0;
87         info_inflated.alloc_data = (value & 2) != 0;
88
89         if (info_inflated.alloc_data)
90                 info_inflated.data = ptr;
91         else
92                 memcpy (&info_inflated.data, ptr, sizeof (guint8*));
93
94         return info_inflated;
95 }
96
97 MonoSeqPointInfo*
98 mono_seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data, int *out_size)
99 {
100         MonoSeqPointInfo *info;
101         guint8 *info_ptr;
102         guint8 buffer[4];
103         int buffer_len;
104         int value;
105         int data_size;
106
107         value = len << 2;
108         if (has_debug_data)
109                 value |= 1;
110         if (alloc_data)
111                 value |= 2;
112
113         buffer_len = encode_var_int (buffer, NULL, value);
114
115         *out_size = data_size = buffer_len + (alloc_data? len : sizeof (guint8*));
116         info_ptr = g_new0 (guint8, data_size);
117         info = (MonoSeqPointInfo*) info_ptr;
118
119         memcpy (info_ptr, buffer, buffer_len);
120         info_ptr += buffer_len;
121
122         if (alloc_data)
123                 memcpy (info_ptr, data, len);
124         else
125                 memcpy (info_ptr, &data, sizeof (guint8*));
126
127         return info;
128 }
129
130 void
131 mono_seq_point_info_free (gpointer ptr)
132 {
133         MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
134         g_free (info);
135 }
136
137 static int
138 seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
139 {
140         int value, i;
141         guint8* ptr0 = ptr;
142
143         value = decode_var_int (ptr, &ptr);
144         seq_point->il_offset += decode_zig_zag (value);
145
146         value = decode_var_int (ptr, &ptr);
147         seq_point->native_offset += decode_zig_zag (value);
148
149         if (has_debug_data) {
150                 value = decode_var_int (ptr, &ptr);
151                 seq_point->flags = value;
152
153                 if (seq_point->flags & MONO_SEQ_POINT_FLAG_EXIT_IL)
154                         seq_point->il_offset = METHOD_EXIT_IL_OFFSET;
155
156                 value = decode_var_int (ptr, &ptr);
157                 seq_point->next_len = value;
158
159                 if (seq_point->next_len) {
160                         // store next offset and skip it
161                         seq_point->next_offset = ptr - buffer_ptr;
162                         for (i = 0; i < seq_point->next_len; ++i)
163                                 decode_var_int (ptr, &ptr);
164                 }
165         }
166
167         return ptr - ptr0;
168 }
169
170 gboolean
171 mono_seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data)
172 {
173         int il_delta, native_delta;
174         GSList *l;
175         guint8 buffer[4];
176         guint8 len;
177         int flags;
178
179         if (!has_debug_data &&
180                 (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
181                 return FALSE;
182
183         il_delta = sp->il_offset - last_seq_point->il_offset;
184         native_delta = sp->native_offset - last_seq_point->native_offset;
185
186         flags = sp->flags;
187
188         if (has_debug_data && sp->il_offset == METHOD_EXIT_IL_OFFSET) {
189                 il_delta = 0;
190                 flags |= MONO_SEQ_POINT_FLAG_EXIT_IL;
191         }
192
193         len = encode_var_int (buffer, NULL, encode_zig_zag (il_delta));
194         g_byte_array_append (array, buffer, len);
195
196         len = encode_var_int (buffer, NULL, encode_zig_zag (native_delta));
197         g_byte_array_append (array, buffer, len);
198
199         if (has_debug_data) {
200                 sp->next_offset = array->len;
201                 sp->next_len = g_slist_length (next);
202
203                 len = encode_var_int (buffer, NULL, flags);
204                 g_byte_array_append (array, buffer, len);
205
206                 len = encode_var_int (buffer, NULL, sp->next_len);
207                 g_byte_array_append (array, buffer, len);
208
209                 for (l = next; l; l = l->next) {
210                         int next_index = GPOINTER_TO_UINT (l->data);
211                         guint8 buffer[4];
212                         int len = encode_var_int (buffer, NULL, next_index);
213                         g_byte_array_append (array, buffer, len);
214                 }
215         }
216
217         return TRUE;
218 }
219
220 gboolean
221 mono_seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
222 {
223         SeqPointIterator it;
224         mono_seq_point_iterator_init (&it, info);
225         while (mono_seq_point_iterator_next (&it)) {
226                 if (it.seq_point.native_offset >= native_offset) {
227                         memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
228                         return TRUE;
229                 }
230         }
231
232         return FALSE;
233 }
234
235 gboolean
236 mono_seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
237 {
238         SeqPoint prev_seq_point;
239         gboolean  is_first = TRUE;
240         SeqPointIterator it;
241         mono_seq_point_iterator_init (&it, info);
242         while (mono_seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
243                 memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
244                 is_first = FALSE;
245         }
246
247         if (!is_first && prev_seq_point.native_offset <= native_offset) {
248                 memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
249                 return TRUE;
250         }
251
252         return FALSE;
253 }
254
255 gboolean
256 mono_seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
257 {
258         SeqPointIterator it;
259         mono_seq_point_iterator_init (&it, info);
260         while (mono_seq_point_iterator_next (&it)) {
261                 if (it.seq_point.il_offset == il_offset) {
262                         memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
263                         return TRUE;
264                 }
265         }
266
267         return FALSE;
268 }
269
270 void
271 mono_seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
272 {
273         int i;
274         guint8* ptr;
275         SeqPointIterator it;
276         GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
277         SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
278
279         g_assert (info_inflated.has_debug_data);
280
281         mono_seq_point_iterator_init (&it, info);
282         while (mono_seq_point_iterator_next (&it))
283                 g_array_append_vals (seq_points, &it.seq_point, 1);
284
285         ptr = info_inflated.data + sp.next_offset;
286         for (i = 0; i < sp.next_len; i++) {
287                 int next_index;
288                 next_index = decode_var_int (ptr, &ptr);
289                 g_assert (next_index < seq_points->len);
290                 memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
291         }
292
293         g_array_free (seq_points, TRUE);
294 }
295
296 gboolean
297 mono_seq_point_iterator_next (SeqPointIterator* it)
298 {
299         if (it->ptr >= it->end)
300                 return FALSE;
301
302         it->ptr += seq_point_read (&it->seq_point, it->ptr, it->begin, it->has_debug_data);
303
304         return TRUE;
305 }
306
307 void
308 mono_seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
309 {
310         SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
311         it->ptr = info_inflated.data;
312         it->begin = info_inflated.data;
313         it->end = it->begin + info_inflated.len;
314         it->has_debug_data = info_inflated.has_debug_data;
315         memset(&it->seq_point, 0, sizeof(SeqPoint));
316 }
317
318 int
319 mono_seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
320 {
321         guint8* buffer0 = buffer;
322         SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
323
324         encode_var_int (buffer, &buffer, info_inflated.has_debug_data);
325
326         //Write sequence points
327         encode_var_int (buffer, &buffer, info_inflated.len);
328         memcpy (buffer, info_inflated.data, info_inflated.len);
329         buffer += info_inflated.len;
330
331         return buffer - buffer0;
332 }
333
334 int
335 mono_seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
336 {
337         guint8* buffer0 = buffer;
338         int size, info_size;
339         gboolean has_debug_data;
340
341         has_debug_data = decode_var_int (buffer, &buffer);
342
343         size = decode_var_int (buffer, &buffer);
344         (*info) = mono_seq_point_info_new (size, copy, buffer, has_debug_data, &info_size);
345         buffer += size;
346
347         return buffer - buffer0;
348 }
349
350 /*
351  * Returns the maximum size of mono_seq_point_info_write.
352  */
353 int
354 mono_seq_point_info_get_write_size (MonoSeqPointInfo* info)
355 {
356         SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
357
358         //4 is the maximum size required to store the size of the data.
359         //1 is the byte used to store has_debug_data.
360         int size = 4 + 1 + info_inflated.len;
361
362         return size;
363 }
364
365 /*
366  * SeqPointData struct and functions
367  * This is used to store/load/use sequence point from a file
368  */
369
370 void
371 mono_seq_point_data_init (SeqPointData *data, int entry_capacity)
372 {
373         data->entry_count = 0;
374         data->entry_capacity = entry_capacity;
375         data->entries = g_malloc (sizeof (SeqPointDataEntry) * entry_capacity);
376 }
377
378 void
379 mono_seq_point_data_free (SeqPointData *data)
380 {
381         int i;
382         for (i=0; i<data->entry_count; i++) {
383                 if (data->entries [i].free_seq_points)
384                         g_free (data->entries [i].seq_points);
385         }
386         g_free (data->entries);
387 }
388
389 gboolean
390 mono_seq_point_data_read (SeqPointData *data, char *path)
391 {
392         guint8 *buffer, *buffer_orig;
393         int entry_count, i;
394         long fsize;
395         FILE *f;
396
397         f = fopen (path, "r");
398         if (!f)
399                 return FALSE;
400
401         fseek(f, 0, SEEK_END);
402         fsize = ftell(f);
403         fseek(f, 0, SEEK_SET);
404
405         buffer_orig = buffer = g_malloc(fsize + 1);
406         fread(buffer_orig, fsize, 1, f);
407         fclose(f);
408
409         entry_count = decode_var_int (buffer, &buffer);
410         mono_seq_point_data_init (data, entry_count);
411         data->entry_count = entry_count;
412
413         for (i=0; i<entry_count; i++) {
414                 data->entries [i].method_token = decode_var_int (buffer, &buffer);
415                 data->entries [i].method_index = decode_var_int (buffer, &buffer);
416                 buffer += mono_seq_point_info_read (&data->entries [i].seq_points, buffer, TRUE);
417                 data->entries [i].free_seq_points = TRUE;
418         }
419
420         g_free (buffer_orig);
421         return TRUE;
422 }
423
424 gboolean
425 mono_seq_point_data_write (SeqPointData *data, char *path)
426 {
427         guint8 *buffer, *buffer_orig;
428         FILE *f;
429         int i, size = 0;
430
431         f = fopen (path, "w+");
432         if (!f)
433                 return FALSE;
434
435         for (i=0; i<data->entry_count; i++) {
436                 size += mono_seq_point_info_get_write_size (data->entries [i].seq_points);
437         }
438         // Add size of entry_count and native_base_offsets
439         size += 4 + data->entry_count * 4;
440
441         buffer_orig = buffer = g_malloc (size);
442
443         encode_var_int (buffer, &buffer, data->entry_count);
444
445         for (i=0; i<data->entry_count; i++) {
446                 encode_var_int (buffer, &buffer, data->entries [i].method_token);
447                 encode_var_int (buffer, &buffer, data->entries [i].method_index);
448                 buffer += mono_seq_point_info_write (data->entries [i].seq_points, buffer);
449         }
450
451         fwrite (buffer_orig, 1, buffer - buffer_orig, f);
452         g_free (buffer_orig);
453
454         return TRUE;
455 }
456
457 void
458 mono_seq_point_data_add (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo* info)
459 {
460         int i;
461
462         g_assert (data->entry_count < data->entry_capacity);
463         i = data->entry_count++;
464         data->entries [i].seq_points = info;
465         data->entries [i].method_token = method_token;
466         data->entries [i].method_index = method_index;
467         data->entries [i].free_seq_points = FALSE;
468 }
469
470 gboolean
471 mono_seq_point_data_get (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo** info)
472 {
473         int i;
474
475         for (i=0; i<data->entry_count; i++) {
476                 if (data->entries [i].method_token == method_token && (method_index == 0xffffff || data->entries [i].method_index == method_index)) {
477                         (*info) = data->entries [i].seq_points;
478                         return TRUE;
479                 }
480         }
481         return FALSE;
482 }
483
484 gboolean
485 mono_seq_point_data_get_il_offset (char *path, guint32 method_token, guint32 method_index, guint32 native_offset, guint32 *il_offset)
486 {
487         SeqPointData sp_data;
488         MonoSeqPointInfo *seq_points;
489         SeqPoint sp;
490
491         if (!mono_seq_point_data_read (&sp_data, path))
492                 return FALSE;
493
494         if (!mono_seq_point_data_get (&sp_data, method_token, method_index, &seq_points))
495                 return FALSE;
496
497         if (!mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, &sp))
498                 return FALSE;
499
500         *il_offset = sp.il_offset;
501
502         return TRUE;
503 }