Proper handling of References

This commit is contained in:
Ruslan Mustakov 2017-07-26 22:50:10 +07:00
commit 863df0ae45
2 changed files with 51 additions and 12 deletions

View file

@ -264,16 +264,29 @@ proc rangeError(nimType: cstring, value: string, className: cstring,
proc nimDestroyFunc(obj: ptr GodotObject, methData: pointer,
userData: pointer) {.noconv.} =
let nimObj = cast[ref NimGodotObject](userData)
let nimObj = cast[NimGodotObject](userData)
nimObj.removeGodotObject()
GC_unref(nimObj)
template registerGodotClass(classNameIdent, classNameLit,
proc nimDestroyRefFunc(obj: ptr GodotObject, methData: pointer,
userData: pointer) {.noconv.} =
# references are destroyed by Godot when they are already destroyed by Nim,
# so nothing to do here.
discard
proc refcountIncremented*(obj: NimGodotObject) =
GC_ref(obj)
proc refcountDecremented*(obj: NimGodotObject): bool =
GC_unref(obj)
template registerGodotClass(classNameIdent, classNameLit, isRef,
baseNameLit, createFuncIdent) =
proc createFuncIdent(obj: ptr GodotObject,
methData: pointer): pointer {.noconv.} =
let nimObj = new(classNameIdent)
setGodotObject(nimObj, obj)
nimObj.setOwn()
nimObj.setGodotObject(obj)
GC_ref(nimObj)
result = cast[pointer](nimObj)
@ -281,7 +294,7 @@ template registerGodotClass(classNameIdent, classNameLit,
createFunc: createFuncIdent
)
let destroyFuncObj = GodotInstanceDestroyFunc(
destroyFunc: nimDestroyFunc
destroyFunc: when isRef: nimDestroyRefFunc else: nimDestroyFunc
)
registerClass(classNameIdent, classNameLit, false)
godotScriptRegisterClass(getNativeLibHandle(), classNameLit, baseNameLit,
@ -328,7 +341,7 @@ template registerGodotField(classNameLit, classNameIdent, propNameLit,
godotScriptRegisterProperty(getNativeLibHandle(), classNameLit, propNameLit,
attr, setFunc, getFunc)
static:
import strutils
import strutils, sets
proc toGodotStyle(s: string): string {.compileTime.} =
result = newStringOfCap(s.len + 10)
for c in s:
@ -385,12 +398,14 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
result.add(meth.nimNode)
# 4. Register Godot object
let parentName = if obj.parentName.isNil: newNilLit()
let parentName = if obj.parentName.isNil: newStrLitNode("Object")
else: newStrLitNode(obj.parentName)
let classNameLit = newCStringLit(obj.name)
let classNameIdent = ident(obj.name)
let isRef: bool = if obj.parentName.isNil: false
else: obj.parentName in refClasses
result.add(getAst(
registerGodotClass(classNameIdent, classNameLit, parentName,
registerGodotClass(classNameIdent, classNameLit, isRef, parentName,
genSym(nskProc, "createFunc"))))
# 5. Register fields (properties)
@ -467,6 +482,20 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
argTypes, genSym(nskProc, "methFunc"),
hasReturnValue)))
if isRef:
# add ref/unref for types inherited from Reference
let noArgs = newSeq[NimNode]()
result.add(getAst(
registerGodotMethod(classNameLit, classNameIdent, ident("refcountIncremented"),
cstring"_refcount_incremented", 0, 0,
noArgs, genSym(nskProc, "refcount_incremented"),
ident("false"))))
result.add(getAst(
registerGodotMethod(classNameLit, classNameIdent, ident("refcountDecremented"),
cstring"_refcount_decremented", 0, 0,
noArgs, genSym(nskProc, "refcount_decremented"),
ident("true"))))
macro gdobj*(definition: untyped, body: untyped): typed =
let typeDef = parseType(definition, callsite())
result = genType(typeDef)

View file

@ -14,7 +14,8 @@ import "../core/strings.nim", "../core/vector2.nim", "../core/rect2.nim",
type
NimGodotObject* = ref object of RootObj
godotObject: ptr GodotObject
isRef: bool
isExternalRef: bool
isOwn: bool
ConversionResult* {.pure.} = enum
OK,
@ -51,7 +52,8 @@ var classRegistry {.threadvar.}: TableRef[cstring, ObjectInfo]
static:
import sets, strutils
var nativeClasses {.compileTime.} = initSet[string]()
var nativeClasses {.compileTime.} = newSeq[string]()
var refClasses* {.compileTime.} = newSeq[string]()
var getClassMethodBind {.threadvar.}: ptr GodotMethodBind
proc getClassName*(o: ptr GodotObject): string =
@ -87,7 +89,9 @@ proc deinit*(obj: NimGodotObject) =
proc godotFinalizer[T: NimGodotObject](obj: T) =
if obj.godotObject.isNil: return
if obj.isRef and obj.godotObject.unreference():
if obj.isExternalRef and obj.godotObject.unreference():
obj.deinit()
elif obj.isOwn:
obj.deinit()
macro baseNativeType(T: typedesc): cstring =
@ -139,9 +143,12 @@ template registerClass*(T: typedesc; godotClassName: cstring,
isNative: isNativeParam,
isRef: isRef
)
when isRef:
static:
refClasses.add(T.name)
when isNativeParam:
static:
nativeClasses.incl(T.name)
nativeClasses.add(T.name)
proc newNimGodotObject[T: NimGodotObject](
godotObject: ptr GodotObject, godotClassName: cstring): T =
@ -154,7 +161,7 @@ proc newNimGodotObject[T: NimGodotObject](
result = T(objInfo.constructor())
result.godotObject = godotObject
if objInfo.isRef:
result.isRef = true
result.isExternalRef = true
result.godotObject.reference()
proc asNimGodotObject*[T: NimGodotObject](godotObject: ptr GodotObject): T =
@ -252,6 +259,9 @@ proc setGodotObject*(nimObj: NimGodotObject, obj: ptr GodotObject) {.inline.} =
assert(nimObj.godotObject.isNil) # reassignment is not allowed
nimObj.godotObject = obj
proc setOwn*(nimObj: NimGodotObject) {.inline.} =
nimObj.isOwn = true
proc godotObject*(nimObj: NimGodotObject): ptr GodotObject {.inline.} =
nimObj.godotObject