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

Commit

Permalink
Merge pull request #1327 from 9rnsr/fix2013
Browse files Browse the repository at this point in the history
Issue 2013 - interface to interface dynamic cast is incorrect in some cases
  • Loading branch information
rainers committed Jul 19, 2015
2 parents 17d2a7a + c83a79c commit 4f19d34
Showing 1 changed file with 49 additions and 27 deletions.
76 changes: 49 additions & 27 deletions src/rt/cast_.d
Expand Up @@ -22,10 +22,9 @@ extern (C):
* If it is null, return null.
* Else, undefined crash
*/

Object _d_toObject(void* p)
{
if(!p)
if (!p)
return null;

Object o = cast(Object) p;
Expand All @@ -36,24 +35,22 @@ Object _d_toObject(void* p)
* so we rely on pointers never being less than 64K,
* and Objects never being greater.
*/
if(pi.offset < 0x10000)
if (pi.offset < 0x10000)
{
debug(cast_) printf("\tpi.offset = %d\n", pi.offset);
return cast(Object)(p - pi.offset);
}
return o;
}


/*************************************
* Attempts to cast Object o to class c.
* Returns o if successful, null if not.
*/

void* _d_interface_cast(void* p, ClassInfo c)
{
debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, c.name);
if(!p)
if (!p)
return null;

Interface* pi = **cast(Interface***) p;
Expand All @@ -68,7 +65,7 @@ void* _d_dynamic_cast(Object o, ClassInfo c)

void* res = null;
size_t offset = 0;
if(o && _d_isbaseof2(typeid(o), c, offset))
if (o && _d_isbaseof2(typeid(o), c, offset))
{
debug(cast_) printf("\toffset = %d\n", offset);
res = cast(void*) o + offset;
Expand All @@ -79,73 +76,98 @@ 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 is c)
return true;

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

foreach(iface; oc.interfaces)
version(all)
{
// Bugzilla 2013: Until the ClassInfo.interfaces[] sturcture is fixed
// (it's generated by compiler), we should keep breadth-first search
// to avoid existing code breaking.

// Before the compiler fix:
// When oc is a ClassInfo of interface type, iface.offset is always zero.
// So the offset addition has no operation.
// After the compiler fix:
// When oc is a ClassInfo of interface type, iface.offset may be nonzero.
foreach (iface; oc.interfaces)
{
if(iface.classinfo is c)
if (iface.classinfo is c)
{
offset = iface.offset;
offset += iface.offset;
return true;
}
}

foreach(iface; oc.interfaces)
foreach (iface; oc.interfaces)
{
if (_d_isbaseof2(iface.classinfo, c, offset))
{
offset += iface.offset;
return true;
}
}
}
else
{
// Bugzilla 2013: After the compiler is actually fixed, we can simply use
// depth-first search to calculate class to base interface offset.
foreach (iface; oc.interfaces)
{
if(_d_isbaseof2(iface.classinfo, c, offset))
if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
{
offset = iface.offset;
offset += iface.offset;
return true;
}
}
}

oc = oc.base;
}
while(oc);
} while(oc);

return false;
}

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

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

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

oc = oc.base;
}
while(oc);
} while(oc);

return false;
}

/*********************************
* Find the vtbl[] associated with Interface ic.
*/

void* _d_interface_vtbl(ClassInfo ic, Object o)
{
debug(cast_) printf("__d_interface_vtbl(o = %p, ic = %p)\n", o, ic);

assert(o);

foreach(iface; typeid(o).interfaces)
if(iface.classinfo is ic)
foreach (iface; typeid(o).interfaces)
{
if (iface.classinfo is ic)
return cast(void*) iface.vtbl;

}
assert(0);
}

0 comments on commit 4f19d34

Please sign in to comment.