clib: uses a macro to manage error printing, allowing customization
[nit.git] / clib / nit_main.c
1 /* This file is part of NIT ( http://www.nitlanguage.org ).
2 *
3 * Copyright 2006-2009 Jean Privat <jean@pryen.org>
4 *
5 * This file is free software, which comes along with NIT. This software is
6 * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 * PARTICULAR PURPOSE. You can modify it is you want, provided this header
9 * is kept unaltered, and a notification of the changes is added.
10 * You are allowed to redistribute it and sell it, alone or is a part of
11 * another product.
12 */
13
14 #include "nit_common.h"
15 #include <signal.h>
16 #include <stdarg.h>
17 #include "gc.h"
18
19 #define PRINT_ERROR(...) ((void)fprintf(stderr, __VA_ARGS__))
20
21 bigint object_id_counter = 1000000;
22 enum gc_option { large, gc_opt_malloc, boehm, nitgc } gc_option;
23
24 #ifdef WITH_LIBGC
25 #define GC_DEBUG
26 #include <gc/gc.h>
27 #endif
28
29 void *raw_alloc(size_t s0)
30 {
31 switch (gc_option) {
32 case nitgc: return malloc(s0);
33 case gc_opt_malloc: return malloc(s0);
34 default: return alloc(s0);
35 }
36 }
37
38 void register_static_object(val_t *o)
39 {
40 switch (gc_option) {
41 case nitgc: GC_add_static_object(o); break;
42 default: break;
43 }
44 return;
45 }
46
47 void *large_alloc(size_t s0)
48 {
49 static char * alloc_pos = NULL;
50 static size_t alloc_size = 0;
51 void * res;
52 size_t s = ((s0+3)/4)*4;
53 if(alloc_size < s) {
54 alloc_size = s + 1024*1024;
55 alloc_pos = (char *)calloc(alloc_size, 1);
56 }
57 res = alloc_pos;
58 alloc_size -= s;
59 alloc_pos += s;
60 return res;
61 }
62
63 void * alloc(size_t s0)
64 {
65 switch (gc_option) {
66 #ifdef WITH_LIBGC
67 case boehm: return GC_MALLOC(s0);
68 #endif
69 case nitgc: return Nit_gc_malloc(s0);
70 case gc_opt_malloc: return calloc(1, s0);
71 case large:
72 default: return large_alloc(s0);
73 }
74 }
75
76 int glob_argc;
77 char **glob_argv;
78 val_t G_sys;
79
80 void exithandler(int s) {
81 PRINT_ERROR( "Recieved signal %d\n", s);
82 nit_exit(1);
83 }
84 void initialize_gc_option(void) {
85 /* GC default */
86 char *def;
87 gc_option = nitgc;
88 def = "nitgc";
89
90 /* Process GC runtime selection */
91 if (getenv("NIT_GC_OPTION") != NULL) {
92 char *opt=getenv("NIT_GC_OPTION");
93 if (strcmp(opt, "boehm")==0) {
94 #ifdef WITH_LIBGC
95 gc_option = boehm;
96 #else
97 PRINT_ERROR( "Compiled without Boehm GC support. Using default '%s'.\n", def);
98 #endif
99 } else if (strcmp(opt, "nitgc")==0) {
100 gc_option = nitgc;
101 } else if (strcmp(opt, "malloc")==0) {
102 gc_option = gc_opt_malloc;
103 } else if (strcmp(opt, "large")==0) {
104 gc_option = large;
105 } else if (strcmp(opt, "help")==0) {
106 PRINT_ERROR( "NIT_GC_OPTION accepts 'nitgc'"
107 #ifdef WITH_LIBGC
108 ", 'boehm'"
109 #endif
110 ", 'large', 'malloc'. Default is '%s'.\n", def);
111 exit(1);
112 } else {
113 PRINT_ERROR( "Invalid GC option in NIT_GC_OPTION environment variable. Using default '%s'.\n", def);
114 }
115 }
116
117 /* Initialize GC (if needed) */
118 switch(gc_option) {
119 #ifdef WITH_LIBGC
120 case boehm: GC_INIT(); break;
121 #endif
122 case nitgc: Nit_gc_init(); break;
123 default: break; /* Nothing */
124 }
125
126 /* Initialize global references list */
127 nitni_global_ref_list_init();
128 }
129 void prepare_signals(void) {
130 initialize_gc_option();
131
132 signal(SIGINT,exithandler);
133 signal(SIGABRT,exithandler);
134 signal(SIGSEGV,exithandler);
135 signal(SIGILL, exithandler);
136 signal(SIGFPE, exithandler);
137 signal(SIGTERM,exithandler);
138 signal(SIGBUS, exithandler);
139 }
140 struct stack_frame_t *stack_frame_head = NULL;
141 void nit_exit(int i) {
142 char *opt=getenv("NIT_NO_STACK");
143 if (opt == NULL || strcmp(opt, "0")==0) {
144 PRINT_ERROR( ",---- Stack trace -- - - -\n");
145 while(stack_frame_head != NULL) {
146 PRINT_ERROR( "| %s (%s:%d)\n", stack_frame_head->meth, stack_frame_head->file, stack_frame_head->line);
147 if (stack_frame_head == stack_frame_head->prev) break;
148 stack_frame_head = stack_frame_head->prev;
149 }
150 PRINT_ERROR( "`------------------- - - -\n");
151 }
152 exit(i);
153 }
154
155 void nit_abort(const char* s, const char* msg, const char* loc, int line) {
156 PRINT_ERROR( "Runtime error: ");
157 PRINT_ERROR( s, msg);
158 PRINT_ERROR( " (%s", loc);
159 if (line != 0) PRINT_ERROR( ":%d", line);
160 PRINT_ERROR( ")\n");
161 nit_exit(1);
162 }
163
164 /* Register reference to Nit object with the latest extern method called. */
165 void nitni_local_ref_add( struct nitni_ref *ref ) {
166 struct nitni_ref_array_link **link_p;
167 struct nitni_ref_array_link * link = NULL;
168
169 /* find usable link or link to create */
170 link_p = &( stack_frame_head->nitni_local_ref_head );
171 while ( (*link_p) != NULL &&
172 (*link_p)->count >= NITNI_REF_ARRAY_LINK_SIZE ) {
173 link_p = &((*link_p)->next);
174 }
175
176 /* create link if needed */
177 if ( *link_p == NULL ) {
178 link = malloc( sizeof(struct nitni_ref_array_link) );
179 link->count = 0;
180 link->next = NULL;
181
182 (*link_p) = link;
183 } else {
184 link = *link_p;
185 }
186
187 /* add to link */
188 link->reg[ link->count ] = ref;
189 link->count ++;
190 }
191
192 /* Clean all references associated to the current (but returning) extern method. */
193 void nitni_local_ref_clean( void ) {
194 struct nitni_ref_array_link * link,
195 * last_link;
196 int i;
197
198 link = stack_frame_head->nitni_local_ref_head;
199 while ( link != NULL )
200 {
201 for ( i = 0; i < link->count; i ++ ) {
202 if ( link->reg[i]->count == 0 ) { /* not registered globally */
203 free( link->reg[ i ] );
204 }
205 }
206
207 last_link = link;
208 if ( link->count == NITNI_REF_ARRAY_LINK_SIZE )
209 link = link->next;
210 else
211 link = NULL;
212
213 free(last_link);
214 }
215
216 stack_frame_head->nitni_local_ref_head = NULL;
217 }
218
219 struct nitni_global_ref_list_t *nitni_global_ref_list;
220 void nitni_global_ref_list_init() {
221 nitni_global_ref_list = (struct nitni_global_ref_list_t*)malloc(sizeof(struct nitni_global_ref_list_t));
222 nitni_global_ref_list->head = NULL;
223 nitni_global_ref_list->tail = NULL;
224 }
225
226 void nitni_global_ref_add( struct nitni_ref *ref ) {
227 if ( nitni_global_ref_list->head == NULL ) {
228 nitni_global_ref_list->head = ref;
229 nitni_global_ref_list->tail = ref;
230
231 ref->prev = NULL;
232 } else {
233 nitni_global_ref_list->tail->next = ref;
234 ref->prev = nitni_global_ref_list->tail;
235 }
236
237 ref->next = NULL;
238 }
239
240 void nitni_global_ref_remove( struct nitni_ref *ref ) {
241 if ( ref->prev == NULL ) {
242 nitni_global_ref_list->head = ref->next;
243 } else {
244 ref->prev->next = ref->next;
245 }
246
247 if ( ref->next == NULL ) {
248 nitni_global_ref_list->tail = ref->prev;
249 } else {
250 ref->next->prev = ref->prev;
251 }
252 }
253
254 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
255 if ( ref->count == 0 ) /* not registered */
256 {
257 /* add to list */
258 nitni_global_ref_add( ref );
259 }
260
261 ref->count ++;
262 }
263
264 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
265 if ( ref->count == 1 ) /* was last reference */
266 {
267 /* remove from list */
268 nitni_global_ref_remove( ref );
269 }
270
271 ref->count --;
272 }