diff --git a/src/rt/cast_.d b/src/rt/cast_.d index 5d70fc5c3b..dcb4438c70 100644 --- a/src/rt/cast_.d +++ b/src/rt/cast_.d @@ -19,6 +19,16 @@ extern (C): nothrow: pure: +// Needed because ClassInfo.opEquals(Object) does a dynamic cast, +// but we are trying to implement dynamic cast. +extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe +{ + if (a is b) + return true; + // take care of potential duplicates across binaries + return a.name == b.name; +} + /****************************************** * Given a pointer: * If it is an Object, return that Object. @@ -80,19 +90,19 @@ void* _d_dynamic_cast(Object o, ClassInfo c) int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe { - if (oc is c) + if (areClassInfosEqual(oc, c)) return true; do { - if (oc.base is c) + if (oc.base && areClassInfosEqual(oc.base, 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 (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset)) { offset += iface.offset; return true; @@ -107,17 +117,17 @@ int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t o int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe { - if (oc is c) + if (areClassInfosEqual(oc, c)) return true; do { - if (oc.base is c) + if (oc.base && areClassInfosEqual(oc.base, c)) return true; foreach (iface; oc.interfaces) { - if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c)) + if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c)) return true; } diff --git a/test/shared/Makefile b/test/shared/Makefile index ae997c5c8b..dbdffaf85e 100644 --- a/test/shared/Makefile +++ b/test/shared/Makefile @@ -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,) @@ -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) @@ -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 $(ROOT)/dynamiccast.so $(DRUNTIMESO) + $(QUIET)$(DMD) $(DFLAGS) -of$@ $(SRC)/dynamiccast.d $(LINKDL) + +$(ROOT)/dynamiccast.so: $(SRC)/dynamiccast.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 diff --git a/test/shared/src/dynamiccast.d b/test/shared/src/dynamiccast.d new file mode 100644 index 0000000000..6fc95e7568 --- /dev/null +++ b/test/shared/src/dynamiccast.d @@ -0,0 +1,98 @@ +class C : Exception +{ + this() { super(""); } +} + +version (DLL) +{ + version (Windows) + { + import core.sys.windows.dll; + mixin SimpleDllMain; + } + + pragma(mangle, "foo") + export Object foo(Object o) + { + assert(cast(C) o); + return new C; + } + + pragma(mangle, "bar") + export void bar(void function() f) + { + import core.stdc.stdio : fopen, fclose; + 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); + } + + version (DigitalMars) version (Win64) version = DMD_Win64; + + void main(string[] args) + { + 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); + + version (DMD_Win64) + { + // FIXME: apparent crash & needs more work, see https://github.com/dlang/druntime/pull/2874 + fclose(fopen("dynamiccast_endbar", "w")); + } + else + { + 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")); + } +} diff --git a/test/shared/win64.mak b/test/shared/win64.mak index 8535d93d32..9658562dff 100644 --- a/test/shared/win64.mak +++ b/test/shared/win64.mak @@ -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 @@ -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 + $(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofdynamiccast.exe test\shared\src\dynamiccast.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 dynamiccast.obj dynamiccast_endbar dynamiccast_endmain