Proper inheritance checks and conversion
This commit is contained in:
parent
643c445f87
commit
327cd0e18f
2 changed files with 64 additions and 32 deletions
|
|
@ -41,9 +41,9 @@ proc nimToGodot[T](val: T): Variant =
|
||||||
when compiles(toVariant(val)):
|
when compiles(toVariant(val)):
|
||||||
result = toVariant(val)
|
result = toVariant(val)
|
||||||
else:
|
else:
|
||||||
printError("Failed to convert Nim value of type " & T.name &
|
const err = "Cannot convert Nim value of type " & T.name &
|
||||||
" into Variant")
|
" into Variant"
|
||||||
initNilVariant(result)
|
{.error: err.}
|
||||||
|
|
||||||
template parseError(node: NimNode, msg: string) =
|
template parseError(node: NimNode, msg: string) =
|
||||||
raise newException(ParseError, lineinfo(node) & ": " & msg)
|
raise newException(ParseError, lineinfo(node) & ": " & msg)
|
||||||
|
|
@ -62,9 +62,10 @@ proc extractNames(definition: NimNode):
|
||||||
else:
|
else:
|
||||||
parseError(definition[2], "parent type expected")
|
parseError(definition[2], "parent type expected")
|
||||||
|
|
||||||
proc newRStrLit(s: string): NimNode {.compileTime.} =
|
when not declared(newRStrLit):
|
||||||
result = newNimNode(nnkRStrLit)
|
proc newRStrLit(s: string): NimNode {.compileTime.} =
|
||||||
result.strVal = s
|
result = newNimNode(nnkRStrLit)
|
||||||
|
result.strVal = s
|
||||||
|
|
||||||
proc newCStringLit(s: string): NimNode {.compileTime.} =
|
proc newCStringLit(s: string): NimNode {.compileTime.} =
|
||||||
newNimNode(nnkCallStrLit).add(ident("cstring"), newRStrLit(s))
|
newNimNode(nnkCallStrLit).add(ident("cstring"), newRStrLit(s))
|
||||||
|
|
@ -318,9 +319,10 @@ template registerGodotClass(classNameIdent, classNameLit, isRef,
|
||||||
baseNameLit, createFuncIdent; isTool: bool) =
|
baseNameLit, createFuncIdent; isTool: bool) =
|
||||||
proc createFuncIdent(obj: ptr GodotObject,
|
proc createFuncIdent(obj: ptr GodotObject,
|
||||||
methData: pointer): pointer {.noconv.} =
|
methData: pointer): pointer {.noconv.} =
|
||||||
let nimObj = new(classNameIdent)
|
var nimObj: classNameIdent
|
||||||
nimObj.setOwn()
|
new(nimObj, nimGodotObjectFinalizer[classNameIdent])
|
||||||
nimObj.setGodotObject(obj)
|
nimObj.setGodotObject(obj)
|
||||||
|
nimObj.setNativeObject(asNimGodotObject[NimGodotObject](obj, noRef = true))
|
||||||
GC_ref(nimObj)
|
GC_ref(nimObj)
|
||||||
result = cast[pointer](nimObj)
|
result = cast[pointer](nimObj)
|
||||||
when compiles(nimObj.init()):
|
when compiles(nimObj.init()):
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,17 @@ import godotinternal
|
||||||
type
|
type
|
||||||
NimGodotObject* = ref object of RootObj
|
NimGodotObject* = ref object of RootObj
|
||||||
## The base type all Godot types inherit from.
|
## The base type all Godot types inherit from.
|
||||||
## Manages lifecycle of the wrapped GodotObject.
|
## Manages lifecycle of the wrapped ``GodotObject``.
|
||||||
godotObject: ptr GodotObject
|
godotObject: ptr GodotObject
|
||||||
|
linkedObject: NimGodotObject
|
||||||
|
## 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.
|
||||||
isExternalRef: bool
|
isExternalRef: bool
|
||||||
isOwn: bool
|
|
||||||
|
|
||||||
ConversionResult* {.pure.} = enum
|
ConversionResult* {.pure.} = enum
|
||||||
## Conversion result to return from ``fromVariant`` procedure.
|
## Conversion result to return from ``fromVariant`` procedure.
|
||||||
|
|
@ -133,12 +140,14 @@ proc deinit*(obj: NimGodotObject) =
|
||||||
assert(not obj.godotObject.isNil)
|
assert(not obj.godotObject.isNil)
|
||||||
obj.godotObject.deinit()
|
obj.godotObject.deinit()
|
||||||
obj.godotObject = nil
|
obj.godotObject = nil
|
||||||
|
# linked object internal pointer should be unset from destructor
|
||||||
|
assert(obj.linkedObject.isNil or obj.linkedObject.godotObject.isNil)
|
||||||
|
|
||||||
proc godotFinalizer[T: NimGodotObject](obj: T) =
|
proc nimGodotObjectFinalizer*[T: NimGodotObject](obj: T) =
|
||||||
if obj.godotObject.isNil: return
|
if obj.godotObject.isNil: return
|
||||||
if obj.isExternalRef and obj.godotObject.unreference():
|
if obj.isExternalRef and obj.godotObject.unreference():
|
||||||
obj.deinit()
|
obj.deinit()
|
||||||
elif obj.isOwn:
|
elif not obj.linkedObject.isNil:
|
||||||
obj.deinit()
|
obj.deinit()
|
||||||
|
|
||||||
macro baseNativeType(T: typedesc): cstring =
|
macro baseNativeType(T: typedesc): cstring =
|
||||||
|
|
@ -181,7 +190,7 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
||||||
classRegistry = newTable[cstring, ObjectInfo]()
|
classRegistry = newTable[cstring, ObjectInfo]()
|
||||||
let constructor = proc(): NimGodotObject =
|
let constructor = proc(): NimGodotObject =
|
||||||
var t: T
|
var t: T
|
||||||
new(t, godotFinalizer[T])
|
new(t, nimGodotObjectFinalizer[T])
|
||||||
result = t
|
result = t
|
||||||
|
|
||||||
const base = baseNativeType(T)
|
const base = baseNativeType(T)
|
||||||
|
|
@ -208,7 +217,7 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
||||||
nativeClasses.add(T.name)
|
nativeClasses.add(T.name)
|
||||||
|
|
||||||
proc newNimGodotObject[T: NimGodotObject](
|
proc newNimGodotObject[T: NimGodotObject](
|
||||||
godotObject: ptr GodotObject, godotClassName: cstring): T =
|
godotObject: ptr GodotObject, godotClassName: cstring, noRef: bool): T =
|
||||||
assert(not classRegistry.isNil)
|
assert(not classRegistry.isNil)
|
||||||
assert(not godotObject.isNil)
|
assert(not godotObject.isNil)
|
||||||
let objInfo = classRegistry.getOrDefault(godotClassName)
|
let objInfo = classRegistry.getOrDefault(godotClassName)
|
||||||
|
|
@ -217,11 +226,12 @@ proc newNimGodotObject[T: NimGodotObject](
|
||||||
else:
|
else:
|
||||||
result = T(objInfo.constructor())
|
result = T(objInfo.constructor())
|
||||||
result.godotObject = godotObject
|
result.godotObject = godotObject
|
||||||
if objInfo.isRef:
|
if not noRef and objInfo.isRef:
|
||||||
result.isExternalRef = true
|
result.isExternalRef = true
|
||||||
result.godotObject.reference()
|
result.godotObject.reference()
|
||||||
|
|
||||||
proc asNimGodotObject*[T: NimGodotObject](godotObject: ptr GodotObject): T =
|
proc asNimGodotObject*[T: NimGodotObject](
|
||||||
|
godotObject: ptr GodotObject, noRef: bool = false): T =
|
||||||
## Wraps ``godotObject`` into Nim type ``T``.
|
## Wraps ``godotObject`` into Nim type ``T``.
|
||||||
## This is used by `godotapigen <godotapigen.html>`_ and should rarely be
|
## This is used by `godotapigen <godotapigen.html>`_ and should rarely be
|
||||||
## used by anything else.
|
## used by anything else.
|
||||||
|
|
@ -234,7 +244,7 @@ proc asNimGodotObject*[T: NimGodotObject](godotObject: ptr GodotObject): T =
|
||||||
result = nil
|
result = nil
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
result = newNimGodotObject[T](
|
result = newNimGodotObject[T](
|
||||||
godotObject, cstring(godotObject.getClassName()))
|
godotObject, cstring(godotObject.getClassName()), noRef)
|
||||||
|
|
||||||
proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
|
proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
|
||||||
newVariant(obj.godotObject)
|
newVariant(obj.godotObject)
|
||||||
|
|
@ -258,14 +268,27 @@ proc `as`*[T: NimGodotObject](obj: NimGodotObject, t: typedesc[T]): T =
|
||||||
##
|
##
|
||||||
## This can be used either in dot notation (``node.as(Button)``) or
|
## This can be used either in dot notation (``node.as(Button)``) or
|
||||||
## infix notation (``node as Button``).
|
## infix notation (``node as Button``).
|
||||||
if obj.isNil or not (obj of T):
|
if obj.isNil: return nil
|
||||||
when not defined(release):
|
if obj of T:
|
||||||
if not obj.isNil:
|
|
||||||
printError("Failed to cast object to " &
|
|
||||||
t.name & "\n" & getStackTrace())
|
|
||||||
result = nil
|
|
||||||
else:
|
|
||||||
result = T(obj)
|
result = T(obj)
|
||||||
|
elif not obj.linkedObject.isNil and obj.linkedObject of T:
|
||||||
|
result = T(obj.linkedObject)
|
||||||
|
else:
|
||||||
|
when not defined(release):
|
||||||
|
printError("Failed to cast object to " &
|
||||||
|
T.name & "\n" & getStackTrace())
|
||||||
|
result = nil
|
||||||
|
|
||||||
|
proc `~`*[T: NimGodotObject](obj: NimGodotObject, t: typedesc[T]): bool =
|
||||||
|
## Godot-specific inheritance check. You must use this over Nim's standard
|
||||||
|
## ``of`` operator, because Nim does not know which object your script is
|
||||||
|
## attached to in runtime. For example, if you create a
|
||||||
|
## ``gdobj Bullet of Spatial`` and the ``Bullet`` is attached to a
|
||||||
|
## ``MeshInstance``, ``myBullet of MeshInstance`` will return ``false``, but
|
||||||
|
## ``myBullet ~ MeshInstance`` will return ``true``.
|
||||||
|
## This is going to be changed to ``of`` in later versions, when Nim starts
|
||||||
|
## supporting overloading ``of`` operator.
|
||||||
|
obj of T or obj.linkedObject of T
|
||||||
|
|
||||||
proc newRStrLit(s: string): NimNode {.compileTime.} =
|
proc newRStrLit(s: string): NimNode {.compileTime.} =
|
||||||
result = newNimNode(nnkRStrLit)
|
result = newNimNode(nnkRStrLit)
|
||||||
|
|
@ -378,21 +401,28 @@ proc newConversionError*(err: ConversionResult): ref ValueError =
|
||||||
result = newException(ValueError, msg)
|
result = newException(ValueError, msg)
|
||||||
|
|
||||||
proc setGodotObject*(nimObj: NimGodotObject, obj: ptr GodotObject) {.inline.} =
|
proc setGodotObject*(nimObj: NimGodotObject, obj: ptr GodotObject) {.inline.} =
|
||||||
## Used from Godot constructor. Don't call this.
|
## Used from Godot constructor produced by ``gdobj`` macro. Do not call.
|
||||||
assert(not obj.isNil)
|
assert(not obj.isNil)
|
||||||
assert(nimObj.godotObject.isNil) # reassignment is not allowed
|
assert(nimObj.godotObject.isNil) # reassignment is not allowed
|
||||||
nimObj.godotObject = obj
|
nimObj.godotObject = obj
|
||||||
|
|
||||||
proc setOwn*(nimObj: NimGodotObject) {.inline.} =
|
proc setNativeObject*(nimObj: NimGodotObject,
|
||||||
## Used from Godot constructor. Don't call this.
|
nativeObj: NimGodotObject) {.inline.} =
|
||||||
nimObj.isOwn = true
|
## Used from Godot constructor produced by ``gdobj`` macro. Do not call.
|
||||||
|
nimObj.linkedObject = nativeObj
|
||||||
|
nativeObj.linkedObject = nimObj
|
||||||
|
|
||||||
proc removeGodotObject*(nimObj: NimGodotObject) {.inline.} =
|
proc removeGodotObject*(nimObj: NimGodotObject) {.inline.} =
|
||||||
## Used from Godot destructor. Don't call this.
|
## Used from Godot destructor produced by ``gdobj`` macro. Do not call.
|
||||||
nimObj.godotObject = nil
|
nimObj.godotObject = nil
|
||||||
|
nimObj.linkedObject.godotObject = nil
|
||||||
|
|
||||||
|
proc `==`*(self, other: NimGodotObject): bool {.inline.} =
|
||||||
|
## Compares objects by referential equality.
|
||||||
|
self.godotObject == other.godotObject
|
||||||
|
|
||||||
proc godotObject*(nimObj: NimGodotObject): ptr GodotObject {.inline.} =
|
proc godotObject*(nimObj: NimGodotObject): ptr GodotObject {.inline.} =
|
||||||
## Returns internal poitner to ``GodotObject``. Use only if you know what
|
## Returns raw poitner to ``GodotObject``. Use only if you know what
|
||||||
## you are doing.
|
## you are doing.
|
||||||
nimObj.godotObject
|
nimObj.godotObject
|
||||||
|
|
||||||
|
|
@ -719,7 +749,7 @@ N_NOINLINE(void, setStackBottom)(void* thestackbottom);
|
||||||
var nativeLibHandle: pointer
|
var nativeLibHandle: pointer
|
||||||
proc getNativeLibHandle*(): pointer =
|
proc getNativeLibHandle*(): pointer =
|
||||||
## Returns NativeScript library handle used to register type information
|
## Returns NativeScript library handle used to register type information
|
||||||
## in Godot. Use only if you know what you are doing.
|
## in Godot.
|
||||||
return nativeLibHandle
|
return nativeLibHandle
|
||||||
|
|
||||||
proc godot_nativescript_init(handle: pointer) {.
|
proc godot_nativescript_init(handle: pointer) {.
|
||||||
|
|
@ -746,7 +776,7 @@ const nimGcStepLengthUs {.intdefine.} = 2000
|
||||||
proc godot_nativescript_frame() {.cdecl, exportc, dynlib.} =
|
proc godot_nativescript_frame() {.cdecl, exportc, dynlib.} =
|
||||||
if asyncdispatch.hasPendingOperations():
|
if asyncdispatch.hasPendingOperations():
|
||||||
poll(0)
|
poll(0)
|
||||||
GC_step(nimGcStepLengthUs, false, 0)
|
GC_step(nimGcStepLengthUs, true, 0)
|
||||||
|
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
onUnhandledException = proc(errorMsg: string) =
|
onUnhandledException = proc(errorMsg: string) =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue