I've tried to convert <https://github.com/pragmagic/godot-nim> for use with
orc. And while it's been working for the most part, I'm getting a crash in a
destructor that I've mechanically converted from a finalizer. godot-nim relies
on gc:refc and when converting to orc I had to convert the finalizer for this
ref type:
type
NimGodotObject* = ref object of RootObj
## The base type all Godot types inherit from.
## Manages lifecycle of the wrapped ``GodotObject``.
godotObject: ptr GodotObject
linkedObjectPtr: pointer
## Wrapper around native object that is the container of the Nim
"script"
## This is needed for `of` checks and `as` conversions to work as
## expected. For example, Nim type may inherit from ``Spatial``, but
the
## script is attached to ``Particles``. In this case conversion to
## ``Particles`` is valid, but Nim type system is not aware of that.
## This works in both directions - for linked native object this
## reference points to Nim object.
## This is stored as a raw pointer to avoid reference cycles and
therefore
## improve GC performance.
isRef*: bool
isFinalized: bool
isNative: bool
proc deinit*(obj: NimGodotObject) =
## Destroy the object. You only need to call this for objects not
inherited
## from Reference, where manual lifecycle control is necessary.
assert(not obj.godotObject.isNil)
obj.godotObject.deinit()
obj.godotObject = ni
proc linkedObject(obj: NimGodotObject): NimGodotObject {.inline.} =
cast[NimGodotObject](obj.linkedObjectPtr)
proc nimGodotObjectFinalizer*[T: NimGodotObject](obj: T) =
if obj.godotObject.isNil or obj.isNative: return
# important to set it before so that ``unreference`` is aware
obj.isFinalized = true
if (obj.isRef or not obj.linkedObject.isNil and obj.linkedObject.isRef)
and
obj.godotObject.unreference():
obj.deinit()
Run
Here's my conversion:
type
NimGodotObject* = ref NimGodotObj
NimGodotObj = object of RootObj
## The base type all Godot types inherit from.
## Manages lifecycle of the wrapped ``GodotObject``.
godotObject: ptr GodotObject
linkedObjectPtr: pointer
## Wrapper around native object that is the container of the Nim
"script"
## This is needed for `of` checks and `as` conversions to work as
## expected. For example, Nim type may inherit from ``Spatial``, but
the
## script is attached to ``Particles``. In this case conversion to
## ``Particles`` is valid, but Nim type system is not aware of that.
## This works in both directions - for linked native object this
## reference points to Nim object.
## This is stored as a raw pointer to avoid reference cycles and
therefore
## improve GC performance.
isRef*: bool
isFinalized: bool
isNative: bool
proc `=destroy`*(obj: var NimGodotObj) =
if obj.godotObject.isNil or obj.isNative: return
# important to set it before so that ``unreference`` is aware
obj.isFinalized = true
let linkedGodotObject = cast[NimGodotObject](obj.linkedObjectPtr)
if (obj.isRef or not linkedGodotObject.isNil and linkedGodotObject.isRef)
and obj.godotObject.unreference():
obj.godotObject.deinit()
obj.godotObject = nil
Run
In the code with the finalizer using gc:refc no crash occurs. With the
destructor code and orc, I get a crash around the line where I create the `let
linkedGodotObject` variable. Here's what the top of stacktrace looks like:
CrashHandlerException: Program crashed
Dumping the backtrace. Please include this when reporting the bug on
https://github.com/godotengine/godot/issues
[0] unregisterCycle__rR8fldvW9aUfvKydORzL1RA (C:\nim\lib\system\orc.nim:140)
[1] rememberCycle__LoYD9cYK9aJvrcDizBN64qaQ (C:\nim\lib\system\orc.nim:448)
[2] nimDecRefIsLastCyclicDyn (C:\nim\lib\system\orc.nim:465)
[3] eqdestroy___SAYng9cJoF6y4ebaA9cSvwLg
(C:\godot\gdnim\deps\godot\nim\godotnim.nim:196)
[4] removeGodotObject__t3kHGTvDIkuUnCL9ab87QOAgodotnim
(C:\godot\gdnim\deps\godot\nim\godotnim.nim:197)
[5] nimDestroyFunc__FMVhGprKRYmCSltkbHn4rw
(C:\godot\gdnim\deps\godot\nim\godotmacros.nim:516)
[6] NativeScriptInstance::`scalar deleting destructor'
...
Run
#orc.nim
proc unregisterCycle(s: Cell) =
# swap with the last element. O(1)
let idx = s.rootIdx-1
when false:
if idx >= roots.len or idx < 0:
cprintf("[Bug!] %ld\n", idx)
quit 1
roots.d[idx] = roots.d[roots.len-1] # <-- crash here
roots.d[idx][0].rootIdx = idx+1
dec roots.len
s.rootIdx = 0
Run
So am I doing something bad in the destructor, or is this a problem elsewhere?