From 863df0ae454b8614c2145474cca717a5301a871d Mon Sep 17 00:00:00 2001 From: Ruslan Mustakov Date: Wed, 26 Jul 2017 22:50:10 +0700 Subject: [PATCH] Proper handling of References --- godot/nim/godotmacros.nim | 43 ++++++++++++++++++++++++++++++++------- godot/nim/godotnim.nim | 20 +++++++++++++----- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/godot/nim/godotmacros.nim b/godot/nim/godotmacros.nim index 9ae1057..4ab140c 100644 --- a/godot/nim/godotmacros.nim +++ b/godot/nim/godotmacros.nim @@ -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) diff --git a/godot/nim/godotnim.nim b/godot/nim/godotnim.nim index a13be85..a36ee91 100644 --- a/godot/nim/godotnim.nim +++ b/godot/nim/godotnim.nim @@ -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