Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Fix exceptions and dynamic casts across dll boundaries #2828

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/rt/cast_.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
*/
module rt.cast_;

// because using == does a dynamic cast, but we
// are trying to implement dynamic cast.
bool compareClassInfo(ClassInfo a, ClassInfo b)
{
if (a is b)
return true;
return (a && b) && a.info.name == b.info.name;
Copy link
Contributor

@kinke kinke Aug 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be sensible to restrict the name check to version (Shared), i.e., a shared druntime (.{so,dll}). I think it's reasonable to expect a process with multiple D binaries to share a single druntime; that's not an option with DMD on Windows, I know (so could be enabled for version (DigitalMars) version (Windows) in general).

It just wouldn't slow down dynamic casting when sticking with a static druntime.

}

extern (C):

/******************************************
Expand Down Expand Up @@ -76,19 +85,19 @@ void* _d_dynamic_cast(Object o, ClassInfo c)

int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
{
if (oc is c)
if (oc.compareClassInfo(c))
return true;

do
{
if (oc.base is c)
if (oc.base.compareClassInfo(c))
return true;

// Bugzilla 2013: Use depth-first search to calculate offset
// from the derived (oc) to the base (c).
foreach (iface; oc.interfaces)
{
if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
if (iface.classinfo.compareClassInfo(c) || _d_isbaseof2(iface.classinfo, c, offset))
{
offset += iface.offset;
return true;
Expand All @@ -103,17 +112,17 @@ int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)

int _d_isbaseof(ClassInfo oc, ClassInfo c)
{
if (oc is c)
if (oc.compareClassInfo(c))
return true;

do
{
if (oc.base is c)
if (oc.base.compareClassInfo(c))
return true;

foreach (iface; oc.interfaces)
{
if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c))
if (iface.classinfo.compareClassInfo(c) || _d_isbaseof(iface.classinfo, c))
return true;
}

Expand Down
11 changes: 10 additions & 1 deletion test/shared/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ LINK_SHARED:=1

include ../common.mak

TESTS:=link load linkD linkDR loadDR host finalize
TESTS:=link load linkD linkDR loadDR host finalize dynamiccast
TESTS+=link_linkdep load_linkdep link_loaddep load_loaddep load_13414

EXPORT_DYNAMIC=$(if $(findstring $(OS),linux freebsd dragonflybsd),-L--export-dynamic,)
Expand All @@ -13,9 +13,12 @@ all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS)))

$(ROOT)/loadDR.done $(ROOT)/host.done: RUN_ARGS:=$(DRUNTIMESO)

$(ROOT)/dynamiccast.done: CLEANUP:=rm dynamiccast_endmain dynamiccast_endbar

$(ROOT)/%.done: $(ROOT)/%
@echo Testing $*
$(QUIET)$(TIMELIMIT)$< $(RUN_ARGS)
$(CLEANUP)
@touch $@

$(ROOT)/link: $(SRC)/link.d $(ROOT)/lib.so $(DRUNTIMESO)
Expand All @@ -39,6 +42,12 @@ $(ROOT)/load $(ROOT)/finalize: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib.so $(DRUNTIMESO
$(ROOT)/load_13414: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib_13414.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL)

$(ROOT)/dynamiccast: $(SRC)/dynamiccast.d $(SRC)/classdef.d $(ROOT)/dynamiccast.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $(SRC)/dynamiccast.d $(SRC)/classdef.d $(LINKDL)

$(ROOT)/dynamiccast.so: $(SRC)/dynamiccast.d $(SRC)/classdef.d $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< -version=DLL -fPIC -shared $(LINKDL)

$(ROOT)/linkD: $(SRC)/linkD.c $(ROOT)/lib.so $(DRUNTIMESO)
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(ROOT)/lib.so $(LDL) -pthread

Expand Down
4 changes: 4 additions & 0 deletions test/shared/src/classdef.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class C : Exception
{
this() { super(""); }
}
87 changes: 87 additions & 0 deletions test/shared/src/dynamiccast.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
version (DLL)
{
version (Windows)
{
import core.sys.windows.dll;
mixin SimpleDllMain;
}

pragma(mangle, "foo")
export Object foo(Object o)
{
import classdef : C;

assert(cast(C) o);
return new C;
}

pragma(mangle, "bar")
export void bar(void function() f)
{
import core.stdc.stdio : fopen, fclose;
import classdef : C;
bool caught;
try
f();
catch (C e)
caught = true;
assert(caught);

// verify we've actually got to the end, because for some reason we can
// end up exiting with code 0 when throwing an exception
fclose(fopen("dynamiccast_endbar", "w"));
throw new C;
}
}
else
{
T getFunc(T)(const(char)* sym, string thisExePath)
{
import core.runtime : Runtime;

version (Windows)
{
import core.sys.windows.winbase : GetProcAddress;
return cast(T) Runtime.loadLibrary("dynamiccast.dll")
.GetProcAddress(sym);
}
else version (Posix)
{
import core.sys.posix.dlfcn : dlsym;
import core.stdc.string : strrchr;

auto name = thisExePath ~ '\0';
const pathlen = strrchr(name.ptr, '/') - name.ptr + 1;
name = name[0 .. pathlen] ~ "dynamiccast.so";
return cast(T) Runtime.loadLibrary(name)
.dlsym(sym);
}
else static assert(0);
}

void main(string[] args)
{
import classdef : C;
import core.stdc.stdio : fopen, fclose, remove;

remove("dynamiccast_endmain");
remove("dynamiccast_endbar");

C c = new C;

auto o = getFunc!(Object function(Object))("foo", args[0])(c);
assert(cast(C) o);

bool caught;
try
getFunc!(void function(void function()))("bar", args[0])(
{ throw new C; });
catch (C e)
caught = true;
assert(caught);

// verify we've actually got to the end, because for some reason we can
// end up exiting with code 0 when throwing an exception
fclose(fopen("dynamiccast_endmain", "w"));
}
}
10 changes: 9 additions & 1 deletion test/shared/win64.mak
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ DMD=dmd
MODEL=64
DRUNTIMELIB=druntime64.lib

test: loadlibwin dllrefcount dllgc
test: loadlibwin dllrefcount dllgc dynamiccast

dllrefcount:
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) test\shared\src\dllrefcount.d
Expand All @@ -21,3 +21,11 @@ dllgc:
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofloaddllgc.exe test\shared\src\dllgc.d
loaddllgc.exe
del loaddllgc.exe loaddllgc.obj dllgc.dll dllgc.obj

dynamiccast:
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -version=DLL -shared -ofdynamiccast.dll test\shared\src\dynamiccast.d test\shared\src\classdef.d
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofdynamiccast.exe test\shared\src\dynamiccast.d test\shared\src\classdef.d
dynamiccast.exe
cmd /c "if not exist dynamiccast_endbar exit 1"
cmd /c "if not exist dynamiccast_endmain exit 1"
del dynamiccast.exe dynamiccast.dll classdef.obj dynamiccast.obj dynamiccast_endbar dynamiccast_endmain