/src/rt/dmain2.d

http://github.com/AlexeyProkhin/druntime · D · 641 lines · 487 code · 78 blank · 76 comment · 37 complexity · 975dac4e32a67aebaa7e1786e1561896 MD5 · raw file

  1. /**
  2. * Contains main program entry point and support routines.
  3. *
  4. * Copyright: Copyright Digital Mars 2000 - 2012.
  5. * License: Distributed under the
  6. * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
  7. * (See accompanying file LICENSE)
  8. * Authors: Walter Bright, Sean Kelly
  9. * Source: $(DRUNTIMESRC src/rt/_dmain2.d)
  10. */
  11. module rt.dmain2;
  12. private
  13. {
  14. import rt.memory;
  15. import rt.util.console;
  16. import rt.util.string;
  17. import core.stdc.stddef;
  18. import core.stdc.stdlib;
  19. import core.stdc.string;
  20. import core.stdc.stdio; // for printf()
  21. }
  22. version (Windows)
  23. {
  24. private import core.stdc.wchar_;
  25. extern (Windows)
  26. {
  27. alias int function() FARPROC;
  28. FARPROC GetProcAddress(void*, in char*);
  29. void* LoadLibraryW(in wchar_t*);
  30. int FreeLibrary(void*);
  31. void* LocalFree(void*);
  32. wchar_t* GetCommandLineW();
  33. wchar_t** CommandLineToArgvW(in wchar_t*, int*);
  34. export int WideCharToMultiByte(uint, uint, in wchar_t*, int, char*, int, in char*, int*);
  35. export int MultiByteToWideChar(uint, uint, in char*, int, wchar_t*, int);
  36. int IsDebuggerPresent();
  37. }
  38. pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW
  39. }
  40. version (all)
  41. {
  42. extern (C) Throwable.TraceInfo _d_traceContext(void* ptr = null);
  43. extern (C) void _d_createTrace(Object *o)
  44. {
  45. auto t = cast(Throwable) o;
  46. if (t !is null && t.info is null &&
  47. cast(byte*) t !is t.classinfo.init.ptr)
  48. {
  49. t.info = _d_traceContext();
  50. }
  51. }
  52. }
  53. version (FreeBSD)
  54. {
  55. import core.stdc.fenv;
  56. }
  57. extern (C) void _STI_monitor_staticctor();
  58. extern (C) void _STD_monitor_staticdtor();
  59. extern (C) void _STI_critical_init();
  60. extern (C) void _STD_critical_term();
  61. extern (C) void gc_init();
  62. extern (C) void gc_term();
  63. extern (C) void rt_moduleCtor();
  64. extern (C) void rt_moduleTlsCtor();
  65. extern (C) void rt_moduleDtor();
  66. extern (C) void rt_moduleTlsDtor();
  67. extern (C) void thread_joinAll();
  68. // NOTE: This is to preserve compatibility with old Windows DLLs.
  69. extern (C) void _moduleCtor()
  70. {
  71. rt_moduleCtor();
  72. }
  73. extern (C) void _moduleDtor()
  74. {
  75. rt_moduleDtor();
  76. }
  77. extern (C) void _moduleTlsCtor()
  78. {
  79. rt_moduleTlsCtor();
  80. }
  81. extern (C) void _moduleTlsDtor()
  82. {
  83. rt_moduleTlsDtor();
  84. }
  85. version (OSX)
  86. {
  87. // The bottom of the stack
  88. extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000;
  89. version (DigitalMars)
  90. {
  91. extern (C) extern (C) void _d_osx_image_init2();
  92. }
  93. }
  94. /***********************************
  95. * These are a temporary means of providing a GC hook for DLL use. They may be
  96. * replaced with some other similar functionality later.
  97. */
  98. extern (C)
  99. {
  100. void* gc_getProxy();
  101. void gc_setProxy(void* p);
  102. void gc_clrProxy();
  103. alias void* function() gcGetFn;
  104. alias void function(void*) gcSetFn;
  105. alias void function() gcClrFn;
  106. }
  107. extern (C) void* rt_loadLibrary(in char[] name)
  108. {
  109. version (Windows)
  110. {
  111. if (name.length == 0) return null;
  112. // Load a DLL at runtime
  113. enum CP_UTF8 = 65001;
  114. auto len = MultiByteToWideChar(
  115. CP_UTF8, 0, name.ptr, cast(int)name.length, null, 0);
  116. if (len == 0)
  117. return null;
  118. auto buf = cast(wchar_t*)malloc((len+1) * wchar_t.sizeof);
  119. if (buf is null)
  120. return null;
  121. scope (exit)
  122. free(buf);
  123. len = MultiByteToWideChar(
  124. CP_UTF8, 0, name.ptr, cast(int)name.length, buf, len);
  125. if (len == 0)
  126. return null;
  127. buf[len] = '\0';
  128. // BUG: LoadLibraryW() call calls rt_init(), which fails if proxy is not set!
  129. auto mod = LoadLibraryW(buf);
  130. if (mod is null)
  131. return mod;
  132. gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy");
  133. if (gcSet !is null)
  134. { // BUG: Set proxy, but too late
  135. gcSet(gc_getProxy());
  136. }
  137. return mod;
  138. }
  139. else version (Posix)
  140. {
  141. throw new Exception("rt_loadLibrary not yet implemented on Posix.");
  142. }
  143. }
  144. extern (C) bool rt_unloadLibrary(void* ptr)
  145. {
  146. version (Windows)
  147. {
  148. gcClrFn gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy");
  149. if (gcClr !is null)
  150. gcClr();
  151. return FreeLibrary(ptr) != 0;
  152. }
  153. else version (Posix)
  154. {
  155. throw new Exception("rt_unloadLibrary not yet implemented on Posix.");
  156. }
  157. }
  158. /***********************************
  159. * These functions must be defined for any D program linked
  160. * against this library.
  161. */
  162. extern (C) void onAssertError(string file, size_t line);
  163. extern (C) void onAssertErrorMsg(string file, size_t line, string msg);
  164. extern (C) void onUnittestErrorMsg(string file, size_t line, string msg);
  165. extern (C) void onRangeError(string file, size_t line);
  166. extern (C) void onHiddenFuncError(Object o);
  167. extern (C) void onSwitchError(string file, size_t line);
  168. extern (C) bool runModuleUnitTests();
  169. // this function is called from the utf module
  170. //extern (C) void onUnicodeError(string msg, size_t idx);
  171. /***********************************
  172. * These are internal callbacks for various language errors.
  173. */
  174. extern (C)
  175. {
  176. // Use ModuleInfo to get file name for "m" versions
  177. void _d_assertm(ModuleInfo* m, uint line)
  178. {
  179. onAssertError(m.name, line);
  180. }
  181. void _d_assert_msg(string msg, string file, uint line)
  182. {
  183. onAssertErrorMsg(file, line, msg);
  184. }
  185. void _d_assert(string file, uint line)
  186. {
  187. onAssertError(file, line);
  188. }
  189. void _d_unittestm(ModuleInfo* m, uint line)
  190. {
  191. _d_unittest(m.name, line);
  192. }
  193. void _d_unittest_msg(string msg, string file, uint line)
  194. {
  195. onUnittestErrorMsg(file, line, msg);
  196. }
  197. void _d_unittest(string file, uint line)
  198. {
  199. _d_unittest_msg("unittest failure", file, line);
  200. }
  201. void _d_array_bounds(ModuleInfo* m, uint line)
  202. {
  203. onRangeError(m.name, line);
  204. }
  205. void _d_switch_error(ModuleInfo* m, uint line)
  206. {
  207. onSwitchError(m.name, line);
  208. }
  209. }
  210. version (LDC)
  211. {
  212. // References to this are emitted into the vtbl for hidden functions. As
  213. // such, we need to match the calling convention for member method calls.
  214. // The below should be a reasonable guess for virtually all architectures,
  215. // given how we are lowering the this paramters to just normal (IR-level)
  216. // parameters.
  217. extern (C) void _d_hidden_func(Object o)
  218. {
  219. onHiddenFuncError(o);
  220. }
  221. }
  222. else
  223. {
  224. extern (C) void _d_hidden_func()
  225. {
  226. Object o;
  227. version(D_InlineAsm_X86)
  228. asm
  229. {
  230. mov o, EAX;
  231. }
  232. else version(D_InlineAsm_X86_64)
  233. asm
  234. {
  235. mov o, RDI;
  236. }
  237. else
  238. static assert(0, "unknown os");
  239. onHiddenFuncError(o);
  240. }
  241. }
  242. __gshared string[] _d_args = null;
  243. extern (C) string[] rt_args()
  244. {
  245. return _d_args;
  246. }
  247. // This variable is only ever set by a debugger on initialization so it should
  248. // be fine to leave it as __gshared.
  249. extern (C) __gshared bool rt_trapExceptions = true;
  250. void _d_criticalInit()
  251. {
  252. _STI_monitor_staticctor();
  253. _STI_critical_init();
  254. }
  255. alias void delegate(Throwable) ExceptionHandler;
  256. extern (C) bool rt_init(ExceptionHandler dg = null)
  257. {
  258. version (DigitalMars) version (OSX)
  259. _d_osx_image_init2();
  260. _d_criticalInit();
  261. try
  262. {
  263. gc_init();
  264. initStaticDataGC();
  265. rt_moduleCtor();
  266. rt_moduleTlsCtor();
  267. runModuleUnitTests();
  268. return true;
  269. }
  270. catch (Throwable e)
  271. {
  272. if (dg)
  273. dg(e);
  274. else
  275. throw e; // rethrow, don't silently ignore error
  276. }
  277. _d_criticalTerm();
  278. return false;
  279. }
  280. void _d_criticalTerm()
  281. {
  282. _STD_critical_term();
  283. _STD_monitor_staticdtor();
  284. }
  285. extern (C) bool rt_term(ExceptionHandler dg = null)
  286. {
  287. try
  288. {
  289. rt_moduleTlsDtor();
  290. thread_joinAll();
  291. rt_moduleDtor();
  292. gc_term();
  293. return true;
  294. }
  295. catch (Throwable e)
  296. {
  297. if (dg)
  298. dg(e);
  299. }
  300. finally
  301. {
  302. _d_criticalTerm();
  303. }
  304. return false;
  305. }
  306. struct CArgs
  307. {
  308. int argc;
  309. char** argv;
  310. }
  311. __gshared CArgs _cArgs;
  312. extern (C) CArgs rt_cArgs()
  313. {
  314. return _cArgs;
  315. }
  316. /***********************************
  317. * The D main() function supplied by the user's program
  318. *
  319. * It always has `_Dmain` symbol name and uses C calling convention.
  320. * But DMD frontend returns its type as `extern(D)` because of Issue @@@9028@@@.
  321. * As we need to deal with actual calling convention we have to mark it
  322. * as `extern(C)` and use its symbol name.
  323. */
  324. extern(C) int _Dmain(char[][] args);
  325. alias extern(C) int function(char[][] args) MainFunc;
  326. /***********************************
  327. * Substitutes for the C main() function.
  328. * Just calls into d_run_main with the default main function.
  329. * Applications are free to implement their own
  330. * main function and call the _d_run_main function
  331. * themselves with any main function.
  332. */
  333. extern (C) int main(int argc, char **argv)
  334. {
  335. return _d_run_main(argc, argv, &_Dmain);
  336. }
  337. version (Solaris) extern (C) int _main(int argc, char** argv)
  338. {
  339. // This is apparently needed on Solaris because the
  340. // C tool chain seems to expect the main function
  341. // to be called _main. It needs both not just one!
  342. return main(argc, argv);
  343. }
  344. /***********************************
  345. * Run the given main function.
  346. * Its purpose is to wrap the D main()
  347. * function and catch any unhandled exceptions.
  348. */
  349. extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
  350. {
  351. _cArgs.argc = argc;
  352. _cArgs.argv = argv;
  353. int result;
  354. version (OSX)
  355. { /* OSX does not provide a way to get at the top of the
  356. * stack, except for the magic value 0xC0000000.
  357. * But as far as the gc is concerned, argv is at the top
  358. * of the main thread's stack, so save the address of that.
  359. */
  360. __osx_stack_end = cast(void*)&argv;
  361. version (DigitalMars)
  362. {
  363. _d_osx_image_init2();
  364. }
  365. }
  366. version (FreeBSD) version (D_InlineAsm_X86)
  367. {
  368. /*
  369. * FreeBSD/i386 sets the FPU precision mode to 53 bit double.
  370. * Make it 64 bit extended.
  371. */
  372. ushort fpucw;
  373. asm
  374. {
  375. fstsw fpucw;
  376. or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision
  377. // 111111: mask all FP exceptions
  378. fldcw fpucw;
  379. }
  380. }
  381. version (Win64)
  382. {
  383. auto fp = __iob_func();
  384. stdin = &fp[0];
  385. stdout = &fp[1];
  386. stderr = &fp[2];
  387. }
  388. _STI_monitor_staticctor();
  389. _STI_critical_init();
  390. char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
  391. version (Windows)
  392. {
  393. const wchar_t* wCommandLine = GetCommandLineW();
  394. immutable size_t wCommandLineLength = wcslen(wCommandLine);
  395. int wargc;
  396. wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc);
  397. assert(wargc == argc);
  398. // This is required because WideCharToMultiByte requires int as input.
  399. assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max");
  400. immutable size_t totalArgsLength = WideCharToMultiByte(65001, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null);
  401. {
  402. char* totalArgsBuff = cast(char*) alloca(totalArgsLength);
  403. int j = 0;
  404. foreach (i; 0 .. wargc)
  405. {
  406. immutable size_t wlen = wcslen(wargs[i]);
  407. assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
  408. immutable int len = WideCharToMultiByte(65001, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null);
  409. args[i] = totalArgsBuff[j .. j + len];
  410. if (len == 0)
  411. continue;
  412. j += len;
  413. assert(j <= totalArgsLength);
  414. WideCharToMultiByte(65001, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null);
  415. }
  416. }
  417. LocalFree(wargs);
  418. wargs = null;
  419. wargc = 0;
  420. }
  421. else version (Posix)
  422. {
  423. size_t totalArgsLength = 0;
  424. foreach(i, ref arg; args)
  425. {
  426. arg = argv[i][0 .. strlen(argv[i])];
  427. totalArgsLength += arg.length;
  428. }
  429. }
  430. else
  431. static assert(0);
  432. {
  433. auto buff = cast(char[]*) alloca(argc * (char[]).sizeof + totalArgsLength);
  434. char[][] argsCopy = buff[0 .. argc];
  435. auto argBuff = cast(char*) (buff + argc);
  436. foreach(i, arg; args)
  437. {
  438. argsCopy[i] = (argBuff[0 .. arg.length] = arg[]);
  439. argBuff += arg.length;
  440. }
  441. _d_args = cast(string[]) argsCopy;
  442. }
  443. bool trapExceptions = rt_trapExceptions;
  444. version (Windows)
  445. {
  446. if (IsDebuggerPresent())
  447. trapExceptions = false;
  448. }
  449. void tryExec(scope void delegate() dg)
  450. {
  451. void printLocLine(Throwable t)
  452. {
  453. if (t.file)
  454. {
  455. console(t.classinfo.name)("@")(t.file)("(")(t.line)(")");
  456. }
  457. else
  458. {
  459. console(t.classinfo.name);
  460. }
  461. console("\n");
  462. }
  463. void printMsgLine(Throwable t)
  464. {
  465. if (t.file)
  466. {
  467. console(t.classinfo.name)("@")(t.file)("(")(t.line)(")");
  468. }
  469. else
  470. {
  471. console(t.classinfo.name);
  472. }
  473. if (t.msg)
  474. {
  475. console(": ")(t.msg);
  476. }
  477. console("\n");
  478. }
  479. void printInfoBlock(Throwable t)
  480. {
  481. if (t.info)
  482. {
  483. console("----------------\n");
  484. foreach (i; t.info)
  485. console(i)("\n");
  486. console("----------------\n");
  487. }
  488. }
  489. void print(Throwable t)
  490. {
  491. Throwable firstWithBypass = null;
  492. for (; t; t = t.next)
  493. {
  494. printMsgLine(t);
  495. printInfoBlock(t);
  496. auto e = cast(Error) t;
  497. if (e && e.bypassedException)
  498. {
  499. console("Bypasses ");
  500. printLocLine(e.bypassedException);
  501. if (firstWithBypass is null)
  502. firstWithBypass = t;
  503. }
  504. }
  505. if (firstWithBypass is null)
  506. return;
  507. console("=== Bypassed ===\n");
  508. for (t = firstWithBypass; t; t = t.next)
  509. {
  510. auto e = cast(Error) t;
  511. if (e && e.bypassedException)
  512. print(e.bypassedException);
  513. }
  514. }
  515. if (trapExceptions)
  516. {
  517. try
  518. {
  519. dg();
  520. }
  521. catch (Throwable t)
  522. {
  523. print(t);
  524. result = EXIT_FAILURE;
  525. }
  526. }
  527. else
  528. {
  529. dg();
  530. }
  531. }
  532. // NOTE: The lifetime of a process is much like the lifetime of an object:
  533. // it is initialized, then used, then destroyed. If initialization
  534. // fails, the successive two steps are never reached. However, if
  535. // initialization succeeds, then cleanup will occur even if the use
  536. // step fails in some way. Here, the use phase consists of running
  537. // the user's main function. If main terminates with an exception,
  538. // the exception is handled and then cleanup begins. An exception
  539. // thrown during cleanup, however, will abort the cleanup process.
  540. void runMain()
  541. {
  542. result = mainFunc(args);
  543. }
  544. void runAll()
  545. {
  546. gc_init();
  547. initStaticDataGC();
  548. rt_moduleCtor();
  549. rt_moduleTlsCtor();
  550. if (runModuleUnitTests())
  551. tryExec(&runMain);
  552. else
  553. result = EXIT_FAILURE;
  554. rt_moduleTlsDtor();
  555. thread_joinAll();
  556. rt_moduleDtor();
  557. gc_term();
  558. }
  559. tryExec(&runAll);
  560. _STD_critical_term();
  561. _STD_monitor_staticdtor();
  562. return result;
  563. }