1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
#[
In this new runtime we simplify the object layouts a bit: The runtime type
information is only accessed for the objects that have it and it's always
at offset 0 then. The ``ref`` object header is independent from the
runtime type and only contains a reference count.
Object subtyping is checked via the generated 'name'. This should have
comparable overhead to the old pointer chasing approach but has the benefit
that it works across DLL boundaries.
The generated name is a concatenation of the object names in the hierarchy
so that a subtype check becomes a substring check. For example::
type
ObjectA = object of RootObj
ObjectB = object of ObjectA
ObjectA's ``name`` is "|ObjectA|RootObj|".
ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|".
Now to check for ``x of ObjectB`` we need to check
for ``x.typ.name.hasSubstring("|ObjectB|")``. In the actual implementation,
however, we could also use a
hash of ``package & "." & module & "." & name`` to save space.
]#
type
RefHeader = object
rc: int # the object header is now a single RC field.
# we could remove it in non-debug builds but this seems
# unwise.
template `+!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) +% s)
template `-!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) -% s)
template head(p: pointer): ptr RefHeader =
cast[ptr RefHeader](cast[int](p) -% sizeof(RefHeader))
var allocs*: int
proc nimNewObj(size: int): pointer {.compilerRtl.} =
let s = size + sizeof(RefHeader)
when defined(nimscript):
discard
elif defined(useMalloc):
var orig = c_malloc(s)
nimZeroMem(orig, s)
result = orig +! sizeof(RefHeader)
else:
result = alloc0(s) +! sizeof(RefHeader)
when hasThreadSupport:
atomicInc allocs
else:
inc allocs
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
when hasThreadSupport:
atomicDec head(p).rc
else:
dec head(p).rc
proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
when hasThreadSupport:
atomicInc head(p).rc
else:
inc head(p).rc
proc nimRawDispose(p: pointer) {.compilerRtl.} =
when not defined(nimscript):
when hasThreadSupport:
let hasDanglingRefs = atomicLoadN(addr head(p).rc, ATOMIC_RELAXED) != 0
else:
let hasDanglingRefs = head(p).rc != 0
if hasDanglingRefs:
cstderr.rawWrite "[FATAL] dangling references exist\n"
quit 1
when defined(useMalloc):
c_free(p -! sizeof(RefHeader))
else:
dealloc(p -! sizeof(RefHeader))
if allocs > 0:
when hasThreadSupport:
discard atomicDec(allocs)
else:
dec allocs
else:
cstderr.rawWrite "[FATAL] unpaired dealloc\n"
quit 1
template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x))
#proc dispose*(x: pointer) = nimRawDispose(x)
proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
let d = cast[ptr PNimType](p)[].destructor
if d != nil: cast[DestructorProc](d)(p)
when false:
cstderr.rawWrite cast[ptr PNimType](p)[].name
cstderr.rawWrite "\n"
if d == nil:
cstderr.rawWrite "bah, nil\n"
else:
cstderr.rawWrite "has destructor!\n"
nimRawDispose(p)
proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
if p != nil:
when hasThreadSupport:
if atomicLoadN(addr head(p).rc, ATOMIC_RELAXED) == 0:
result = true
else:
if atomicDec(head(p).rc) <= 0:
result = true
else:
if head(p).rc == 0:
result = true
else:
dec head(p).rc
proc isObj(obj: PNimType, subclass: cstring): bool {.compilerRtl, inl.} =
proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
result = strstr(obj.name, subclass) != nil
proc chckObj(obj: PNimType, subclass: cstring) {.compilerRtl.} =
# checks if obj is of type subclass:
if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")
|