diff options
Diffstat (limited to 'libphobos/libdruntime/rt/dmain2.d')
-rw-r--r-- | libphobos/libdruntime/rt/dmain2.d | 333 |
1 files changed, 221 insertions, 112 deletions
diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index e6acbd5..328452e 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -1,12 +1,12 @@ /** * Contains druntime startup and shutdown routines. * - * Copyright: Copyright Digital Mars 2000 - 2013. + * Copyright: Copyright Digital Mars 2000 - 2018. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_dmain2.d) + * Source: $(DRUNTIMESRC rt/_dmain2.d) */ /* NOTE: This file has been patched from the original DMD distribution to @@ -14,22 +14,27 @@ */ module rt.dmain2; -private -{ - import rt.memory; - import rt.sections; - import core.atomic; - import core.stdc.stddef; - import core.stdc.stdlib; - import core.stdc.string; - import core.stdc.stdio; // for printf() - import core.stdc.errno : errno; -} +import rt.memory; +import rt.sections; +import core.atomic; +import core.stdc.stddef; +import core.stdc.stdlib; +import core.stdc.string; +import core.stdc.stdio; // for printf() +import core.stdc.errno : errno; version (Windows) { - private import core.stdc.wchar_; - private import core.sys.windows.windows; + import core.stdc.wchar_; + import core.sys.windows.basetsd : HANDLE; + import core.sys.windows.shellapi : CommandLineToArgvW; + import core.sys.windows.winbase : FreeLibrary, GetCommandLineW, GetProcAddress, + IsDebuggerPresent, LoadLibraryW, LocalFree, WriteFile; + import core.sys.windows.wincon : CONSOLE_SCREEN_BUFFER_INFO, GetConsoleOutputCP, + GetConsoleScreenBufferInfo; + import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar, WideCharToMultiByte; + import core.sys.windows.winnt : WCHAR; + import core.sys.windows.winuser : MB_ICONERROR, MessageBoxW; pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW } @@ -47,27 +52,33 @@ version (DragonFlyBSD) import core.stdc.fenv; } +// not sure why we can't define this in one place, but this is to keep this +// module from importing core.runtime. +struct UnitTestResult +{ + size_t executed; + size_t passed; + bool runMain; + bool summarize; +} + extern (C) void _d_monitor_staticctor(); extern (C) void _d_monitor_staticdtor(); extern (C) void _d_critical_init(); extern (C) void _d_critical_term(); extern (C) void gc_init(); extern (C) void gc_term(); +extern (C) void thread_init() @nogc; +extern (C) void thread_term() @nogc; extern (C) void lifetime_init(); extern (C) void rt_moduleCtor(); extern (C) void rt_moduleTlsCtor(); extern (C) void rt_moduleDtor(); extern (C) void rt_moduleTlsDtor(); extern (C) void thread_joinAll(); -extern (C) bool runModuleUnitTests(); +extern (C) UnitTestResult runModuleUnitTests(); extern (C) void _d_initMonoTime(); -version (OSX) -{ - // The bottom of the stack - extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000; -} - version (CRuntime_Microsoft) { extern(C) void init_msvc(); @@ -83,9 +94,6 @@ extern (C) string[] rt_args() return _d_args; } -// make arguments passed to main available for being filtered by runtime initializers -extern(C) __gshared char[][] _d_main_args = null; - // This variable is only ever set by a debugger on initialization so it should // be fine to leave it as __gshared. extern (C) __gshared bool rt_trapExceptions = true; @@ -123,7 +131,8 @@ extern (C) int rt_init() // this initializes mono time before anything else to allow usage // in other druntime systems. _d_initMonoTime(); - gc_init(); + thread_init(); + // TODO: fixme - calls GC.addRange -> Initializes GC initStaticDataGC(); lifetime_init(); rt_moduleCtor(); @@ -132,7 +141,7 @@ extern (C) int rt_init() } catch (Throwable t) { - _initCount = 0; + atomicStore!(MemoryOrder.raw)(_initCount, 0); _d_print_throwable(t); } _d_critical_term(); @@ -145,7 +154,7 @@ extern (C) int rt_init() */ extern (C) int rt_term() { - if (!_initCount) return 0; // was never initialized + if (atomicLoad!(MemoryOrder.raw)(_initCount) == 0) return 0; // was never initialized if (atomicOp!"-="(_initCount, 1)) return 1; try @@ -154,6 +163,7 @@ extern (C) int rt_term() thread_joinAll(); rt_moduleDtor(); gc_term(); + thread_term(); return 1; } catch (Throwable t) @@ -234,74 +244,22 @@ extern (C) CArgs rt_cArgs() @nogc return _cArgs; } -/*********************************** - * Run the given main function. - * Its purpose is to wrap the D main() - * function and catch any unhandled exceptions. - */ +/// Type of the D main() function (`_Dmain`). private alias extern(C) int function(char[][] args) MainFunc; -extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) +/** + * Sets up the D char[][] command-line args, initializes druntime, + * runs embedded unittests and then runs the given D main() function, + * optionally catching and printing any unhandled exceptions. + */ +extern (C) int _d_run_main(int argc, char** argv, MainFunc mainFunc) { + // Set up _cArgs and array of D char[] slices, then forward to _d_run_main2 + // Remember the original C argc/argv _cArgs.argc = argc; _cArgs.argv = argv; - int result; - - version (OSX) - { /* OSX does not provide a way to get at the top of the - * stack, except for the magic value 0xC0000000. - * But as far as the gc is concerned, argv is at the top - * of the main thread's stack, so save the address of that. - */ - __osx_stack_end = cast(void*)&argv; - } - - version (FreeBSD) version (D_InlineAsm_X86) - { - /* - * FreeBSD/i386 sets the FPU precision mode to 53 bit double. - * Make it 64 bit extended. - */ - ushort fpucw; - asm - { - fstsw fpucw; - or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision - // 111111: mask all FP exceptions - fldcw fpucw; - } - } - version (CRuntime_Microsoft) - { - // enable full precision for reals - version (D_InlineAsm_X86_64) - { - asm - { - push RAX; - fstcw word ptr [RSP]; - or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision - // 111111: mask all FP exceptions - fldcw word ptr [RSP]; - pop RAX; - } - } - else version (D_InlineAsm_X86) - { - asm - { - push EAX; - fstcw word ptr [ESP]; - or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision - // 111111: mask all FP exceptions - fldcw word ptr [ESP]; - pop EAX; - } - } - } - version (Windows) { /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that, @@ -309,10 +267,10 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) * Then, reparse into wargc/wargs, and then use Windows API to convert * to UTF-8. */ - const wchar_t* wCommandLine = GetCommandLineW(); + const wCommandLine = GetCommandLineW(); immutable size_t wCommandLineLength = wcslen(wCommandLine); int wargc; - wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc); + auto wargs = CommandLineToArgvW(wCommandLine, &wargc); // assert(wargc == argc); /* argc can be broken by Unicode arguments */ // Allocate args[] on the stack - use wargc @@ -357,6 +315,114 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) else static assert(0); + return _d_run_main2(args, totalArgsLength, mainFunc); +} + +/** + * Windows-specific version for wide command-line arguments, e.g., + * from a wmain/wWinMain C entry point. + * This wide version uses the specified arguments, unlike narrow + * _d_run_main which uses the actual (wide) process arguments instead. + */ +version (Windows) +extern (C) int _d_wrun_main(int argc, wchar** wargv, MainFunc mainFunc) +{ + // Allocate args[] on the stack + char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc]; + + // 1st pass: compute each argument's length as UTF-16 and UTF-8 + size_t totalArgsLength = 0; + foreach (i; 0 .. argc) + { + const warg = wargv[i]; + const size_t wlen = wcslen(warg) + 1; // incl. terminating null + assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max"); + const int len = WideCharToMultiByte(CP_UTF8, 0, warg, cast(int) wlen, null, 0, null, null); + args[i] = (cast(char*) wlen)[0 .. len]; // args[i].ptr = wlen, args[i].length = len + totalArgsLength += len; + } + + // Allocate a single buffer for all (null-terminated) argument strings in UTF-8 on the stack + char* utf8Buffer = cast(char*) alloca(totalArgsLength); + + // 2nd pass: convert to UTF-8 and finalize `args` + char* utf8 = utf8Buffer; + foreach (i; 0 .. argc) + { + const wlen = cast(int) args[i].ptr; + const len = cast(int) args[i].length; + WideCharToMultiByte(CP_UTF8, 0, wargv[i], wlen, utf8, len, null, null); + args[i] = utf8[0 .. len-1]; // excl. terminating null + utf8 += len; + } + + // Set C argc/argv; argv is a new stack-allocated array of UTF-8 C strings + char*[] argv = (cast(char**) alloca(argc * (char*).sizeof))[0 .. argc]; + foreach (i, ref arg; argv) + arg = args[i].ptr; + _cArgs.argc = argc; + _cArgs.argv = argv.ptr; + + totalArgsLength -= argc; // excl. null terminator per arg + return _d_run_main2(args, totalArgsLength, mainFunc); +} + +private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainFunc mainFunc) +{ + int result; + + version (FreeBSD) version (D_InlineAsm_X86) + { + /* + * FreeBSD/i386 sets the FPU precision mode to 53 bit double. + * Make it 64 bit extended. + */ + ushort fpucw; + asm + { + fstsw fpucw; + or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + fldcw fpucw; + } + } + version (CRuntime_Microsoft) + { + // enable full precision for reals + version (D_InlineAsm_X86_64) + { + asm + { + push RAX; + fstcw word ptr [RSP]; + or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + fldcw word ptr [RSP]; + pop RAX; + } + } + else version (D_InlineAsm_X86) + { + asm + { + push EAX; + fstcw word ptr [ESP]; + or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + fldcw word ptr [ESP]; + pop EAX; + } + } + else version (GNU_InlineAsm) + { + size_t fpu_cw; + asm { "fstcw %0" : "=m" (fpu_cw); } + fpu_cw |= 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + asm { "fldcw %0" : "=m" (fpu_cw); } + } + } + /* Create a copy of args[] on the stack to be used for main, so that rt_args() * cannot be modified by the user. * Note that when this function returns, _d_args will refer to garbage. @@ -368,28 +434,33 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) char[][] argsCopy = buff[0 .. args.length]; auto argBuff = cast(char*) (buff + args.length); size_t j = 0; + import rt.config : rt_cmdline_enabled; + bool parseOpts = rt_cmdline_enabled!(); foreach (arg; args) { - if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options - { - argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]); - argBuff += arg.length; - } + // Do not pass Druntime options to the program + if (parseOpts && arg.length >= 6 && arg[0 .. 6] == "--DRT-") + continue; + // https://issues.dlang.org/show_bug.cgi?id=20459 + if (arg == "--") + parseOpts = false; + argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]); + argBuff += arg.length; } args = argsCopy[0..j]; } - bool trapExceptions = rt_trapExceptions; + auto useExceptionTrap = parseExceptionOptions(); version (Windows) { if (IsDebuggerPresent()) - trapExceptions = false; + useExceptionTrap = false; } void tryExec(scope void delegate() dg) { - if (trapExceptions) + if (useExceptionTrap) { try { @@ -417,8 +488,34 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) // thrown during cleanup, however, will abort the cleanup process. void runAll() { - if (rt_init() && runModuleUnitTests()) - tryExec({ result = mainFunc(args); }); + if (rt_init()) + { + auto utResult = runModuleUnitTests(); + assert(utResult.passed <= utResult.executed); + if (utResult.passed == utResult.executed) + { + if (utResult.summarize) + { + if (utResult.passed == 0) + .fprintf(.stderr, "No unittests run\n"); + else + .fprintf(.stderr, "%d modules passed unittests\n", + cast(int)utResult.passed); + } + if (utResult.runMain) + tryExec({ result = mainFunc(args); }); + else + result = EXIT_SUCCESS; + } + else + { + if (utResult.summarize) + .fprintf(.stderr, "%d/%d modules FAILED unittests\n", + cast(int)(utResult.executed - utResult.passed), + cast(int)utResult.executed); + result = EXIT_FAILURE; + } + } else result = EXIT_FAILURE; @@ -441,17 +538,17 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) return result; } -private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink) +private void formatThrowable(Throwable t, scope void delegate(const scope char[] s) nothrow sink) { - for (; t; t = t.next) + foreach (u; t) { - t.toString(sink); sink("\n"); + u.toString(sink); sink("\n"); - auto e = cast(Error)t; + auto e = cast(Error)u; if (e is null || e.bypassedException is null) continue; sink("=== Bypassed ===\n"); - for (auto t2 = e.bypassedException; t2; t2 = t2.next) + foreach (t2; e.bypassedException) { t2.toString(sink); sink("\n"); } @@ -459,6 +556,18 @@ private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothr } } +private auto parseExceptionOptions() +{ + import rt.config : rt_configOption; + import core.internal.parseoptions : rt_parseOption; + const optName = "trapExceptions"; + auto option = rt_configOption(optName); + auto trap = rt_trapExceptions; + if (option.length) + rt_parseOption(optName, option, trap, ""); + return trap; +} + extern (C) void _d_print_throwable(Throwable t) { // On Windows, a console may not be present to print the output to. @@ -468,17 +577,17 @@ extern (C) void _d_print_throwable(Throwable t) { static struct WSink { - wchar_t* ptr; size_t len; + WCHAR* ptr; size_t len; - void sink(in char[] s) scope nothrow + void sink(const scope char[] s) scope nothrow { if (!s.length) return; int swlen = MultiByteToWideChar( CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0); if (!swlen) return; - auto newPtr = cast(wchar_t*)realloc(ptr, - (this.len + swlen + 1) * wchar_t.sizeof); + auto newPtr = cast(WCHAR*)realloc(ptr, + (this.len + swlen + 1) * WCHAR.sizeof); if (!newPtr) return; ptr = newPtr; auto written = MultiByteToWideChar( @@ -486,7 +595,7 @@ extern (C) void _d_print_throwable(Throwable t) len += written; } - wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; } + typeof(ptr) get() { if (ptr) ptr[len] = 0; return ptr; } void free() { .free(ptr); } } @@ -558,7 +667,7 @@ extern (C) void _d_print_throwable(Throwable t) } } - void sink(in char[] buf) scope nothrow + void sink(const scope char[] buf) scope nothrow { fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); } |