| /* |
| * YASM assembler virtual line mapping handling (for parse stage) |
| * |
| * Copyright (C) 2002-2007 Peter Johnson |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include "util.h" |
| |
| #include "coretype.h" |
| #include "hamt.h" |
| |
| #include "errwarn.h" |
| #include "linemap.h" |
| |
| |
| typedef struct line_mapping { |
| /* monotonically increasing virtual line */ |
| unsigned long line; |
| |
| /* related info */ |
| /* "original" source filename */ |
| /*@null@*/ /*@dependent@*/ const char *filename; |
| /* "original" source base line number */ |
| unsigned long file_line; |
| /* "original" source line number increment (for following lines) */ |
| unsigned long line_inc; |
| } line_mapping; |
| |
| typedef struct line_source_info { |
| /* first bytecode on line; NULL if no bytecodes on line */ |
| /*@null@*/ /*@dependent@*/ yasm_bytecode *bc; |
| |
| /* source code line */ |
| /*@owned@*/ char *source; |
| } line_source_info; |
| |
| struct yasm_linemap { |
| /* Shared storage for filenames */ |
| /*@only@*/ /*@null@*/ HAMT *filenames; |
| |
| /* Current virtual line number. */ |
| unsigned long current; |
| |
| /* Mappings from virtual to physical line numbers */ |
| struct line_mapping *map_vector; |
| unsigned long map_size; |
| unsigned long map_allocated; |
| |
| /* Bytecode and source line information */ |
| /*@only@*/ line_source_info *source_info; |
| size_t source_info_size; |
| }; |
| |
| static void |
| filename_delete_one(/*@only@*/ void *d) |
| { |
| yasm_xfree(d); |
| } |
| |
| void |
| yasm_linemap_set(yasm_linemap *linemap, const char *filename, |
| unsigned long virtual_line, unsigned long file_line, |
| unsigned long line_inc) |
| { |
| char *copy; |
| unsigned long i; |
| int replace = 0; |
| line_mapping *mapping = NULL; |
| |
| if (virtual_line == 0) { |
| virtual_line = linemap->current; |
| } |
| |
| /* Replace all existing mappings that have line numbers >= this one. */ |
| for (i = linemap->map_size; i > 0; i--) { |
| if (linemap->map_vector[i-1].line < virtual_line) { |
| if (i < linemap->map_size) { |
| mapping = &linemap->map_vector[i]; |
| linemap->map_size = i + 1; |
| } |
| break; |
| } |
| } |
| |
| if (mapping == NULL) { |
| /* Create a new mapping in the map */ |
| if (linemap->map_size >= linemap->map_allocated) { |
| /* allocate another size bins when full for 2x space */ |
| linemap->map_vector = yasm_xrealloc(linemap->map_vector, |
| 2*linemap->map_allocated*sizeof(line_mapping)); |
| linemap->map_allocated *= 2; |
| } |
| mapping = &linemap->map_vector[linemap->map_size]; |
| linemap->map_size++; |
| } |
| |
| /* Fill it */ |
| |
| if (!filename) { |
| if (linemap->map_size >= 2) |
| mapping->filename = |
| linemap->map_vector[linemap->map_size-2].filename; |
| else |
| filename = "unknown"; |
| } |
| if (filename) { |
| /* Copy the filename (via shared storage) */ |
| copy = yasm__xstrdup(filename); |
| /*@-aliasunique@*/ |
| mapping->filename = HAMT_insert(linemap->filenames, copy, copy, |
| &replace, filename_delete_one); |
| /*@=aliasunique@*/ |
| } |
| |
| mapping->line = virtual_line; |
| mapping->file_line = file_line; |
| mapping->line_inc = line_inc; |
| } |
| |
| unsigned long |
| yasm_linemap_poke(yasm_linemap *linemap, const char *filename, |
| unsigned long file_line) |
| { |
| unsigned long line; |
| line_mapping *mapping; |
| |
| linemap->current++; |
| yasm_linemap_set(linemap, filename, 0, file_line, 0); |
| |
| mapping = &linemap->map_vector[linemap->map_size-1]; |
| |
| line = linemap->current; |
| |
| linemap->current++; |
| yasm_linemap_set(linemap, mapping->filename, 0, |
| mapping->file_line + |
| mapping->line_inc*(linemap->current-2-mapping->line), |
| mapping->line_inc); |
| |
| return line; |
| } |
| |
| yasm_linemap * |
| yasm_linemap_create(void) |
| { |
| size_t i; |
| yasm_linemap *linemap = yasm_xmalloc(sizeof(yasm_linemap)); |
| |
| linemap->filenames = HAMT_create(0, yasm_internal_error_); |
| |
| linemap->current = 1; |
| |
| /* initialize mapping vector */ |
| linemap->map_vector = yasm_xmalloc(8*sizeof(line_mapping)); |
| linemap->map_size = 0; |
| linemap->map_allocated = 8; |
| |
| /* initialize source line information array */ |
| linemap->source_info_size = 2; |
| linemap->source_info = yasm_xmalloc(linemap->source_info_size * |
| sizeof(line_source_info)); |
| for (i=0; i<linemap->source_info_size; i++) { |
| linemap->source_info[i].bc = NULL; |
| linemap->source_info[i].source = NULL; |
| } |
| |
| return linemap; |
| } |
| |
| void |
| yasm_linemap_destroy(yasm_linemap *linemap) |
| { |
| size_t i; |
| for (i=0; i<linemap->source_info_size; i++) { |
| if (linemap->source_info[i].source) |
| yasm_xfree(linemap->source_info[i].source); |
| } |
| yasm_xfree(linemap->source_info); |
| |
| yasm_xfree(linemap->map_vector); |
| |
| if (linemap->filenames) |
| HAMT_destroy(linemap->filenames, filename_delete_one); |
| |
| yasm_xfree(linemap); |
| } |
| |
| unsigned long |
| yasm_linemap_get_current(yasm_linemap *linemap) |
| { |
| return linemap->current; |
| } |
| |
| void |
| yasm_linemap_add_source(yasm_linemap *linemap, yasm_bytecode *bc, |
| const char *source) |
| { |
| size_t i; |
| |
| while (linemap->current > linemap->source_info_size) { |
| /* allocate another size bins when full for 2x space */ |
| linemap->source_info = yasm_xrealloc(linemap->source_info, |
| 2*linemap->source_info_size*sizeof(line_source_info)); |
| for (i=linemap->source_info_size; i<linemap->source_info_size*2; i++) { |
| linemap->source_info[i].bc = NULL; |
| linemap->source_info[i].source = NULL; |
| } |
| linemap->source_info_size *= 2; |
| } |
| |
| /* Delete existing info for that line (if any) */ |
| if (linemap->source_info[linemap->current-1].source) |
| yasm_xfree(linemap->source_info[linemap->current-1].source); |
| |
| linemap->source_info[linemap->current-1].bc = bc; |
| linemap->source_info[linemap->current-1].source = yasm__xstrdup(source); |
| } |
| |
| unsigned long |
| yasm_linemap_goto_next(yasm_linemap *linemap) |
| { |
| return ++(linemap->current); |
| } |
| |
| void |
| yasm_linemap_lookup(yasm_linemap *linemap, unsigned long line, |
| const char **filename, unsigned long *file_line) |
| { |
| line_mapping *mapping; |
| unsigned long vindex, step; |
| |
| assert(line <= linemap->current); |
| |
| /* Binary search through map to find highest line_index <= index */ |
| vindex = 0; |
| /* start step as the greatest power of 2 <= size */ |
| step = 1; |
| while (step*2<=linemap->map_size) |
| step*=2; |
| while (step>0) { |
| if (vindex+step < linemap->map_size |
| && linemap->map_vector[vindex+step].line <= line) |
| vindex += step; |
| step /= 2; |
| } |
| mapping = &linemap->map_vector[vindex]; |
| |
| *filename = mapping->filename; |
| *file_line = (line ? mapping->file_line + mapping->line_inc*(line-mapping->line) : 0); |
| } |
| |
| int |
| yasm_linemap_traverse_filenames(yasm_linemap *linemap, /*@null@*/ void *d, |
| int (*func) (const char *filename, void *d)) |
| { |
| return HAMT_traverse(linemap->filenames, d, (int (*) (void *, void *))func); |
| } |
| |
| int |
| yasm_linemap_get_source(yasm_linemap *linemap, unsigned long line, |
| yasm_bytecode **bcp, const char **sourcep) |
| { |
| if (line > linemap->source_info_size) { |
| *bcp = NULL; |
| *sourcep = NULL; |
| return 1; |
| } |
| |
| *bcp = linemap->source_info[line-1].bc; |
| *sourcep = linemap->source_info[line-1].source; |
| |
| return (!(*sourcep)); |
| } |