Bug 19539 – Interface address is offset by 16bytes, causing memory leaks
Status
RESOLVED
Resolution
INVALID
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2019-01-02T20:22:10Z
Last change time
2023-10-03T17:11:00Z
Keywords
bootcamp
Assigned to
No Owner
Creator
Dru
Comments
Comment #0 by ihcuo21 — 2019-01-02T20:22:10Z
code example
-----------------------
import std.stdio;
import core.memory;
interface A {
int func();
}
class AX : A {
override int func() {
return 1;
}
char[100] data;
}
void main() {
AX ax = new AX;
A a = ax;
//the offset is 16 bytes
writeln(cast(void*)ax);
writeln(cast(void*)a);
//delete will not free memory because "a" does not point to allocated block
__delete(a);
}
-----------------------
Comment #1 by info — 2023-10-03T17:11:00Z
This is actually correct. This happends because of the way interfaces are implemented in machine code.
First of we need to understand how AX is layed out in memory:
---
<vtable>, <monitor>, <vtable for interface A>, <...rest of AX's fields>
---
Since vtable and monitor are both pointers, skipping 16 bytes means we now have the interface's vtable at the head. This is more effective in the long run for looking up methods to be called.
Secondly we need to know how exactly interface methods are invoked. One would belive that 'a.func()' simply would call 'func()' of 'AX' but thats not quite right. D (like many other languages) compiles here so called "thunk-functions", which essentially are specialized wrapper function that know what original method we're wanting to call and how to get from the modified pointer that 'a' has, back to the original pointer of 'ax', so that 'func()' still has all the data it needs. In this case it simply subtracts 16 bytes from the pointer, landing at exactly the original address.
This same behaviour is also the magic that make inheritance work in the first place.
If you print the address of 'this' inside 'func()' you'll see we got the orginal address back.
Also: If you run 'writeln(cast(void*)cast(AX)a);', you'll also get the original address back :)