1 /*
2  * Placed into the Public Domain.
3  * written by Walter Bright
4  * www.digitalmars.com
5  */
6 
7 /*
8  *  Modified by Sean Kelly <sean@f4.ca> for use with Tango.
9  */
10 
11 module rt.compiler.dmd.rt.dmain2;
12 
13 private
14 {
15     import rt.compiler.util.console;
16 
17     import tango.stdc.stdlib : malloc, free, exit, EXIT_FAILURE;
18     import tango.stdc.string : strlen;
19 }
20 
21 version( Win32 )
22 {
23     import tango.stdc.stdlib: alloca;
24     import tango.stdc.string: wcslen;
25     extern (Windows) void*    LocalFree(void*);
26     extern (Windows) wchar*   GetCommandLineW();
27     extern (Windows) wchar**  CommandLineToArgvW(wchar*, int*);
28     extern (Windows) export int WideCharToMultiByte(uint, uint, wchar*, int, char*, int, char*, int);
29     pragma(lib, "shell32.lib");   // needed for CommandLineToArgvW
30     //pragma(lib, "tango-win32-dmd.lib"); // links Tango's Win32 library to reduce EXE size
31 }
32 
33 extern (C) void _STI_monitor_staticctor();
34 extern (C) void _STD_monitor_staticdtor();
35 extern (C) void _STI_critical_init();
36 extern (C) void _STD_critical_term();
37 extern (C) void gc_init();
38 extern (C) void gc_term();
39 extern (C) void _minit();
40 extern (C) void _moduleCtor();
41 extern (C) void _moduleDtor();
42 extern (C) void thread_joinAll();
43 
44 /***********************************
45  * These functions must be defined for any D program linked
46  * against this library.
47  */
48 extern (C) void onAssertError( char[] file, size_t line );
49 extern (C) void onAssertErrorMsg( char[] file, size_t line, char[] msg );
50 extern (C) void onArrayBoundsError( char[] file, size_t line );
51 extern (C) void onSwitchError( char[] file, size_t line );
52 extern (C) bool runModuleUnitTests();
53 
54 // this function is called from the utf module
55 //extern (C) void onUnicodeError( char[] msg, size_t idx );
56 
57 /***********************************
58  * These are internal callbacks for various language errors.
59  */
60 extern (C) void _d_assert( char[] file, uint line )
61 {
62     onAssertError( file, line );
63 }
64 
65 extern (C) static void _d_assert_msg( char[] msg, char[] file, uint line )
66 {
67     onAssertErrorMsg( file, line, msg );
68 }
69 
70 extern (C) void _d_array_bounds( char[] file, uint line )
71 {
72     onArrayBoundsError( file, line );
73 }
74 
75 extern (C) void _d_switch_error( char[] file, uint line )
76 {
77     onSwitchError( file, line );
78 }
79 
80 bool _d_isHalting = false;
81 
82 extern (C) bool rt_isHalting()
83 {
84     return _d_isHalting;
85 }
86 
87 extern (C) bool rt_trapExceptions = true;
88 
89 void _d_criticalInit()
90 {
91     static bool hasBeenCalled;
92 
93     if (hasBeenCalled)
94         return;
95 
96     hasBeenCalled = true;
97 
98     version (Posix)
99     {
100         _STI_monitor_staticctor();
101         _STI_critical_init();
102     }
103 }
104 
105 alias void delegate( Exception ) ExceptionHandler;
106 
107 extern (C) bool rt_init( ExceptionHandler dg = null )
108 {
109     static bool result;
110 
111     if (result)
112         return result;
113 
114     _d_criticalInit();
115 
116     try
117     {
118         gc_init();
119         version (Win32)
120             _minit();
121         _moduleCtor();
122         return result = true;
123     }
124     catch( Exception e )
125     {
126         if( dg ){
127             dg( e );
128         } else {
129             console("exception while executing module initializers:\n");
130             e.writeOut(delegate void(char[]s){
131                 console(s);
132             });
133             console();
134         }
135     }
136     catch
137     {
138         console("error while executing module initializers\n");
139     }
140     _d_criticalTerm();
141     return result = false;
142 }
143 
144 void _d_criticalTerm()
145 {
146     static bool hasBeenCalled;
147 
148     if (hasBeenCalled)
149         return;
150 
151     hasBeenCalled = true;
152 
153     version (Posix)
154     {
155         _STD_critical_term();
156         _STD_monitor_staticdtor();
157     }
158 }
159 
160 extern (C) bool rt_term( ExceptionHandler dg = null )
161 {
162     static bool result;
163 
164     if (result)
165         return result;
166 
167     try
168     {
169         _d_isHalting = true;
170         thread_joinAll();
171         _moduleDtor();
172         gc_term();
173         return result = true;
174     }
175     catch( Exception e )
176     {
177         if( dg )
178             dg( e );
179     }
180     catch
181     {
182 
183     }
184     finally
185     {
186         _d_criticalTerm();
187     }
188     return result = false;
189 }
190 
191 version (OSX)
192 {
193     // The bottom of the stack
194     extern (C) void* __osx_stack_end = cast(void*)0xC0000000;
195 }
196 
197 version (FreeBSD)
198 {
199     // The bottom of the stack
200     extern (C) void* __libc_stack_end;
201 }
202 
203 version (Solaris)
204 {
205     // The bottom of the stack
206     extern (C) void* __libc_stack_end;
207 }
208 
209 /***********************************
210  * The D main() function supplied by the user's program
211  */
212 int main(char[][] args);
213 
214 version(NoCMain)
215 {
216 
217 }
218 else
219 {
220 
221 /***********************************
222  * Substitutes for the C main() function.
223  * It's purpose is to wrap the call to the D main()
224  * function and catch any unhandled exceptions.
225  */
226 
227 extern (C) int main(int argc, char **argv)
228 {
229     char[][] args;
230     int result;
231 
232     version (OSX)
233     {/* OSX does not provide a way to get at the top of the
234       * stack, except for the magic value 0xC0000000.
235       * But as far as the gc is concerned, argv is at the top
236       * of the main thread's stack, so save the address of that.
237       */
238     __osx_stack_end = cast(void*)&argv;
239     }
240     version (FreeBSD)
241     {	/* FreeBSD does not provide a way to get at the top of the
242 	 * stack.
243 	 * But as far as the gc is concerned, argv is at the top
244 	 * of the main thread's stack, so save the address of that.
245 	 */
246 	__libc_stack_end = cast(void*)&argv;
247     }
248 
249     version (Solaris)
250     {	/* As far as the gc is concerned, argv is at the top
251 	 * of the main thread's stack, so save the address of that.
252 	 */
253 	__libc_stack_end = cast(void*)&argv;
254     }
255 
256     version (Posix)
257         _d_criticalInit();
258 
259     version (Win32)
260     {
261         wchar*    wcbuf = GetCommandLineW();
262         size_t    wclen = wcslen(wcbuf);
263         int       wargc = 0;
264         wchar**   wargs = CommandLineToArgvW(wcbuf, &wargc);
265         assert(wargc == argc);
266 
267         char*     cargp = null;
268         size_t    cargl = WideCharToMultiByte(65001, 0, wcbuf, wclen, null, 0, null, 0);
269 
270         cargp = cast(char*) alloca(cargl);
271         args  = ((cast(char[]*) alloca(wargc * (char[]).sizeof)))[0 .. wargc];
272 
273         for (size_t i = 0, p = 0; i < wargc; i++)
274         {
275             int wlen = wcslen( wargs[i] );
276             int clen = WideCharToMultiByte(65001, 0, &wargs[i][0], wlen, null, 0, null, 0);
277             args[i]  = cargp[p .. p+clen];
278             p += clen; assert(p <= cargl);
279             WideCharToMultiByte(65001, 0, &wargs[i][0], wlen, &args[i][0], clen, null, 0);
280         }
281         LocalFree(wargs);
282         wargs = null;
283         wargc = 0;
284     }
285     else version (Posix)
286     {
287         char[]* am = cast(char[]*) malloc(argc * (char[]).sizeof);
288         scope(exit) free(am);
289 
290         for (size_t i = 0; i < argc; i++)
291         {
292             auto len = strlen(argv[i]);
293             am[i] = argv[i][0 .. len];
294         }
295         args = am[0 .. argc];
296     }
297 
298     bool trapExceptions = rt_trapExceptions;
299 
300     void tryExec(void delegate() dg)
301     {
302 
303         if (trapExceptions)
304         {
305             try
306             {
307                 dg();
308             }
309             catch (Exception e)
310             {
311                 e.writeOut(delegate void(char[]s){ console(s); });
312                 result = EXIT_FAILURE;
313             }
314             catch (Object o)
315             {
316                 console (o.toString)("\n");
317                 result = EXIT_FAILURE;
318             }
319         }
320         else
321         {
322             dg();
323         }
324     }
325 
326     // NOTE: The lifetime of a process is much like the lifetime of an object:
327     //       it is initialized, then used, then destroyed.  If initialization
328     //       fails, the successive two steps are never reached.  However, if
329     //       initialization succeeds, then cleanup will occur even if the use
330     //       step fails in some way.  Here, the use phase consists of running
331     //       the user's main function.  If main terminates with an exception,
332     //       the exception is handled and then cleanup begins.  An exception
333     //       thrown during cleanup, however, will abort the cleanup process.
334 
335     void runMain()
336     {
337         result = main(args);
338     }
339 
340     void runAll()
341     {
342         rt_init();
343         if (runModuleUnitTests())
344         {
345             tryExec(&runMain);
346         }
347 
348         rt_term();
349     }
350 
351     tryExec(&runAll);
352 
353     version (Posix)
354         _d_criticalTerm();
355 
356     return result;
357 }
358 
359 }