blob: e6acbd5105f242cf65a97751b51f0c78b1a029f7 [file] [log] [blame]
/**
* Contains druntime startup and shutdown routines.
*
* Copyright: Copyright Digital Mars 2000 - 2013.
* 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)
*/
/* NOTE: This file has been patched from the original DMD distribution to
* work with the GDC compiler.
*/
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;
}
version (Windows)
{
private import core.stdc.wchar_;
private import core.sys.windows.windows;
pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW
}
version (FreeBSD)
{
import core.stdc.fenv;
}
version (NetBSD)
{
import core.stdc.fenv;
}
version (DragonFlyBSD)
{
import core.stdc.fenv;
}
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 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) 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();
}
/* To get out-of-band access to the args[] passed to main().
*/
__gshared string[] _d_args = null;
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;
alias void delegate(Throwable) ExceptionHandler;
/**
* Keep track of how often rt_init/rt_term were called.
*/
shared size_t _initCount;
/**********************************************
* Initialize druntime.
* If a C program wishes to call D code, and there's no D main(), then it
* must call rt_init() and rt_term().
*/
extern (C) int rt_init()
{
/* @@BUG 11380 @@ Need to synchronize rt_init/rt_term calls for
version (Shared) druntime, because multiple C threads might
initialize different D libraries without knowing about the
shared druntime. Also we need to attach any thread that calls
rt_init. */
if (atomicOp!"+="(_initCount, 1) > 1) return 1;
version (CRuntime_Microsoft)
init_msvc();
_d_monitor_staticctor();
_d_critical_init();
try
{
initSections();
// this initializes mono time before anything else to allow usage
// in other druntime systems.
_d_initMonoTime();
gc_init();
initStaticDataGC();
lifetime_init();
rt_moduleCtor();
rt_moduleTlsCtor();
return 1;
}
catch (Throwable t)
{
_initCount = 0;
_d_print_throwable(t);
}
_d_critical_term();
_d_monitor_staticdtor();
return 0;
}
/**********************************************
* Terminate use of druntime.
*/
extern (C) int rt_term()
{
if (!_initCount) return 0; // was never initialized
if (atomicOp!"-="(_initCount, 1)) return 1;
try
{
rt_moduleTlsDtor();
thread_joinAll();
rt_moduleDtor();
gc_term();
return 1;
}
catch (Throwable t)
{
_d_print_throwable(t);
}
finally
{
finiSections();
_d_critical_term();
_d_monitor_staticdtor();
}
return 0;
}
/**********************************************
* Trace handler
*/
alias Throwable.TraceInfo function(void* ptr) TraceHandler;
private __gshared TraceHandler traceHandler = null;
/**
* Overrides the default trace hander with a user-supplied version.
*
* Params:
* h = The new trace handler. Set to null to use the default handler.
*/
extern (C) void rt_setTraceHandler(TraceHandler h)
{
traceHandler = h;
}
/**
* Return the current trace handler
*/
extern (C) TraceHandler rt_getTraceHandler()
{
return traceHandler;
}
/**
* This function will be called when an exception is constructed. The
* user-supplied trace handler will be called if one has been supplied,
* otherwise no trace will be generated.
*
* Params:
* ptr = A pointer to the location from which to generate the trace, or null
* if the trace should be generated from within the trace handler
* itself.
*
* Returns:
* An object describing the current calling context or null if no handler is
* supplied.
*/
extern (C) Throwable.TraceInfo _d_traceContext(void* ptr = null)
{
if (traceHandler is null)
return null;
return traceHandler(ptr);
}
/***********************************
* Provide out-of-band access to the original C argc/argv
* passed to this program via main(argc,argv).
*/
struct CArgs
{
int argc;
char** argv;
}
__gshared CArgs _cArgs;
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.
*/
private alias extern(C) int function(char[][] args) MainFunc;
extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
{
// 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,
* we ignore argc/argv and go get the Windows command line again as UTF-16.
* Then, reparse into wargc/wargs, and then use Windows API to convert
* to UTF-8.
*/
const wchar_t* wCommandLine = GetCommandLineW();
immutable size_t wCommandLineLength = wcslen(wCommandLine);
int wargc;
wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc);
// assert(wargc == argc); /* argc can be broken by Unicode arguments */
// Allocate args[] on the stack - use wargc
char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc];
// This is required because WideCharToMultiByte requires int as input.
assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max");
immutable size_t totalArgsLength = WideCharToMultiByte(CP_UTF8, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null);
{
char* totalArgsBuff = cast(char*) alloca(totalArgsLength);
size_t j = 0;
foreach (i; 0 .. wargc)
{
immutable size_t wlen = wcslen(wargs[i]);
assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
immutable int len = WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null);
args[i] = totalArgsBuff[j .. j + len];
if (len == 0)
continue;
j += len;
assert(j <= totalArgsLength);
WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null);
}
}
LocalFree(wargs);
wargs = null;
wargc = 0;
}
else version (Posix)
{
// Allocate args[] on the stack
char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
size_t totalArgsLength = 0;
foreach (i, ref arg; args)
{
arg = argv[i][0 .. strlen(argv[i])];
totalArgsLength += arg.length;
}
}
else
static assert(0);
/* 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.
*/
{
_d_args = cast(string[]) args;
auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength);
char[][] argsCopy = buff[0 .. args.length];
auto argBuff = cast(char*) (buff + args.length);
size_t j = 0;
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;
}
}
args = argsCopy[0..j];
}
bool trapExceptions = rt_trapExceptions;
version (Windows)
{
if (IsDebuggerPresent())
trapExceptions = false;
}
void tryExec(scope void delegate() dg)
{
if (trapExceptions)
{
try
{
dg();
}
catch (Throwable t)
{
_d_print_throwable(t);
result = EXIT_FAILURE;
}
}
else
{
dg();
}
}
// NOTE: The lifetime of a process is much like the lifetime of an object:
// it is initialized, then used, then destroyed. If initialization
// fails, the successive two steps are never reached. However, if
// initialization succeeds, then cleanup will occur even if the use
// step fails in some way. Here, the use phase consists of running
// the user's main function. If main terminates with an exception,
// the exception is handled and then cleanup begins. An exception
// thrown during cleanup, however, will abort the cleanup process.
void runAll()
{
if (rt_init() && runModuleUnitTests())
tryExec({ result = mainFunc(args); });
else
result = EXIT_FAILURE;
if (!rt_term())
result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result;
}
tryExec(&runAll);
// Issue 10344: flush stdout and return nonzero on failure
if (.fflush(.stdout) != 0)
{
.fprintf(.stderr, "Failed to flush stdout: %s\n", .strerror(.errno));
if (result == 0)
{
result = EXIT_FAILURE;
}
}
return result;
}
private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink)
{
for (; t; t = t.next)
{
t.toString(sink); sink("\n");
auto e = cast(Error)t;
if (e is null || e.bypassedException is null) continue;
sink("=== Bypassed ===\n");
for (auto t2 = e.bypassedException; t2; t2 = t2.next)
{
t2.toString(sink); sink("\n");
}
sink("=== ~Bypassed ===\n");
}
}
extern (C) void _d_print_throwable(Throwable t)
{
// On Windows, a console may not be present to print the output to.
// Show a message box instead. If the console is present, convert to
// the correct encoding.
version (Windows)
{
static struct WSink
{
wchar_t* ptr; size_t len;
void sink(in 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);
if (!newPtr) return;
ptr = newPtr;
auto written = MultiByteToWideChar(
CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen);
len += written;
}
wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; }
void free() { .free(ptr); }
}
HANDLE windowsHandle(int fd)
{
version (CRuntime_Microsoft)
return cast(HANDLE)_get_osfhandle(fd);
else
return _fdToHandle(fd);
}
auto hStdErr = windowsHandle(fileno(stderr));
CONSOLE_SCREEN_BUFFER_INFO sbi;
bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0;
// ensure the exception is shown at the beginning of the line, while also
// checking whether stderr is a valid file
int written = fprintf(stderr, "\n");
if (written <= 0)
{
WSink buf;
formatThrowable(t, &buf.sink);
if (buf.ptr)
{
WSink caption;
if (t)
caption.sink(t.classinfo.name);
// Avoid static user32.dll dependency for console applications
// by loading it dynamically as needed
auto user32 = LoadLibraryW("user32.dll");
if (user32)
{
alias typeof(&MessageBoxW) PMessageBoxW;
auto pMessageBoxW = cast(PMessageBoxW)
GetProcAddress(user32, "MessageBoxW");
if (pMessageBoxW)
pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR);
}
FreeLibrary(user32);
caption.free();
buf.free();
}
return;
}
else if (isConsole)
{
WSink buf;
formatThrowable(t, &buf.sink);
if (buf.ptr)
{
uint codepage = GetConsoleOutputCP();
int slen = WideCharToMultiByte(codepage, 0,
buf.ptr, cast(int)buf.len, null, 0, null, null);
auto sptr = cast(char*)malloc(slen * char.sizeof);
if (sptr)
{
WideCharToMultiByte(codepage, 0,
buf.ptr, cast(int)buf.len, sptr, slen, null, null);
WriteFile(hStdErr, sptr, slen, null, null);
free(sptr);
}
buf.free();
}
return;
}
}
void sink(in char[] buf) scope nothrow
{
fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
}
formatThrowable(t, &sink);
}