1/*
2 * YASM assembler virtual line mapping handling (for parse stage)
3 *
4 *  Copyright (C) 2002-2007  Peter Johnson
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27#include "util.h"
28
29#include "coretype.h"
30#include "hamt.h"
31
32#include "errwarn.h"
33#include "linemap.h"
34
35
36typedef struct line_mapping {
37    /* monotonically increasing virtual line */
38    unsigned long line;
39
40    /* related info */
41    /* "original" source filename */
42    /*@null@*/ /*@dependent@*/ const char *filename;
43    /* "original" source base line number */
44    unsigned long file_line;
45    /* "original" source line number increment (for following lines) */
46    unsigned long line_inc;
47} line_mapping;
48
49typedef struct line_source_info {
50    /* first bytecode on line; NULL if no bytecodes on line */
51    /*@null@*/ /*@dependent@*/ yasm_bytecode *bc;
52
53    /* source code line */
54    /*@owned@*/ char *source;
55} line_source_info;
56
57struct yasm_linemap {
58    /* Shared storage for filenames */
59    /*@only@*/ /*@null@*/ HAMT *filenames;
60
61    /* Current virtual line number. */
62    unsigned long current;
63
64    /* Mappings from virtual to physical line numbers */
65    struct line_mapping *map_vector;
66    unsigned long map_size;
67    unsigned long map_allocated;
68
69    /* Bytecode and source line information */
70    /*@only@*/ line_source_info *source_info;
71    size_t source_info_size;
72};
73
74static void
75filename_delete_one(/*@only@*/ void *d)
76{
77    yasm_xfree(d);
78}
79
80void
81yasm_linemap_set(yasm_linemap *linemap, const char *filename,
82                 unsigned long virtual_line, unsigned long file_line,
83                 unsigned long line_inc)
84{
85    char *copy;
86    unsigned long i;
87    int replace = 0;
88    line_mapping *mapping = NULL;
89
90    if (virtual_line == 0) {
91        virtual_line = linemap->current;
92    }
93
94    /* Replace all existing mappings that have line numbers >= this one. */
95    for (i = linemap->map_size; i > 0; i--) {
96        if (linemap->map_vector[i-1].line < virtual_line) {
97            if (i < linemap->map_size) {
98                mapping = &linemap->map_vector[i];
99                linemap->map_size = i + 1;
100            }
101            break;
102        }
103    }
104
105    if (mapping == NULL) {
106        /* Create a new mapping in the map */
107        if (linemap->map_size >= linemap->map_allocated) {
108            /* allocate another size bins when full for 2x space */
109            linemap->map_vector = yasm_xrealloc(linemap->map_vector,
110                2*linemap->map_allocated*sizeof(line_mapping));
111            linemap->map_allocated *= 2;
112        }
113        mapping = &linemap->map_vector[linemap->map_size];
114        linemap->map_size++;
115    }
116
117    /* Fill it */
118
119    if (!filename) {
120        if (linemap->map_size >= 2)
121            mapping->filename =
122                linemap->map_vector[linemap->map_size-2].filename;
123        else
124            filename = "unknown";
125    }
126    if (filename) {
127        /* Copy the filename (via shared storage) */
128        copy = yasm__xstrdup(filename);
129        /*@-aliasunique@*/
130        mapping->filename = HAMT_insert(linemap->filenames, copy, copy,
131                                        &replace, filename_delete_one);
132        /*@=aliasunique@*/
133    }
134
135    mapping->line = virtual_line;
136    mapping->file_line = file_line;
137    mapping->line_inc = line_inc;
138}
139
140unsigned long
141yasm_linemap_poke(yasm_linemap *linemap, const char *filename,
142                  unsigned long file_line)
143{
144    unsigned long line;
145    line_mapping *mapping;
146
147    linemap->current++;
148    yasm_linemap_set(linemap, filename, 0, file_line, 0);
149
150    mapping = &linemap->map_vector[linemap->map_size-1];
151
152    line = linemap->current;
153
154    linemap->current++;
155    yasm_linemap_set(linemap, mapping->filename, 0,
156                     mapping->file_line +
157                     mapping->line_inc*(linemap->current-2-mapping->line),
158                     mapping->line_inc);
159
160    return line;
161}
162
163yasm_linemap *
164yasm_linemap_create(void)
165{
166    size_t i;
167    yasm_linemap *linemap = yasm_xmalloc(sizeof(yasm_linemap));
168
169    linemap->filenames = HAMT_create(0, yasm_internal_error_);
170
171    linemap->current = 1;
172
173    /* initialize mapping vector */
174    linemap->map_vector = yasm_xmalloc(8*sizeof(line_mapping));
175    linemap->map_size = 0;
176    linemap->map_allocated = 8;
177
178    /* initialize source line information array */
179    linemap->source_info_size = 2;
180    linemap->source_info = yasm_xmalloc(linemap->source_info_size *
181                                        sizeof(line_source_info));
182    for (i=0; i<linemap->source_info_size; i++) {
183        linemap->source_info[i].bc = NULL;
184        linemap->source_info[i].source = NULL;
185    }
186
187    return linemap;
188}
189
190void
191yasm_linemap_destroy(yasm_linemap *linemap)
192{
193    size_t i;
194    for (i=0; i<linemap->source_info_size; i++) {
195        if (linemap->source_info[i].source)
196            yasm_xfree(linemap->source_info[i].source);
197    }
198    yasm_xfree(linemap->source_info);
199
200    yasm_xfree(linemap->map_vector);
201
202    if (linemap->filenames)
203        HAMT_destroy(linemap->filenames, filename_delete_one);
204
205    yasm_xfree(linemap);
206}
207
208unsigned long
209yasm_linemap_get_current(yasm_linemap *linemap)
210{
211    return linemap->current;
212}
213
214void
215yasm_linemap_add_source(yasm_linemap *linemap, yasm_bytecode *bc,
216                        const char *source)
217{
218    size_t i;
219
220    while (linemap->current > linemap->source_info_size) {
221        /* allocate another size bins when full for 2x space */
222        linemap->source_info = yasm_xrealloc(linemap->source_info,
223            2*linemap->source_info_size*sizeof(line_source_info));
224        for (i=linemap->source_info_size; i<linemap->source_info_size*2; i++) {
225            linemap->source_info[i].bc = NULL;
226            linemap->source_info[i].source = NULL;
227        }
228        linemap->source_info_size *= 2;
229    }
230
231    /* Delete existing info for that line (if any) */
232    if (linemap->source_info[linemap->current-1].source)
233        yasm_xfree(linemap->source_info[linemap->current-1].source);
234
235    linemap->source_info[linemap->current-1].bc = bc;
236    linemap->source_info[linemap->current-1].source = yasm__xstrdup(source);
237}
238
239unsigned long
240yasm_linemap_goto_next(yasm_linemap *linemap)
241{
242    return ++(linemap->current);
243}
244
245void
246yasm_linemap_lookup(yasm_linemap *linemap, unsigned long line,
247                    const char **filename, unsigned long *file_line)
248{
249    line_mapping *mapping;
250    unsigned long vindex, step;
251
252    assert(line <= linemap->current);
253
254    /* Binary search through map to find highest line_index <= index */
255    vindex = 0;
256    /* start step as the greatest power of 2 <= size */
257    step = 1;
258    while (step*2<=linemap->map_size)
259        step*=2;
260    while (step>0) {
261        if (vindex+step < linemap->map_size
262                && linemap->map_vector[vindex+step].line <= line)
263            vindex += step;
264        step /= 2;
265    }
266    mapping = &linemap->map_vector[vindex];
267
268    *filename = mapping->filename;
269    *file_line = (line ? mapping->file_line + mapping->line_inc*(line-mapping->line) : 0);
270}
271
272int
273yasm_linemap_traverse_filenames(yasm_linemap *linemap, /*@null@*/ void *d,
274                                int (*func) (const char *filename, void *d))
275{
276    return HAMT_traverse(linemap->filenames, d, (int (*) (void *, void *))func);
277}
278
279int
280yasm_linemap_get_source(yasm_linemap *linemap, unsigned long line,
281                        yasm_bytecode **bcp, const char **sourcep)
282{
283    if (line > linemap->source_info_size) {
284        *bcp = NULL;
285        *sourcep = NULL;
286        return 1;
287    }
288
289    *bcp = linemap->source_info[line-1].bc;
290    *sourcep = linemap->source_info[line-1].source;
291
292    return (!(*sourcep));
293}
294