summary refs log tree commit diff stats
path: root/lib/core/runtime_v2.nim
blob: 0165833b4a0e65415bab07306f460c2bd4abe673 (plain) (blame)
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
#[
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)
  inc allocs

proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
  dec head(p).rc

proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
  inc head(p).rc

proc nimRawDispose(p: pointer) {.compilerRtl.} =
  when not defined(nimscript):
    if head(p).rc != 0:
      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:
      dec allocs
    else:
      cstderr.rawWrite "[FATAL] unpaired dealloc\n"
      quit 1

proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
  let d = cast[ptr PNimType](p)[].destructor
  if d != nil: cast[DestructorProc](d)(p)
  nimRawDispose(p)

proc isObj(obj: PNimType, subclass: cstring): bool {.compilerproc.} =
  proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}

  result = strstr(obj.name, subclass) != nil

proc chckObj(obj: PNimType, subclass: cstring) {.compilerproc.} =
  # checks if obj is of type subclass:
  if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")