├── LICENSE ├── README.md ├── bindgen ├── api.nim ├── bindgen.nim ├── filters │ ├── files │ │ ├── api.nimf │ │ ├── api │ │ │ ├── build_config.nimf │ │ │ └── header.nimf │ │ ├── builtins │ │ │ ├── all_types.nimf │ │ │ ├── procs.nimf │ │ │ └── type.nimf │ │ ├── classes │ │ │ ├── procs.nimf │ │ │ └── type.nimf │ │ ├── enums.nimf │ │ ├── native_structs.nimf │ │ └── utility.nimf │ └── utils │ │ └── conditional.nimf ├── gdtype.nim └── helpers.nim ├── contrib ├── extension_api.json ├── gdextension_interface.h └── gdextension_interface.nim ├── examples └── register_custom_type.nim ├── nimrodot.nim ├── nimrodot.nimble └── nimrodot ├── builtins ├── types │ ├── typedarray.nim │ └── variant.nim └── variant.nim ├── classdb.nim ├── classes └── internal │ └── instance_funcs.nim ├── ffi.nim ├── gdffi.nim ├── hooks.nim ├── interface_ptrs.nim ├── internal ├── bindwrapper.nim └── typeinfo.nim ├── ref_helper.nim └── utils.nim /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 chmod222 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GDExtension Nim Bindings 2 | This project is a work in progress implementation / proof of concept of the 3 | Godot 4 GDExtension API. 4 | 5 | Be aware that this is not yet usable for actual development unless you are prepared to live with 6 | a very much moving target until all the pieces have fallen into place. 7 | 8 | Building it requires Nim 2.0 RC because by the time this project is far along enough to be used, 9 | Nim 2.0 should be released. 10 | 11 | ## Status: 12 | - [x] Library initialization / deinitialization hooks 13 | 14 | - Bindings of all builtin classes (`Variant`, `Vector2`, ...) 15 | - [x] Construction, deconstruction 16 | - [x] Methods 17 | - [x] Properties 18 | - [x] Index (keyed and positional) 19 | - [x] Fixed-arity and variadic calls 20 | 21 | - Bindings of all utility functions 22 | - [x] Fixed-arity and variadic calls 23 | 24 | - Godot classes: 25 | - [x] Basic usage 26 | - [x] Destruction 27 | - [x] Calling methods 28 | - Works in principle, but further testing required to make sure every case works. 29 | 30 | - Registering custom classes: 31 | - [x] Construction, Destruction hooks 32 | - Dynamic Properties (`.get`, `.set` in Godot) 33 | - [x] Get/Set 34 | - [x] Query revertible status and revert value 35 | - [x] Query property List 36 | - Methods 37 | - [ ] Virtual 38 | - Fundamentals implemented, missing usability 39 | - [x] Static 40 | - [x] Instance 41 | - [x] Bindcall 42 | - [x] ptrcall 43 | - [x] Variadic 44 | - [x] Builtin Properties (`.property_name` in Godot) 45 | - Lower level exposure implemented, higher level wrapper missing 46 | - [X] Signals 47 | - Should work, still needs testing 48 | 49 | ## What somewhat works: 50 | - TypedArray[T] does not enforce the `T` or `Typed` part so far, but with some self discipline it 51 | will work until compile time enforcements are implemented. 52 | 53 | - Memory management 54 | - `RefCounted` (and it's associated Ref[T] wrapper) and manually managed objects *should* work, 55 | but need testing to iron out any lurking issues. Objects not deriving from `RefCounted` need 56 | some discipline to work with for now, until a nicer abstraction over them makes life easier. 57 | 58 | ## What does not work: 59 | - Most likely some other things that can not be tested as of yet. The GDExtension interface is 60 | very sparsely documented for now and surprises still lurk in some corners where assumption and 61 | reality drift apart. 62 | 63 | ## How it works: 64 | 65 | Two step auto-generation from the included "contrib/extension_api.json" 66 | 67 | nimble generateApi 68 | 69 | This is also called automatically in the pre-install step. 70 | 71 | This generates the following modules: 72 | 73 | - `nimrodot/api`: Very high level definitions. 74 | - `nimrodot/enums`: Global enumerations and bitfields. 75 | - `nimrodot/utility_functions`: Utility functions. 76 | - `nimrodot/builtins/types/*` (except `variant`): Builtin class type 77 | - `nimrodot/builtins/*` (except `variant`): Builtin class procs 78 | - `nimrodot/classes/types/*`: Godot Classes 79 | - `nimrodot/classes/*`: Godot Classes 80 | 81 | Most methods are stubbed with various Macros that implement the actual glue 82 | on end-compile, i.e. 83 | 84 | ```nim 85 | proc lerp*(self: Vector2; to: Vector2; weight: float64): Vector2 86 | {.gd_builtin_method(Vector2, 4250033116).} 87 | ``` 88 | 89 | These do the job of caching the various function pointers and converting the arguments and are implemented in the `nimrodot/gdffi` module. 90 | 91 | ## Usage Example (Proof of Concept) 92 | 93 | (A longer and more useful example is contained in the [examples](/examples) folder. 94 | 95 | ```nim 96 | # If not specified, entry point defaults to "gdext_init" 97 | godotHooks(GDEXTENSION_INITIALIZATION_SCENE): 98 | # Called for every initialization level 99 | initialize(level): 100 | if level == GDEXTENSION_INITIALIZATION_SCENE: 101 | echo "Hello World from Godot" 102 | 103 | # Dumping some random information for now 104 | var os: OS = getSingleton[OS]("OS") 105 | 106 | echo "Processor Name: ", os.get_processor_name() 107 | 108 | let fonts = os.get_system_fonts().newVariant() 109 | 110 | var dir = DirAccess.open("res://".newString()) 111 | var files = dir.get_files().newVariant() 112 | 113 | echo fonts 114 | echo files 115 | 116 | # Called for every initialization level in reverse order 117 | deinitialize(level): 118 | echo "Bye World from Godot!" 119 | ``` 120 | -------------------------------------------------------------------------------- /bindgen/api.nim: -------------------------------------------------------------------------------- 1 | import std/[sets, options, tables, json] 2 | 3 | type 4 | Header* = object 5 | version_major*: int 6 | version_minor*: int 7 | version_patch*: int 8 | version_status*: string 9 | version_build*: string 10 | version_full_name*: string 11 | 12 | MemberOffset* = object 13 | member*: string 14 | meta*: string 15 | offset*: int 16 | 17 | ClassSize* = object 18 | name*: string 19 | size*: int 20 | 21 | ClassOffsets* = object 22 | name*: string 23 | members*: seq[MemberOffset] 24 | 25 | BuiltinClassSizes* = object 26 | build_configuration*: string 27 | sizes*: seq[ClassSize] 28 | 29 | BuiltinClassMemberOffsets* = object 30 | build_configuration*: string 31 | classes*: seq[ClassOffsets] 32 | 33 | EnumDefiniton* = object 34 | name*: string 35 | is_bitfield*: Option[bool] 36 | values*: seq[EnumValue] 37 | 38 | EnumValue* = object 39 | name*: string 40 | value*: int 41 | 42 | FunctionArgument* = object 43 | name*: string 44 | `type`*: string 45 | meta*: Option[string] 46 | default_value*: Option[string] 47 | 48 | FunctionDefinition* = object 49 | name*: string 50 | return_type*: Option[string] 51 | category*: string 52 | is_vararg*: bool 53 | hash*: uint 54 | arguments*: Option[seq[FunctionArgument]] 55 | 56 | ConstructorDefinition* = object 57 | index*: int 58 | arguments*: Option[seq[FunctionArgument]] 59 | 60 | OperatorDefinition* = object 61 | name*: string 62 | right_type*: Option[string] 63 | return_type*: string 64 | 65 | MethodDefinition* = object 66 | name*: string 67 | 68 | is_vararg*: bool 69 | is_const*: bool 70 | is_static*: bool 71 | 72 | return_type*: Option[string] 73 | hash*: uint 74 | arguments*: Option[seq[FunctionArgument]] 75 | 76 | ClassMethodReturn* = object 77 | `type`*: string 78 | meta*: Option[string] 79 | 80 | #ClassMethodArgument* = object 81 | # name*: string 82 | # `type`*: string 83 | # meta*: Option[string] 84 | # default_value*: Option[string] 85 | 86 | ClassMethodDefinition* = object 87 | name*: string 88 | 89 | is_const*: bool 90 | is_vararg*: bool 91 | is_static*: bool 92 | is_virtual*: bool 93 | 94 | hash*: Option[uint] 95 | 96 | return_value*: Option[ClassMethodReturn] 97 | arguments*: Option[seq[FunctionArgument]] 98 | 99 | PropertyDefinition* = object 100 | name*: string 101 | `type`*: string 102 | 103 | ConstantDefinition* = object 104 | name*: string 105 | `type`*: string 106 | value*: string 107 | 108 | BuiltinClassDefinition* = object 109 | name*: string 110 | is_keyed*: bool 111 | indexing_return_type*: Option[string] 112 | has_destructor*: bool 113 | 114 | constructors*: seq[ConstructorDefinition] 115 | operators*: seq[OperatorDefinition] 116 | 117 | members*: Option[seq[PropertyDefinition]] 118 | methods*: Option[seq[MethodDefinition]] 119 | enums*: Option[seq[EnumDefiniton]] 120 | constants*: Option[seq[ConstantDefinition]] 121 | 122 | ClassConstant* = object 123 | name*: string 124 | value*: int 125 | 126 | ClassPropertyDefinition* = object 127 | `type`*: string 128 | name*: string 129 | setter*: Option[string] 130 | getter*: string 131 | 132 | SignalDefinition* = object 133 | name*: string 134 | arguments*: Option[seq[FunctionArgument]] 135 | 136 | ClassDefinition* = object 137 | name*: string 138 | is_refcounted*: bool 139 | is_instantiable*: bool 140 | inherits*: Option[string] 141 | api_type*: string 142 | 143 | constants*: Option[seq[ClassConstant]] 144 | enums*: Option[seq[EnumDefiniton]] 145 | methods*: Option[seq[ClassMethodDefinition]] 146 | properties*: Option[seq[ClassPropertyDefinition]] 147 | signals*: Option[seq[SignalDefinition]] 148 | 149 | NativeStructure* = object 150 | name*: string 151 | format*: string 152 | 153 | Singleton* = object 154 | name*: string 155 | `type`*: string 156 | 157 | ApiDump* = object 158 | header*: Header 159 | 160 | builtin_classes*: seq[BuiltinClassDefinition] 161 | builtin_class_sizes*: seq[BuiltinClassSizes] 162 | builtin_class_member_offsets*: seq[BuiltinClassMemberOffsets] 163 | 164 | global_enums*: seq[EnumDefiniton] 165 | 166 | classes*: seq[ClassDefinition] 167 | singletons*: seq[Singleton] 168 | 169 | utility_functions*: seq[FunctionDefinition] 170 | native_structures*: seq[NativeStructure] 171 | 172 | Api* = ref object 173 | inner: ApiDump 174 | 175 | nativeStructTypes*: HashSet[string] 176 | builtinClassTypes*: HashSet[string] 177 | classTypes*: Table[string, ClassDefinition] 178 | 179 | typeDeps*: Table[string, OrderedSet[string]] 180 | 181 | proc header*(api: Api): Header = api.inner.header 182 | proc builtin_classes*(api: Api): seq[BuiltinClassDefinition] = api.inner.builtin_classes 183 | proc builtin_class_sizes*(api: Api): seq[BuiltinClassSizes] = api.inner.builtin_class_sizes 184 | proc builtin_class_member_offsets*(api: Api): seq[BuiltinClassMemberOffsets] = api.inner.builtin_class_member_offsets 185 | proc global_enums*(api: Api): seq[EnumDefiniton] = api.inner.global_enums 186 | proc classes*(api: Api): seq[ClassDefinition] = api.inner.classes 187 | proc singletons*(api: Api): seq[Singleton] = api.inner.singletons 188 | proc utility_functions*(api: Api): seq[FunctionDefinition] = api.inner.utility_functions 189 | proc native_structures*(api: Api): seq[NativeStructure] = api.inner.native_structures 190 | 191 | proc fill_caches(api: var Api) = 192 | api.typeDeps = initTable[string, OrderedSet[string]]() 193 | 194 | for native in api.native_structures: 195 | api.nativeStructTypes.incl native.name 196 | 197 | for builtin in api.builtin_classes: 198 | api.builtinClassTypes.incl builtin.name 199 | 200 | api.builtinClassTypes.excl "bool" 201 | api.builtinClassTypes.excl "int" 202 | api.builtinClassTypes.excl "float" 203 | 204 | api.builtinClassTypes.incl "Variant" 205 | api.builtinClassTypes.incl "TypedArray" 206 | 207 | for class in api.classes: 208 | api.classTypes[class.name] = class 209 | 210 | proc importApi*(path: string): Api = 211 | let dump = parseJson(readFile(path)).to(ApiDump) 212 | 213 | result = Api(inner: dump) 214 | result.fill_caches() 215 | -------------------------------------------------------------------------------- /bindgen/bindgen.nim: -------------------------------------------------------------------------------- 1 | import std/[os, options, sets] 2 | 3 | import ./api 4 | import ./helpers 5 | 6 | const sourceApiFile = "contrib/extension_api.json" 7 | 8 | import filters/files/"api.nimf" as apiFileGen 9 | import filters/files/"utility.nimf" as utilityFileGen 10 | import filters/files/"native_structs.nimf" as nativeStructsFileGen 11 | import filters/files/"enums.nimf" as enumsFileGen 12 | import filters/files/builtins/"all_types.nimf" as builtinAllTypesGen 13 | import filters/files/builtins/"type.nimf" as builtinTypeGen 14 | import filters/files/builtins/"procs.nimf" as builtinProcGen 15 | import filters/files/classes/"type.nimf" as classTypeGen 16 | import filters/files/classes/"procs.nimf" as classProcGen 17 | 18 | when nimvm: 19 | func projectPath(): string = "./bindgen/bindgen.nim" 20 | 21 | proc rmFile(path: string) = 22 | path.removeFile() 23 | 24 | proc cpFile(source, dest: string) = 25 | source.copyFile(dest) 26 | 27 | proc mkDir(dirs: string) = 28 | dirs.createDir() 29 | 30 | else: 31 | discard 32 | 33 | when isMainModule: 34 | echo projectPath() 35 | 36 | let projectRoot = projectPath() 37 | .parentDir() 38 | .parentDir() 39 | 40 | let sourceRoot = projectRoot / "nimrodot" 41 | let apiFile = sourceApiFile.importApi() 42 | 43 | cpFile(projectRoot / "contrib/gdextension_interface.nim", sourceRoot / "ffi.nim") 44 | 45 | helpers.apiDef = apiFile 46 | 47 | writeFile(sourceRoot / "api.nim", apiFileGen.generate()) 48 | writeFile(sourceRoot / "utility_functions.nim", utilityFileGen.generate()) 49 | writeFile(sourceRoot / "enums.nim", enumsFileGen.generate()) 50 | writeFile(sourceRoot / "native_structs.nim", nativeStructsFileGen.generate()) 51 | 52 | mkdir sourceRoot / "builtins" / "types" 53 | mkdir sourceRoot / "classes" / "types" 54 | 55 | writeFile( 56 | sourceRoot / "builtins" / "types.nim", 57 | builtinAllTypesGen.generate()) 58 | 59 | for builtinClass in apiFile.builtin_classes: 60 | if not builtinClass.isNativeClass(): 61 | writeFile( 62 | sourceRoot / "builtins" / "types" / builtinClass.moduleName() & ".nim", 63 | builtinTypeGen.generate(builtinClass)) 64 | 65 | writeFile( 66 | sourceRoot / "builtins" / builtinClass.moduleName() & ".nim", 67 | builtinProcGen.generate(builtinClass)) 68 | 69 | # We want to generate the classes in the correct topological order as 70 | # determined by inheritance, starting at "Object" 71 | var remainingClasses = apiFile.classes 72 | 73 | var sortedClasses = newSeq[ClassDefinition]() 74 | var openParentClasses = newSeq[ClassDefinition]() 75 | var resolved = initHashSet[string]() 76 | 77 | # Need object first 78 | for cls in apiFile.classes: 79 | if cls.inherits.isNone: 80 | openParentClasses &= cls 81 | resolved.incl cls.name 82 | 83 | break 84 | 85 | while len(openParentClasses) > 0: 86 | sortedClasses &= openParentClasses.pop() 87 | 88 | let parentName = sortedClasses[^1].name 89 | 90 | for cls in remainingClasses: 91 | if cls.name notin resolved and 92 | cls.inherits.isSome() and 93 | cls.inherits.unsafeGet() == parentName: 94 | 95 | resolved.incl cls.name 96 | openParentClasses &= cls 97 | 98 | for class in sortedClasses: 99 | writeFile( 100 | sourceRoot / "classes" / "types" / class.moduleName() & ".nim", 101 | classTypeGen.generate(class)) 102 | 103 | writeFile( 104 | sourceRoot / "classes" / class.moduleName() & ".nim", 105 | classProcGen.generate(class)) -------------------------------------------------------------------------------- /bindgen/filters/files/api.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import ./api/"header.nimf" 4 | #import ./api/"build_config.nimf" 5 | # 6 | #import ../../helpers 7 | # 8 | #proc generate*(): string = 9 | # result = autogenDisclaimer() 10 | # result &= generateHeader() 11 | # result &= generateBuildConfig() -------------------------------------------------------------------------------- /bindgen/filters/files/api/build_config.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/[tables, sequtils, strutils] 4 | # 5 | #import ../../../helpers 6 | #import ../../utils/"conditional.nimf" 7 | # 8 | #proc generateBuildConfig*(): string = 9 | # result = "# Compile with single precision floats, unless defined otherwise.\n" 10 | const floatPrecision = when defined(nimrodotDoublePrecision): 11 | "double" 12 | else: 13 | "float" 14 | 15 | # result &= "# Detect CPU architecture\n" 16 | const pointerWidth = 17 | when sizeOf(pointer) == 4: 32 18 | elif sizeOf(pointer) == 8: 64 19 | else: {.error: "Unsupported CPU architecture".} 20 | 21 | const buildConfig* = floatPrecision & "_" & $$pointerWidth 22 | 23 | # result &= generateConditional("Variant".sizeGroups(), proc(size: int): string = 24 | # "const variantSize* = " & $size) & "\n\n" 25 | # 26 | # result &= generateConditional("Object".sizeGroups(), proc(size: int): string = 27 | # "const objectSize* = " & $size) & "\n" -------------------------------------------------------------------------------- /bindgen/filters/files/api/header.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import ../../../api 4 | #import ../../../helpers 5 | # 6 | #proc generateHeader*(): string = 7 | # let apiHeader = apiDef.header 8 | # 9 | # result = "" 10 | const 11 | versionMajor* = $apiHeader.version_major 12 | versionMinor* = $apiHeader.version_minor 13 | versionPatch* = $apiHeader.version_patch 14 | versionStatus* = "$apiHeader.version_status" 15 | versionBuild* = "$apiHeader.version_build" 16 | versionFullName* = "$apiHeader.version_full_name" 17 | 18 | -------------------------------------------------------------------------------- /bindgen/filters/files/builtins/all_types.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import ../../../api 4 | #import ../../../helpers 5 | # 6 | #proc generate*(): string = 7 | # result = autogenDisclaimer() 8 | # 9 | import ./types/[ 10 | # for i, builtin in apiDef.builtin_classes: 11 | # if builtin.isNativeClass(): 12 | # continue 13 | # end if 14 | # 15 | # let moduleName = builtin.moduleName().safeImport() 16 | # let comma = if i < len(apiDef.builtin_classes) - 1: "," else: "" 17 | $moduleName$comma 18 | # end for 19 | ] 20 | 21 | # for builtin in apiDef.builtin_classes: 22 | # if builtin.isNativeClass(): 23 | # continue 24 | # end if 25 | # 26 | # let moduleName = builtin.moduleName().safeIdent() 27 | export $moduleName 28 | # end for -------------------------------------------------------------------------------- /bindgen/filters/files/builtins/procs.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/options 4 | # 5 | #import ../../../api 6 | #import ../../../helpers 7 | # 8 | #proc generate*(def: BuiltinClassDefinition): string = 9 | # let selfType = def.fromSelf().render() 10 | # let selfModule = def.moduleName().safeImport() 11 | # let selfModuleExport = def.moduleName().safeIdent() 12 | # 13 | # result = autogenDisclaimer() 14 | # result &= "# Builtin Class " & def.name & " (Procs)\n" 15 | import ./types/$selfModule 16 | export $selfModuleExport 17 | 18 | import ../gdffi 19 | 20 | # result &= def.renderImportList( 21 | # {roProperties, roMethods, roStructFields, roConstructors, roIndexes, roOperators}, 22 | # "nimrodot/builtins") 23 | # 24 | # if def.constants.isSome: 25 | # result &= "# Constants\n" 26 | # 27 | # for constant in def.constants.unsafeGet(): 28 | # let constName = constant.name.safeIdent() 29 | # let constTy = constant.fromConst(def).render() 30 | # let constVal = constant.value 31 | # 32 | # if constant.isSimpleType(): 33 | const $constName*: $constTy = $constVal 34 | # else: 35 | let $constName*: $constTy = gd_constant[$constTy, $def.name]("$constName") # $constVal 36 | # end if 37 | # end for 38 | 39 | # end if 40 | # result &= "# Constructors\n" 41 | # for ctor in def.constructors: 42 | # let ctorPrototype = ctor.render(def) 43 | $ctorPrototype 44 | {.gd_builtin_ctor($def.name, $ctor.index).} 45 | 46 | # end for 47 | # if def.members.isSome: 48 | # result &= "# Properties\n" 49 | # 50 | # for prop in def.members.unsafeGet(): 51 | # let safeProp = prop.name.safeIdent() 52 | # let propType = prop.fromProperty(def).render() 53 | # 54 | # # prop.name below is intentional, as it is already quoted in the source. 55 | proc $safeProp*(self: $selfType): $propType {.gd_builtin_get($def.name).} 56 | proc `$prop.name=`*(self: var $selfType; val: $propType) {.gd_builtin_set($def.name).} 57 | 58 | # end for 59 | # end if 60 | # if def.methods.isSome: 61 | # result &= "# Methods\n" 62 | # 63 | # for meth in def.methods.unsafeGet(): 64 | # let methPrototype = meth.render(def) 65 | $methPrototype 66 | {.gd_builtin_method($def.name, $meth.hash).} 67 | 68 | # end for 69 | # end if 70 | # if def.indexing_return_type.isSome: 71 | # let indexRetType = def.indexing_return_type.unsafeGet().fromVarious(def).render() 72 | # 73 | # result &= "# Indexers\n" 74 | # 75 | # let indexType = (if def.is_keyed: "Variant" else: "int64").fromVarious(def).render() 76 | # 77 | proc `[]`*(self: $selfType; idx: $indexType): $indexRetType 78 | {.gd_builtin_index_get($def.name).} 79 | 80 | # if indexType != indexRetType: 81 | proc `[]=`*(self: var $selfType; idx: $indexType; val: $indexRetType) 82 | {.gd_builtin_index_set($def.name).} 83 | # else: 84 | proc `[]=`*(self: var $selfType; idx, val: $indexRetType) 85 | {.gd_builtin_index_set($def.name).} 86 | # end if 87 | 88 | # end if 89 | # if len(def.operators) > 0: 90 | # result &= "# Operators\n" 91 | # 92 | # for operator in def.operators: 93 | # let operProto = operator.render(selfType) 94 | $operProto {.gd_builtin_operator($def.name).} 95 | # end for 96 | # end if -------------------------------------------------------------------------------- /bindgen/filters/files/builtins/type.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/[tables, options, algorithm, sequtils, strutils, sets] 4 | # 5 | #import ../../../api 6 | #import ../../../helpers 7 | #import ../../utils/"conditional.nimf" 8 | # 9 | #proc generate*( 10 | # def: BuiltinClassDefinition): string = 11 | # let memberGroups = def.memberGroups() 12 | # let sizeGroups = def.sizeGroups() 13 | # let needsApiImport = not def.isPointerType() and (if def.isOpaque(): len(sizeGroups) > 1 else: len(memberGroups) > 1) 14 | # 15 | # result = autogenDisclaimer() 16 | # result &= "# Builtin Class " & def.name & "\n" 17 | # 18 | import ../../ffi 19 | # if needsApiImport: 20 | import ../../api 21 | 22 | # else: 23 | 24 | # end if 25 | # 26 | # result &= def.renderImportList({roStructFields}, "nimrodot/builtins/types") 27 | # 28 | # # if not def.isOpaque(apiDef): 29 | # # for members, cfg in memberGroups: 30 | # # let imports = generateImportList(members) 31 | # # if len(imports) > 0: 32 | # ## $imports 33 | # # end if 34 | ## 35 | # # break # We only need one iteration to determine all dependencies 36 | # # end for 37 | # #end if 38 | type 39 | # if def.isPointerType(): 40 | $def.name* = distinct pointer 41 | # else: 42 | $def.name* = object 43 | # if def.isOpaque(): 44 | # result &= generateConditional(sizeGroups, proc(size: int): string = 45 | # "opaque: array[" & $size & ", byte]\n").indent(2) 46 | # else: 47 | # let members = def.memberGroups() 48 | # 49 | # result &= generateConditional(members, proc(members: seq[MemberOffset]): string = 50 | # for member in members: 51 | # result &= member.member & "*: " & member.fromField(def).render() & "\n").indent(2) 52 | # end for 53 | # end if 54 | # end if 55 | # if def.enums.isSome: 56 | 57 | # result &= "# Enums\n" 58 | type 59 | # for enu in def.enums.unsafeGet(): 60 | # let enumName = enu.name 61 | # 62 | # # associated enums don't have duplicate values for now, so we can 63 | # # keep it simple. They do share names across modules so we do need 64 | # # to declare them as pure however. 65 | $enumName* {.size: sizeof(uint64).} = enum 66 | # for field in enu.values.sortedByIt(it.value): 67 | # let cleanName = field.cleanName(enu) 68 | $cleanName = $field.value 69 | # end for 70 | 71 | # end for 72 | # else: 73 | 74 | # end if 75 | func variantTypeId*(_: typedesc[$def.name]): GDExtensionVariantType = 76 | cast[GDExtensionVariantType]($def.typeId) 77 | 78 | # if def.has_destructor: 79 | import ../../hooks 80 | 81 | generateBuiltinHooks($def.name, $def.copyCtorIdx) 82 | # end if -------------------------------------------------------------------------------- /bindgen/filters/files/classes/procs.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/[options, sets] 4 | # 5 | #import ../../../api 6 | #import ../../../helpers 7 | # 8 | #proc generate*( 9 | # def: ClassDefinition): string = 10 | # let selfModule = def.moduleName().safeImport() 11 | # let selfModuleExport = def.moduleName().safeIdent() 12 | # let selfType = def.name 13 | # 14 | # result = autogenDisclaimer() 15 | # result &= "# Builtin Class " & def.name & " (Procs)\n" 16 | import ./types/$selfModule 17 | export $selfModuleExport 18 | # if def.inherits.isSome(): 19 | # let parentName = def.inherits.unsafeGet() 20 | # let parentModuleName = parentName.moduleName().safeImport() 21 | # let parentModuleNameExport = parentName.moduleName().safeIdent() 22 | 23 | import ../classes/$parentModuleName 24 | export $parentModuleNameExport 25 | # end if 26 | # if def.name == "Thread": 27 | 28 | type Thread = thread.Thread 29 | # end if 30 | 31 | # result &= def.renderImportList( 32 | # {roProperties, roMethods, roStructFields, roConstructors, roIndexes, roOperators}, 33 | # "nimrodot/classes", def.inherits) 34 | # 35 | import internal/instance_funcs 36 | 37 | # if def.needsVTable: 38 | # let vtable = def.renderVTable() 39 | $vtable 40 | # 41 | # # All pointers start out as "nil", as no virtual method has any defined 42 | # # behaviour from Godot unless we override it. 43 | let vtable: ${def.name}VTable = default(${def.name}VTable) 44 | # end if 45 | 46 | # if def.needsVTable: 47 | proc gdVTablePointer*(_: typedesc[$def.name]): ptr ${def.name}VTable = 48 | # else: 49 | proc gdVTablePointer*(_: typedesc[$def.name]): ptr $def.parentVTable = 50 | # end if 51 | # if def.needsVTable: 52 | unsafeAddr vtable 53 | # elif def.inherits.isSome(): 54 | # let parent = def.inherits.unsafeGet() 55 | ${parent}.gdVTablePointer() 56 | # else: 57 | nil 58 | # end if 59 | 60 | let instanceBindingCallbacks* = makeInstanceFunctions($def.name) 61 | 62 | proc gdInstanceBindingCallbacks*(_: typedesc[$def.name]): ptr GDExtensionInstanceBindingCallbacks = 63 | unsafeAddr instanceBindingCallbacks 64 | 65 | # #instanceBindingCallbacks = makeInstanceFunctions($def.name) 66 | 67 | # if def.constants.isSome: 68 | # result &= "# Constants\n" 69 | # 70 | # for constant in def.constants.unsafeGet(): 71 | # let constName = constant.name.safeIdent() 72 | # let constVal = constant.value 73 | # 74 | const $constName*: int = $constVal 75 | # end for 76 | 77 | # end if 78 | # 79 | # let ctorName = def.name.deriveCtorName() 80 | # 81 | # if def.is_instantiable and not def.is_singleton: 82 | # result &= "# Constructor\n" 83 | # 84 | # if def.is_refcounted and def.is_instantiable: 85 | proc $ctorName*(): Ref[$selfType] 86 | # elif def.is_instantiable: 87 | proc $ctorName*(): $selfType 88 | # end if 89 | {.gd_class_ctor.} 90 | 91 | # elif def.is_singleton: 92 | proc $ctorName*(): $selfType 93 | {.gd_class_singleton.} 94 | 95 | # end if 96 | # if def.methods.isSome: 97 | # result &= "# Methods\n" 98 | # 99 | # for meth in def.methods.unsafeGet(): 100 | # let methPrototype = meth.render(def) 101 | # 102 | # if meth.is_virtual: 103 | # let vtableEntry = meth.vtableEntryName() 104 | $methPrototype 105 | {.gd_class_method_virtual("$meth.name", ${def.name}VTable.$vtableEntry).} 106 | # else: 107 | $methPrototype 108 | # let hash = meth.hash.unsafeGet() 109 | # 110 | # if meth.return_value.isSome() and meth.return_value.unsafeGet().`type`.isClassType: 111 | {.gd_class_method_obj($hash).} 112 | # else: 113 | {.gd_class_method($hash).} 114 | # end if 115 | # end if 116 | 117 | # end for 118 | # end if 119 | # 120 | # # Properties seem infeasible to auto generate for now. 121 | # if false and def.properties.isSome: 122 | # result &= "# Properties\n" 123 | # 124 | # for property in def.properties.unsafeGet(): 125 | # if def.definesMethod(property.getter): 126 | # let getterProto = def.renderGetter(property) 127 | $getterProto = discard 128 | # end if 129 | # if property.setter.isSome(): 130 | # if def.definesMethod(property.setter.unsafeGet()): 131 | # let setterProto = def.renderSetter(property) 132 | $setterProto = discard 133 | # end if 134 | # end if 135 | # end for 136 | # end if -------------------------------------------------------------------------------- /bindgen/filters/files/classes/type.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/[options, tables, strutils, algorithm] 4 | # 5 | #import ../../../api 6 | #import ../../../helpers 7 | # 8 | #proc generate*( 9 | # def: ClassDefinition): string = 10 | # result = autogenDisclaimer() 11 | # 12 | import ../../builtins/types/stringname 13 | import ../../utils 14 | 15 | # if def.inherits.isSome(): 16 | # let parentName = def.inherits.unsafeGet() 17 | # let parentModule = parentName.toLower().safeImport() 18 | # 19 | # # We do need ../types/$module here because for some reason, ./"object" is refused. 20 | import ../types/$parentModule 21 | 22 | type 23 | ${def.name}* = ptr object of ${parentName} 24 | # else: 25 | type 26 | ${def.name}* = ptr object of RootObj 27 | opaque*: pointer 28 | vtable*: pointer 29 | # end if 30 | 31 | var className: StringName = "$def.name" 32 | 33 | proc gdClassName*(_: typedesc[$def.name]): ptr StringName = addr className 34 | # for enu in def.enums.get(@[]): 35 | # if not enu.name.startsWith("Variant."): 36 | # var duplicates = initOrderedTable[int, seq[EnumValue]]() 37 | # var lastValue = none int 38 | # 39 | 40 | type 41 | # let enumName = enu.name 42 | $enumName* {.size: sizeof(uint64).} = enum 43 | # for value in enu.values.sortedByIt(it.value): 44 | # if lastValue.isSome() and lastValue.unsafeGet() == value.value: 45 | # if value.value notin duplicates: 46 | # duplicates[value.value] = @[value] 47 | # else: 48 | # duplicates[value.value] &= value 49 | # end if 50 | # else: 51 | # lastValue = some value.value 52 | # 53 | # let cleanName = value.cleanName(enu) 54 | $cleanName = $value.value 55 | # end if 56 | # end for 57 | # if len(duplicates) > 0: 58 | 59 | # for value, dupes in pairs(duplicates): 60 | # for duplicate in dupes: 61 | # let cleanDupe = duplicate.cleanName(enu) 62 | const $cleanDupe*: $enumName = $enumName($value) 63 | # end for 64 | # end for 65 | # end if 66 | # end if 67 | # end for -------------------------------------------------------------------------------- /bindgen/filters/files/enums.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import ../../api 4 | #import ../../helpers 5 | # 6 | #import std/[strutils, algorithm, options, tables] 7 | # 8 | #proc generate*(): string = 9 | # result = autogenDisclaimer() 10 | # result &= "# Global Enumerations\n" 11 | # for enu in apiDef.global_enums: 12 | # if not enu.name.startsWith("Variant."): 13 | # var duplicates = initOrderedTable[int, seq[EnumValue]]() 14 | # var lastValue = none int 15 | # 16 | type 17 | # let enumName = enu.name 18 | $enumName* {.size: sizeof(uint64).} = enum 19 | # for value in enu.values.sortedByIt(it.value): 20 | # if lastValue.isSome() and lastValue.unsafeGet() == value.value: 21 | # if value.value notin duplicates: 22 | # duplicates[value.value] = @[value] 23 | # else: 24 | # duplicates[value.value] &= value 25 | # end if 26 | # else: 27 | # lastValue = some value.value 28 | # 29 | # let cleanName = value.cleanName(enu) 30 | $cleanName = $value.value 31 | # end if 32 | # end for 33 | # if len(duplicates) > 0: 34 | 35 | # for value, dupes in pairs(duplicates): 36 | # for duplicate in dupes: 37 | # let cleanDupe = duplicate.cleanName(enu) 38 | const $cleanDupe*: $enumName = $enumName($value) 39 | # end for 40 | # end for 41 | # end if 42 | 43 | # end if 44 | # end for -------------------------------------------------------------------------------- /bindgen/filters/files/native_structs.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/strutils 4 | # 5 | #import ../../api 6 | #import ../../helpers 7 | # 8 | #proc generate*(): string = 9 | # let structs = apiDef.native_structures 10 | # 11 | # result = autogenDisclaimer() 12 | # result &= structs.renderImportList({roNoNatives}, "nimrodot") 13 | # 14 | type 15 | # for native in structs: 16 | $native.name* = object 17 | # for field in native.format.split(';'): 18 | # var fieldName: string 19 | # var fieldType: GodotType 20 | # 21 | # field.parseCtype(fieldName, fieldType) 22 | # 23 | # let rid = fieldName.safeIdent() 24 | # let rty = fieldType.render() 25 | # 26 | $rid*: $rty 27 | # end for 28 | 29 | # end for -------------------------------------------------------------------------------- /bindgen/filters/files/utility.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/[options, sets] 4 | # 5 | #import ../../api 6 | #import ../../helpers 7 | # 8 | #proc generate*(): string = 9 | # let funcs = apiDef.utility_functions 10 | # 11 | # result = autogenDisclaimer() 12 | # result &= "# Utility Functions\n" 13 | # 14 | import ./gdffi 15 | # 16 | 17 | # result &= funcs.renderImportList({}, "nimrodot") 18 | # for fn in funcs: 19 | $fn.render 20 | {.gd_utility($fn.hash).} 21 | 22 | # end for -------------------------------------------------------------------------------- /bindgen/filters/utils/conditional.nimf: -------------------------------------------------------------------------------- 1 | #? stdtmpl() | standard 2 | # 3 | #import std/[enumerate, tables, sequtils] 4 | # 5 | #proc generateConditional*[T](grouped: OrderedTable[T, seq[string]], callable: proc(v: T): string): string = 6 | # result = "" 7 | # 8 | # for i, (thing, cfgs) in enumerate(pairs(grouped)): 9 | # let conds = cfgs.mapIt("buildConfig == \"" & it & "\"").join(" or ") 10 | # 11 | # if len(grouped) > 1: 12 | # if i == 0: 13 | when $conds: 14 | # else: 15 | elif $conds: 16 | # end if 17 | # end if 18 | # 19 | # let id = if len(grouped) > 1: 1 else: 0 20 | # 21 | # result &= (callable(thing).indent(id) & "\n") 22 | # if len(grouped) == 1: 23 | # break 24 | # end if 25 | # end for 26 | # 27 | # result.stripLineEnd() -------------------------------------------------------------------------------- /bindgen/gdtype.nim: -------------------------------------------------------------------------------- 1 | import std/[options, strutils, tables] 2 | 3 | import ./api 4 | import ./helpers 5 | 6 | type 7 | GodotTypeKind* = enum 8 | # The type was found in its own declaration 9 | tkBuiltinClassDef, 10 | tkClassDef, 11 | tkEnumDef, 12 | 13 | # The type was found in another place and is a dependency 14 | tkField, 15 | tkVarArg, 16 | tkOther # Anything else that doesn't have special requirements 17 | 18 | GodotTypeFlag* = enum 19 | tfVarType, 20 | tfPtrType, 21 | tfRefType, 22 | tfTypeDesc 23 | 24 | GodotType* = object 25 | dependant*: Option[string] 26 | flags*: set[GodotTypeFlag] 27 | kind*: GodotTypeKind 28 | rawType*: string 29 | metaType*: Option[string] 30 | 31 | # Only used for the native structs which are pretty wildly defined. 32 | dimensionality*: Option[int] 33 | 34 | func asVarType*(t: GodotType): GodotType = 35 | result = t 36 | result.flags.incl tfVarType 37 | 38 | func asTypeDesc*(t: GodotType): GodotType = 39 | result = t 40 | result.flags.incl tfTypeDesc 41 | 42 | proc cleanType(t: GodotType): GodotType = 43 | result = t 44 | 45 | if t.rawType in apiDef.classTypes and apiDef.classTypes[t.rawType].isRefcounted: 46 | result.flags.incl tfRefType 47 | 48 | # Some special cases. 49 | if t.rawType == "const void*" or t.rawType == "void*": 50 | result.rawType = "pointer" 51 | elif t.rawType == "const uint8_t*": 52 | result.rawType = "cstring" 53 | elif t.rawType == "const uint8_t **": 54 | result.rawType = "cstring" 55 | result.flags.incl tfPtrType 56 | 57 | elif t.rawType.endsWith('*'): 58 | result.flags.incl tfPtrType 59 | 60 | result.rawType = t.rawType[0..^2] 61 | 62 | if result.rawType.startsWith("const "): 63 | # Cannot model "const T*", so we model it to "ptr T" 64 | result.rawType = result.rawType[6..^1] 65 | 66 | proc fromSelf*(def: BuiltinClassDefinition | ClassDefinition | EnumDefiniton): GodotType = 67 | cleanType GodotType( 68 | dependant: none string, 69 | kind: if def is ClassDefinition: 70 | tkClassDef 71 | elif def is BuiltinClassDefinition: 72 | tkBuiltinClassDef 73 | else: 74 | tkEnumDef, 75 | 76 | rawType: def.name, 77 | metaType: none string) 78 | 79 | proc fromConst*(k: ConstantDefinition; def: BuiltinClassDefinition | ClassDefinition): GodotType = 80 | cleanType GodotType(dependant: some def.name, kind: tkOther, rawType: k.`type`, metaType: none string) 81 | 82 | proc fromField*(field: MemberOffset; def: BuiltinClassDefinition): GodotType = 83 | cleanType GodotType(dependant: some def.name, kind: tkField, rawType: field.meta, metaType: none string) 84 | 85 | proc fromProperty*(prop: PropertyDefinition; def: BuiltinClassDefinition): GodotType = 86 | cleanType GodotType(dependant: some def.name, kind: tkOther, rawType: prop.`type`, metaType: none string) 87 | 88 | proc fromVarious*(t: string; def: ClassDefinition | BuiltinClassDefinition): GodotType = 89 | cleanType GodotType(dependant: some def.name, kind: tkOther, rawType: t, metaType: none string) 90 | 91 | proc fromParameter*(arg: FunctionArgument; def: ClassDefinition | BuiltinClassDefinition): GodotType = 92 | result = cleanType GodotType(dependant: some def.name, kind: tkOther, rawType: arg.`type`, metaType: arg.meta) 93 | 94 | proc fromParameter*(arg: FunctionArgument; def: FunctionDefinition): GodotType = 95 | cleanType GodotType(dependant: none string, kind: tkOther, rawType: arg.`type`, metaType: arg.meta) 96 | 97 | proc fromReturn*(ret: string; def: BuiltinClassDefinition | FunctionDefinition): GodotType = 98 | let dep = if def is MethodDefinition: 99 | some def.name 100 | else: 101 | none string 102 | 103 | result = cleanType GodotType( 104 | dependant: dep, 105 | kind: tkOther, 106 | rawType: ret, 107 | metaType: none string) 108 | 109 | 110 | proc fromReturn*(ret: ClassMethodReturn; def: ClassDefinition | BuiltinClassDefinition): GodotType = 111 | cleanType GodotType(dependant: some def.name, kind: tkOther, rawType: ret.`type`, metaType: ret.meta) 112 | 113 | 114 | func parseCtype*(raw: string; ident: var string; outType: var GodotType) = 115 | var parts = raw.split(' ') 116 | var typBase = parts[0] 117 | 118 | if "::" in typBase: 119 | typBase = "enum::" & typBase.replace("::", ".") 120 | 121 | outType = GodotType(dependant: none string, kind: tkOther, rawType: typBase, metaType: none string) 122 | ident = parts[1] 123 | 124 | if ident.startsWith('*'): 125 | outType.flags.incl tfPtrType 126 | ident = ident[1..^1] 127 | 128 | if ident.endsWith(']'): 129 | let pos = ident.find('[') 130 | let dim = parseInt ident[pos + 1..^2] 131 | 132 | ident = ident[0..pos - 1] 133 | 134 | outType.dimensionality = some dim 135 | 136 | proc makeVarArgsParam*(def: ClassDefinition | BuiltinClassDefinition | FunctionDefinition): GodotType = 137 | let dep = when compiles(def.name): 138 | some def.name 139 | else: 140 | none string 141 | 142 | cleanType GodotType( 143 | dependant: dep, 144 | kind: tkVarArg, 145 | rawType: "varargs[Variant, newVariant]", 146 | metaType: none string) -------------------------------------------------------------------------------- /bindgen/helpers.nim: -------------------------------------------------------------------------------- 1 | import std/[options, enumerate, strutils, strformat, sets, tables, sugar] 2 | 3 | import ./api 4 | 5 | var apiDef*: Api 6 | 7 | import ./gdtype 8 | export gdtype 9 | 10 | func typeId(name: string): int = 11 | case name 12 | of "Nil": 0 13 | of "bool": 1 14 | of "int": 2 15 | of "float": 3 16 | of "String": 4 17 | of "Vector2": 5 18 | of "Vector2i": 6 19 | of "Rect2": 7 20 | of "Rect2i": 8 21 | of "Vector3": 9 22 | of "Vector3i": 10 23 | of "Transform2D": 11 24 | of "Vector4": 12 25 | of "Vector4i": 13 26 | of "Plane": 14 27 | of "Quaternion": 15 28 | of "AABB": 16 29 | of "Basis": 17 30 | of "Transform3D": 18 31 | of "Projection": 19 32 | of "Color": 20 33 | of "StringName": 21 34 | of "NodePath": 22 35 | of "RID": 23 36 | of "Object": 24 37 | of "Callable": 25 38 | of "Signal": 26 39 | of "Dictionary": 27 40 | of "Array": 28 41 | of "PackedByteArray": 29 42 | of "PackedInt32Array": 30 43 | of "PackedInt64Array": 31 44 | of "PackedFloat32Array": 32 45 | of "PackedFloat64Array": 33 46 | of "PackedStringArray": 34 47 | of "PackedVector2Array": 35 48 | of "PackedVector3Array": 36 49 | of "PackedColorArray": 37 50 | else: -1 51 | 52 | func typeId*(def: BuiltinClassDefinition): int = 53 | let id = def.name.typeId 54 | 55 | if id >= 0: 56 | return id 57 | else: 58 | raise newException(ValueError, "bad builtin class " & def.name) 59 | 60 | func isBuiltinClass*(name: string): bool = name.typeId >= 0 61 | 62 | const reservedWords = block: 63 | var words = initHashSet[string]() 64 | const reserved = """ 65 | string int float bool array result 66 | addr and as asm 67 | bind block break 68 | case cast concept const continue converter 69 | defer discard distinct div do 70 | elif else end enum except export 71 | finally for from func 72 | if import in include interface is isnot iterator 73 | let 74 | macro method mixin mod 75 | nil not notin 76 | object of or out 77 | proc ptr 78 | raise ref return 79 | shl shr static 80 | template try tuple type 81 | using 82 | var 83 | when while 84 | xor 85 | yield""" 86 | 87 | for word in reserved.split(): 88 | incl words, word 89 | 90 | words 91 | 92 | func isReservedWord*(ident: string): bool = 93 | if ident in reservedWords: 94 | return true 95 | 96 | case ident 97 | of "nil", "from", "type", "object", "method", "func", "end", "mixin", "bind", "mod", "div": return true 98 | else: return false 99 | 100 | type 101 | DependencyHint* = enum 102 | dhGlobalEnums 103 | dhGdffi 104 | dhCoreClasses 105 | 106 | TypedEnum* = tuple 107 | class: Option[string] 108 | enumName: string 109 | 110 | func parseTypedEnum*(e: string): TypedEnum = 111 | let offset = if e.startsWith("enum::"): 6 else: 10 112 | let normalized = e[offset..^1] 113 | 114 | if '.' in normalized: 115 | let r = normalized.split('.') 116 | 117 | result.class = some r[0] 118 | result.enumName = r[1] 119 | else: 120 | result.class = none string 121 | result.enumName = normalized 122 | 123 | func moduleName*(className: string): string = 124 | className.toLower() 125 | 126 | func moduleName*(class: BuiltinClassDefinition): string = 127 | class.name.moduleName() 128 | 129 | func moduleName*(class: ClassDefinition): string = 130 | class.name.moduleName() 131 | 132 | func render*(t: GodotType): string = 133 | let actualType = t.rawType 134 | let isMember = t.kind == tkField 135 | 136 | result = case actualType: 137 | of "int": 138 | if isMember: "int32" else: "int64" 139 | 140 | of "float": 141 | if isMember: "float32" else: "float64" 142 | 143 | of "double": 144 | if isMember: "float64" else: "float64" 145 | 146 | of "const void*", "void*": "pointer" 147 | 148 | of "real_t": "float64" 149 | 150 | of "uint8_t": "uint8" 151 | of "uint16_t": "uint16" 152 | of "uint32_t": "uint32" 153 | of "uint64_t": "uint64" 154 | 155 | of "int8_t": "int8" 156 | of "int16_t": "int16" 157 | of "int32_t", "int32": "int32" 158 | of "int64_t": "int64" 159 | else: 160 | actualType 161 | 162 | if result.startsWith("typedarray::"): 163 | result = "TypedArray[" & result[12..^1] & "]" 164 | 165 | if result.startsWith("enum::") or result.startsWith("bitfield::"): 166 | # enums and bitfields are the same for our purposes 167 | let te = actualType.parseTypedEnum() 168 | 169 | if te.class.isSome(): 170 | result = te.class.unsafeGet().moduleName() & "." & te.enumName 171 | else: 172 | result = te.enumName 173 | 174 | if tfRefType in t.flags: 175 | result = "Ref[" & result & "]" 176 | 177 | if tfVarType in t.flags: 178 | result = "var " & result 179 | elif tfPtrType in t.flags: 180 | result = "ptr " & result 181 | elif tfTypeDesc in t.flags: 182 | result = "typedesc[" & result & "]" 183 | 184 | if t.dimensionality.isSome(): 185 | result = "array[" & $t.dimensionality.unsafeGet() & ", " & result & "]" 186 | 187 | 188 | func innerDependencies*(t: GodotType; hints: var set[DependencyHint]): OrderedSet[string] = 189 | result = initOrderedSet[string]() 190 | 191 | let rt = t.metaType.get(t.rawType) 192 | 193 | if tfRefType in t.flags: 194 | hints.incl dhCoreClasses 195 | 196 | if rt.startsWith("enum::") or rt.startsWith("bitfield::"): 197 | # enums and bitfields are the same for our purposes 198 | let te = rt.parseTypedEnum() 199 | 200 | if te.class.isSome(): 201 | result.incl te.class.unsafeGet() 202 | else: 203 | hints.incl dhGlobalEnums 204 | 205 | elif rt.startsWith("typedarray::"): 206 | result.incl "TypedArray" 207 | result.incl rt[12..^1] 208 | 209 | else: 210 | result.incl rt 211 | 212 | 213 | func isNativeClass*(className: string): bool = 214 | case className 215 | of "int", "bool", "float", "double", "float64", "int32", "int64", "uint64": true 216 | 217 | # Oddities from classes and native structs 218 | of "const void*", "uint64_t": true 219 | else: false 220 | 221 | func autogenDisclaimer*(): string = 222 | "# Generated by nimrodot on " & CompileDate & "T" & CompileTime & "Z\n" & 223 | "# Tamper at your own leisure but know that changes may get wiped out\n\n" 224 | 225 | func isNativeClass*(class: BuiltinClassDefinition): bool = 226 | class.name.isNativeClass() 227 | 228 | func isOperator(id: string): bool = 229 | for c in id: 230 | if c.isAlphaAscii(): return false 231 | 232 | return true 233 | 234 | func safeIdent*(id: string): string = 235 | # Nim identifiers may not start with "_", which some protected identifiers in 236 | # C like languages like to do 237 | 238 | if id == "result": # very reserved in functions. 239 | return "p_result" 240 | 241 | let canonicalId = if id.startsWith('_'): 242 | "v" & id 243 | else: 244 | id 245 | 246 | if canonicalId.isReservedWord or canonicalId.isOperator: 247 | return "`" & canonicalId & "`" 248 | else: 249 | return canonicalId 250 | 251 | func safeImport*(id: string): string = 252 | if id.isReservedWord: 253 | return '"' & id & '"' 254 | else: 255 | return id 256 | 257 | proc isOpaque*(class: BuiltinClassDefinition): bool = 258 | # It's either opaque one all configs or on none. 259 | for cfgClass in apiDef.builtin_class_member_offsets[0].classes: 260 | if cfgClass.name == class.name: 261 | return false 262 | 263 | return true 264 | 265 | proc isClassType*(name: string): bool = 266 | name in apiDef.classTypes 267 | 268 | func isSimpleType*(k: ConstantDefinition): bool = 269 | k.type.isNativeClass() 270 | 271 | #func actualType(arg: ClassMethodReturn | FunctionArgument): string = 272 | # arg.meta.get(arg.`type`) 273 | 274 | func indent*(code: string; n: int = 1): string = 275 | let indentPad = repeat(" ", n) 276 | 277 | indentPad & code 278 | # Indent after newline 279 | .replace("\n", "\n" & indentPad) 280 | 281 | # Strip trailing whitespace 282 | .strip(false, true, {' '}) 283 | 284 | # Trim only-indent lines 285 | .replace("\n" & indentPad & "\n", "\n\n") 286 | 287 | 288 | ## Dependency tracking 289 | type 290 | SomeDependant* = BuiltinClassDefinition | ClassDefinition | 291 | seq[FunctionDefinition] | seq[NativeStructure] 292 | 293 | ClassDependencies* = object 294 | builtins*: OrderedSet[string] 295 | classes*: OrderedSet[string] 296 | native_structs*: OrderedSet[string] 297 | 298 | DependencyResolveOption* = enum 299 | roNoNatives 300 | 301 | # Builtins + Classes 302 | roProperties 303 | roMethods 304 | 305 | # Builtins 306 | roStructFields 307 | roConstructors 308 | roIndexes 309 | roOperators 310 | 311 | # For Classes 312 | roParentClass 313 | 314 | proc referencedTypes*( 315 | def: SomeDependant; 316 | opt: set[DependencyResolveOption]; 317 | hints: var set[DependencyHint]): OrderedSet[string] = 318 | var topLevel = initOrderedSet[GodotType]() 319 | 320 | when def is BuiltinClassDefinition: 321 | # Reference fields 322 | if roStructFields in opt: 323 | for fields, _ in pairs def.memberGroups(): 324 | for field in fields: 325 | topLevel.incl field.fromField(def) 326 | 327 | break 328 | 329 | # Reference all parameter types in all constructors 330 | if roConstructors in opt: 331 | for ctor in def.constructors: 332 | for arg in ctor.arguments.get(@[]): 333 | topLevel.incl arg.fromParameter(def) 334 | 335 | # Reference members 336 | if roProperties in opt: 337 | for member in def.members.get(@[]): 338 | topLevel.incl member.fromProperty(def) 339 | 340 | if roIndexes in opt: 341 | # Reference index 342 | if def.indexing_return_type.isSome(): 343 | topLevel.incl def.indexing_return_type.unsafeGet().fromVarious(def) 344 | 345 | if def.is_keyed: # No need to import anything for the alternative 346 | topLevel.incl "Variant".fromVarious(def) 347 | 348 | if roOperators in opt: 349 | # Reference operator 350 | for oper in def.operators: 351 | topLevel.incl oper.return_type.fromVarious(def) 352 | 353 | if oper.right_type.isSome(): 354 | topLevel.incl oper.right_type.unsafeGet().fromVarious(def) 355 | 356 | when def is BuiltinClassDefinition or def is ClassDefinition: 357 | if roMethods in opt: 358 | for meth in def.methods.get(@[]): 359 | if not meth.is_static: 360 | topLevel.incl fromSelf(def) 361 | 362 | when def is BuiltinClassDefinition: 363 | if meth.return_type.isSome(): 364 | topLevel.incl meth.return_type.unsafeGet().fromReturn(def) 365 | 366 | elif def is ClassDefinition: 367 | if meth.return_value.isSome(): 368 | topLevel.incl meth.return_value.unsafeGet().fromReturn(def) 369 | 370 | for param in meth.arguments.get(@[]): 371 | topLevel.incl param.fromParameter(def) 372 | 373 | when def is seq[FunctionDefinition]: 374 | for fn in def: 375 | if fn.return_type.isSome(): 376 | topLevel.incl fn.return_type.unsafeGet().fromReturn(fn) 377 | 378 | for arg in fn.arguments.get(@[]): 379 | topLevel.incl arg.fromParameter(fn) 380 | 381 | when def is seq[NativeStructure]: 382 | for nat in def: 383 | for field in nat.format.split(";"): 384 | var fieldName: string 385 | var fieldType: GodotType 386 | 387 | field.parseCtype(fieldName, fieldType) 388 | 389 | topLevel.incl fieldType 390 | 391 | # Resolve inner dependencies 392 | result = initOrderedSet[string]() 393 | 394 | # I broke overload resolution somehow, so we explicitely refer to 395 | # sets.items() 396 | for referenced in sets.items(topLevel): 397 | for inner in sets.items(referenced.innerDependencies(hints)): 398 | result.incl inner 399 | 400 | when def is BuiltinClassDefinition or def is ClassDefinition: 401 | result.excl def.name 402 | 403 | let nativeTypes = toHashSet [ 404 | "int", "float", "double", "pointer", "real_t", "bool", 405 | "cstring", 406 | 407 | # Very little consistency in the JSON data. 408 | "uint8_t", "uint16_t", "uint32_t", "uint64_t", 409 | "uint8", "uint16", "uint32", "uint64", 410 | 411 | "int8_t", "int16_t", "int32_t", "int64_t", 412 | "int8", "int16", "int32", "int64" 413 | ] 414 | 415 | proc splitClassDependencies*(deps: OrderedSet[string]): ClassDependencies = 416 | # TODO: cache 417 | result.builtins = initOrderedSet[string]() 418 | result.classes = initOrderedSet[string]() 419 | result.native_structs = initOrderedSet[string]() 420 | 421 | for unsorted in deps: 422 | if unsorted in apiDef.builtinClassTypes: 423 | result.builtins.incl unsorted 424 | 425 | elif unsorted in apiDef.nativeStructTypes: 426 | result.native_structs.incl unsorted 427 | 428 | elif unsorted notin nativeTypes: 429 | # Not in builtins, native structs or a totally native type => class 430 | result.classes.incl unsorted 431 | 432 | 433 | # Helpers to group equivalent members and sizes into `when ...`-able groups. 434 | proc memberGroups*(class: BuiltinClassDefinition): OrderedTable[seq[MemberOffset], seq[string]] = 435 | result = initOrderedTable[seq[MemberOffset], seq[string]]() 436 | 437 | for cfg in apiDef.builtin_class_member_offsets: 438 | for cfgClass in cfg.classes: 439 | if cfgClass.name == class.name: 440 | let cleanedMembers = collect(newSeq()): 441 | for mem in cfgClass.members: 442 | # We don't care about the offset for the groupings because the 443 | # the compiler will do that for us. 444 | MemberOffset(member: mem.member, meta: mem.meta, offset: 0) 445 | 446 | if cleanedMembers notin result: 447 | result[cleanedMembers] = @[] 448 | 449 | result[cleanedMembers] &= cfg.build_configuration 450 | 451 | proc sizeGroups*(className: string): OrderedTable[int, seq[string]] = 452 | result = initOrderedTable[int, seq[string]]() 453 | 454 | for cfg in apiDef.builtin_class_sizes: 455 | for cfgClass in cfg.sizes: 456 | if cfgClass.name == className: 457 | if cfgClass.size notin result: 458 | result[cfgClass.size] = @[] 459 | 460 | result[cfgClass.size] &= cfg.build_configuration 461 | 462 | proc sizeGroups*(class: BuiltinClassDefinition): OrderedTable[int, seq[string]] = 463 | return class.name.sizeGroups() 464 | 465 | func isPointerType*(class: BuiltinClassDefinition): bool = 466 | # Small optimization so we can render some opaque types as "pointer" 467 | case class.name 468 | of "String", "Dictionary", "Array", "StringName", "NodePath": true 469 | else: false 470 | 471 | proc isSingleton*(class: ClassDefinition): bool = 472 | for sngl in apiDef.singletons: 473 | if sngl.name == class.name: 474 | return true 475 | 476 | func getMethod*(class: ClassDefinition; meth: string): Option[ClassMethodDefinition] = 477 | for m in class.methods.get(@[]): 478 | if m.name == meth: 479 | return some m 480 | 481 | func copyCtorIdx*(builtin: BuiltinClassDefinition): int = 482 | for constructor in builtin.constructors: 483 | if constructor.arguments.isSome() and 484 | len(constructor.arguments.unsafeGet()) == 1 and 485 | constructor.arguments.unsafeGet()[0].`type` == builtin.name: 486 | 487 | return constructor.index 488 | 489 | func definesMethod*(class: ClassDefinition; meth: string): bool = 490 | class.getMethod(meth).isSome() 491 | 492 | # Style conventions 493 | func deriveCtorName*(clsName: string): string = 494 | "new" & clsName 495 | 496 | func deriveDtorName*(clsName: string): string = 497 | "destroy" 498 | 499 | 500 | # Purely visual rendering things below 501 | type FunctionRenderOptions = enum 502 | roNotExported, 503 | roFunc, 504 | roAnonymous 505 | 506 | type 507 | CombinedArgs = tuple 508 | bindings: seq[string] 509 | `type`: GodotType 510 | 511 | import std/[os, sequtils] 512 | 513 | proc renderImportList*( 514 | dependant: SomeDependant; 515 | options: set[DependencyResolveOption]; 516 | selfDir: string; 517 | ignore: Option[string] = none string): string = 518 | 519 | result = "" 520 | 521 | let relativeRootPath = "nimrodot".relativePath(selfDir) & "/" 522 | let classesPath = "nimrodot/classes/types".relativePath(selfDir) & "/" 523 | let builtinsPath = "nimrodot/builtins/types".relativePath(selfDir) & "/" 524 | 525 | var outHints: set[DependencyHint] = {} 526 | var references = dependant.referencedTypes(options, outHints) 527 | 528 | when dependant is ClassDefinition: 529 | # Include Ref[T] is we're going to generate a constructor 530 | if dependant.is_refcounted and dependant.is_instantiable: 531 | outHints.incl dhCoreClasses 532 | 533 | if dependant.is_instantiable or # constructor needs gdffi 534 | dependant.is_singleton or # singleton query too 535 | dependant.methods.isSome(): 536 | outHints.incl dhGdffi 537 | 538 | 539 | if ignore.isSome(): 540 | references.excl ignore.unsafeGet() 541 | 542 | let sorted = references.splitClassDependencies() 543 | 544 | # Update our cache 545 | when compiles(dependant.name): 546 | if dependant.name notin apiDef.typeDeps: 547 | apiDef.typeDeps[dependant.name] = initOrderedSet[string]() 548 | 549 | for reference in sets.items(references): 550 | apiDef.typeDeps[dependant.name].incl reference 551 | apiDef.typeDeps[dependant.name].incl reference.moduleName() 552 | 553 | if dhGlobalEnums in outHints: result &= "import " & relativeRootPath & "enums\n" 554 | if dhCoreClasses in outHints: result &= "import " & relativeRootPath & "ref_helper\n" 555 | if dhGdffi in outHints: result &= "import " & relativeRootPath & "gdffi\n" 556 | 557 | if roNoNatives notin options: 558 | if len(sorted.native_structs) > 0: result &= "import " & relativeRootPath & "native_structs\n" 559 | 560 | if len(sorted.builtins) > 0: 561 | if len(result) > 0: 562 | result &= "\n" 563 | 564 | result &= "import\n" 565 | 566 | for i, builtin in enumerate(sets.items(sorted.builtins)): 567 | result &= " " & builtinsPath & builtin.moduleName().safeImport() 568 | result &= (if i < len(sorted.builtins) - 1: ",\n" else: "\n") 569 | 570 | 571 | if len(sorted.classes) > 0: 572 | if len(result) > 0: 573 | result &= "\n" 574 | 575 | result &= "import\n" 576 | 577 | for i, class in enumerate(sets.items(sorted.classes)): 578 | result &= " " & classesPath & class.moduleName().safeImport() 579 | result &= (if i < len(sorted.classes) - 1: ",\n" else: "\n") 580 | 581 | if len(result) > 0: 582 | result &= "\n" 583 | 584 | proc renderGetter*(def: ClassDefinition; property: ClassPropertyDefinition): string = 585 | let getterMethod = def.getMethod(property.getter) 586 | 587 | "TBD" 588 | 589 | proc renderSetter*(def: ClassDefinition; property: ClassPropertyDefinition): string = 590 | let setterMethod = def.getMethod(property.getter) 591 | 592 | "TBD" 593 | 594 | proc renderArgs(args: openArray[CombinedArgs]): string = 595 | result = "(" 596 | 597 | for i, arg in enumerate(args): 598 | result &= arg.bindings.join(", ") & ": " & arg.`type`.render() 599 | 600 | if i < args.len - 1: 601 | result &= "; " 602 | 603 | result &= ")" 604 | 605 | proc renderDtor*(def: BuiltinClassDefinition): string = 606 | &"proc destroy*(self: sink {def.name})" 607 | 608 | proc render*(ctor: ConstructorDefinition, def: BuiltinClassDefinition): string = 609 | var args = newSeq[CombinedArgs]() 610 | 611 | let selfType = fromSelf(def) 612 | 613 | #args &= (bindings: @["_"], `type`: "typedesc[" & selfType.renderType() & "]") 614 | 615 | for defArg in ctor.arguments.get(@[]): 616 | let defArgType = defArg.fromParameter(def) 617 | 618 | if len(args) == 0 or args[^1].`type` != defArgType: 619 | args &= ( 620 | bindings: @[defArg.name.safeIdent()], 621 | `type`: defArgType) 622 | else: 623 | args[^1].bindings &= defArg.name.safeIdent() 624 | 625 | result = "proc " & def.name.deriveCtorName() & "*" & args.renderArgs() & ": " & selfType.render() 626 | 627 | proc render*( 628 | meth: ClassMethodDefinition; 629 | def: ClassDefinition | BuiltinClassDefinition; 630 | qualifyEnums: bool = true; 631 | anonymous: bool = false): string = 632 | # TODO: 633 | # - Default values 634 | # 635 | var args = newSeq[CombinedArgs]() 636 | 637 | let selfType = fromSelf(def) 638 | let isRc = when def is ClassDefinition: 639 | def.is_refcounted 640 | else: 641 | false 642 | 643 | var genericParam = none string 644 | 645 | if meth.is_static: 646 | var tmp = selfType 647 | 648 | tmp.flags.excl tfRefType 649 | 650 | args &= (bindings: @["_"], `type`: tmp.asTypeDesc()) 651 | else: 652 | if isRc and not anonymous: 653 | var genericRefT = "T".fromVarious(def) 654 | genericParam = some def.name 655 | genericRefT.flags.incl tfRefType 656 | 657 | args &= (bindings: @["self"], `type`: genericRefT) 658 | else: 659 | if meth.is_const or def is ClassDefinition: 660 | args &= (bindings: @["self"], `type`: selfType) 661 | else: 662 | args &= (bindings: @["self"], `type`: selfType.asVarType()) 663 | 664 | for defArg in meth.arguments.get(@[]): 665 | let defArgType = defArg.fromParameter(def) 666 | var paramArg = defArg.name.safeIdent() 667 | 668 | # We need to hack around a bit in case a method defines a parameter with the same 669 | # name as a dependency prefix that may be used within the same prototype. 670 | if paramArg in apiDef.typeDeps[def.name] or paramArg == def.name.moduleName(): 671 | paramArg = "p_" & paramArg 672 | 673 | if len(args) == 1 or args[^1].`type` != defArgType: 674 | args &= ( 675 | bindings: @[paramArg], 676 | `type`: defArgType) 677 | else: 678 | args[^1].bindings &= paramArg 679 | 680 | if meth.is_vararg: 681 | args &= (bindings: @["args"], `type`: def.makeVarArgsParam()) 682 | 683 | if anonymous: 684 | result = "proc" & args.renderArgs() 685 | else: 686 | if genericParam.isSome(): 687 | let constraint = "[T: " & genericParam.unsafeGet() & "]" 688 | 689 | result = "proc " & meth.name.safeIdent() & "*" & constraint & args.renderArgs() 690 | else: 691 | result = "proc " & meth.name.safeIdent() & "*" & args.renderArgs() 692 | 693 | if meth.return_value.isSome(): 694 | let ret = meth.return_value.unsafeGet().fromReturn(def) 695 | 696 | result &= ": " & ret.render() 697 | 698 | proc render*(meth: MethodDefinition, def: BuiltinClassDefinition): string = 699 | # A builtin class method is just a simpler class method. 700 | let asClassMethod = ClassMethodDefinition( 701 | name: meth.name, 702 | is_static: meth.is_static, 703 | is_const: meth.is_const, 704 | is_vararg: meth.is_vararg, 705 | is_virtual: false, 706 | hash: some meth.hash, 707 | return_value: meth.return_type.map(proc(ty: string): ClassMethodReturn = 708 | result.`type` = ty 709 | result.meta = none string), 710 | 711 | arguments: meth.arguments 712 | ) 713 | 714 | return asClassMethod.render(def, false) 715 | 716 | proc render*(fn: FunctionDefinition; opts: set[FunctionRenderOptions] = {}): string = 717 | result = if roFunc in opts: "func" else: "proc" 718 | 719 | if roAnonymous notin opts: 720 | result &= " " & fn.name.safeIdent() 721 | 722 | if roNotExported notin opts: 723 | result &= "*" 724 | 725 | var args = newSeq[CombinedArgs]() 726 | 727 | for defArg in fn.arguments.get(@[]): 728 | let defArgType = defArg.fromParameter(fn) 729 | 730 | if len(args) == 0 or args[^1].`type` != defArgType: 731 | args &= ( 732 | bindings: @[defArg.name.safeIdent()], 733 | `type`: defArgType) 734 | else: 735 | args[^1].bindings &= defArg.name.safeIdent() 736 | 737 | if fn.is_vararg: 738 | args &= (bindings: @["args"], `type`: fn.makeVarArgsParam()) 739 | 740 | result &= args.renderArgs() 741 | 742 | if fn.return_type.get("void") != "void": 743 | let ret = fromReturn(fn.return_type.unsafeGet(), fn) 744 | 745 | result &= ": " & ret.render() 746 | 747 | 748 | proc render*(op: OperatorDefinition; left: string): string = 749 | let name = case op.name 750 | of "unary-": "-" 751 | of "unary+": "+" 752 | else: op.name 753 | 754 | var args = @[FunctionArgument(name: "lhs", `type`: left)] 755 | 756 | if op.right_type.isSome(): 757 | args &= FunctionArgument(name: "rhs", `type`: op.right_type.unsafeGet()) 758 | 759 | let dummyFunc = FunctionDefinition( 760 | name: name, 761 | return_type: some op.return_type, 762 | category: "operator", 763 | is_vararg: false, 764 | hash: 0, 765 | arguments: some args) 766 | 767 | return dummyFunc.render() 768 | 769 | proc needsVtable*(def: ClassDefinition): bool = 770 | for meth in def.methods.get(@[]): 771 | if meth.is_virtual: 772 | result = true 773 | 774 | break 775 | 776 | proc vtableEntryName*(meth: ClassMethodDefinition): string = 777 | meth.name.safeIdent() 778 | 779 | proc parentVTable*(def: ClassDefinition): string = 780 | result = "RootObj" 781 | 782 | var parent = def.inherits 783 | 784 | while parent.isSome() and not apiDef.classTypes[parent.unsafeGet()].needsVTable: 785 | parent = apiDef.classTypes[parent.unsafeGet()].inherits 786 | 787 | if parent.isSome(): 788 | result = parent.unsafeGet() & "VTable" 789 | 790 | proc renderVtable*(def: ClassDefinition): string = 791 | result = "type\n " & def.name & "VTable* = object of " & def.parentVTable() & "\n" 792 | 793 | for meth in def.methods.unsafeGet(): 794 | if not meth.is_virtual: 795 | continue 796 | 797 | let virtualProto = meth.render(def, anonymous = true) 798 | 799 | result &= " " & meth.vtableEntryName() & "*: " & virtualProto & " {.gd_name: \"" & meth.name & "\".}\n" 800 | 801 | func splitTitleCase(tcs: string): seq[string] = 802 | result = @[$tcs[0]] 803 | 804 | for c in tcs[1..^1]: 805 | if result[^1][^1].isLowerAscii() and c.isUpperAscii(): 806 | result &= $c 807 | else: 808 | result[^1] &= $c 809 | 810 | proc derivePrefix(enu: EnumDefiniton; pfxLen: int = 3): string = 811 | var prefixCache {.global.} = initTable[EnumDefiniton, string]() 812 | 813 | if enu in prefixCache: 814 | return prefixCache[enu] 815 | 816 | result = "" 817 | 818 | let parts = enu.name.splitTitleCase() 819 | 820 | var i = 0 821 | 822 | while i < pfxLen: 823 | if i >= len(parts): 824 | break 825 | elif len(parts[i]) == 0: 826 | continue 827 | 828 | if len(parts) - 1 <= i: 829 | result &= parts[^1][0..min(len(parts[^1]) - 1, pfxLen - i - 1)].toLowerAscii() 830 | break 831 | else: 832 | result &= parts[i][0].toLowerAscii() 833 | 834 | inc i 835 | 836 | prefixCache[enu] = result 837 | 838 | proc cleanName*(value: EnumValue; parent: EnumDefiniton; prefixEnumAbbr: bool = true): string = 839 | result = case parent.name: 840 | of "Key": value.name[len("KEY_")..^1] 841 | of "Axis": value.name[len("AXIS_")..^1] 842 | of "MouseButton": value.name[len("MOUSE_BUTTON_")..^1] 843 | of "MouseButtonMask": value.name[len("MOUSE_BUTTON_MASK_")..^1] 844 | of "JoyButton": value.name[len("JOY_BUTTON_")..^1] 845 | of "JoyAxis": value.name[len("JOY_AXIS_")..^1] 846 | of "MIDIMessage": value.name[len("MIDI_MESSAGE_")..^1] 847 | of "PropertyHint": value.name[len("PROPERTY_HINT_")..^1] 848 | of "PropertyUsageFlags": value.name[len("PROPERTY_USAGE_")..^1] 849 | of "MethodFlags": 850 | if value.name == "METHOD_FLAGS_DEFAULT": "DEFAULT" 851 | else: value.name[len("METHOD_FLAG_")..^1] 852 | 853 | of "Error": 854 | if value.name.startsWith("ERR_"): 855 | value.name[len("ERR_")..^1] 856 | else: 857 | value.name 858 | 859 | else: value.name 860 | 861 | var parts = result.split('_') 862 | 863 | # Transform to camelCase 864 | for i in 0..high(parts): 865 | parts[i] = parts[i][0].toUpperAscii() & parts[i][1..^1].toLowerAscii() 866 | 867 | # If we want to prefix, make TitleCase 868 | if not prefixEnumAbbr: 869 | parts[0] = parts[0].toLowerAscii() 870 | 871 | result = parts.join() 872 | 873 | # Append prefix, maybe 874 | if prefixEnumAbbr: 875 | result = parent.derivePrefix() & result -------------------------------------------------------------------------------- /contrib/gdextension_interface.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /* gdextension_interface.h */ 3 | /**************************************************************************/ 4 | /* This file is part of: */ 5 | /* GODOT ENGINE */ 6 | /* https://godotengine.org */ 7 | /**************************************************************************/ 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ 10 | /* */ 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ 12 | /* a copy of this software and associated documentation files (the */ 13 | /* "Software"), to deal in the Software without restriction, including */ 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ 16 | /* permit persons to whom the Software is furnished to do so, subject to */ 17 | /* the following conditions: */ 18 | /* */ 19 | /* The above copyright notice and this permission notice shall be */ 20 | /* included in all copies or substantial portions of the Software. */ 21 | /* */ 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29 | /**************************************************************************/ 30 | 31 | #ifndef GDEXTENSION_INTERFACE_H 32 | #define GDEXTENSION_INTERFACE_H 33 | 34 | /* This is a C class header, you can copy it and use it directly in your own binders. 35 | * Together with the JSON file, you should be able to generate any binder. 36 | */ 37 | 38 | #include 39 | #include 40 | 41 | #ifndef __cplusplus 42 | typedef uint32_t char32_t; 43 | typedef uint16_t char16_t; 44 | #endif 45 | 46 | #ifdef __cplusplus 47 | extern "C" { 48 | #endif 49 | 50 | /* VARIANT TYPES */ 51 | 52 | typedef enum { 53 | GDEXTENSION_VARIANT_TYPE_NIL, 54 | 55 | /* atomic types */ 56 | GDEXTENSION_VARIANT_TYPE_BOOL, 57 | GDEXTENSION_VARIANT_TYPE_INT, 58 | GDEXTENSION_VARIANT_TYPE_FLOAT, 59 | GDEXTENSION_VARIANT_TYPE_STRING, 60 | 61 | /* math types */ 62 | GDEXTENSION_VARIANT_TYPE_VECTOR2, 63 | GDEXTENSION_VARIANT_TYPE_VECTOR2I, 64 | GDEXTENSION_VARIANT_TYPE_RECT2, 65 | GDEXTENSION_VARIANT_TYPE_RECT2I, 66 | GDEXTENSION_VARIANT_TYPE_VECTOR3, 67 | GDEXTENSION_VARIANT_TYPE_VECTOR3I, 68 | GDEXTENSION_VARIANT_TYPE_TRANSFORM2D, 69 | GDEXTENSION_VARIANT_TYPE_VECTOR4, 70 | GDEXTENSION_VARIANT_TYPE_VECTOR4I, 71 | GDEXTENSION_VARIANT_TYPE_PLANE, 72 | GDEXTENSION_VARIANT_TYPE_QUATERNION, 73 | GDEXTENSION_VARIANT_TYPE_AABB, 74 | GDEXTENSION_VARIANT_TYPE_BASIS, 75 | GDEXTENSION_VARIANT_TYPE_TRANSFORM3D, 76 | GDEXTENSION_VARIANT_TYPE_PROJECTION, 77 | 78 | /* misc types */ 79 | GDEXTENSION_VARIANT_TYPE_COLOR, 80 | GDEXTENSION_VARIANT_TYPE_STRING_NAME, 81 | GDEXTENSION_VARIANT_TYPE_NODE_PATH, 82 | GDEXTENSION_VARIANT_TYPE_RID, 83 | GDEXTENSION_VARIANT_TYPE_OBJECT, 84 | GDEXTENSION_VARIANT_TYPE_CALLABLE, 85 | GDEXTENSION_VARIANT_TYPE_SIGNAL, 86 | GDEXTENSION_VARIANT_TYPE_DICTIONARY, 87 | GDEXTENSION_VARIANT_TYPE_ARRAY, 88 | 89 | /* typed arrays */ 90 | GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY, 91 | GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY, 92 | GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY, 93 | GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY, 94 | GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY, 95 | GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY, 96 | GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY, 97 | GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY, 98 | GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY, 99 | 100 | GDEXTENSION_VARIANT_TYPE_VARIANT_MAX 101 | } GDExtensionVariantType; 102 | 103 | typedef enum { 104 | /* comparison */ 105 | GDEXTENSION_VARIANT_OP_EQUAL, 106 | GDEXTENSION_VARIANT_OP_NOT_EQUAL, 107 | GDEXTENSION_VARIANT_OP_LESS, 108 | GDEXTENSION_VARIANT_OP_LESS_EQUAL, 109 | GDEXTENSION_VARIANT_OP_GREATER, 110 | GDEXTENSION_VARIANT_OP_GREATER_EQUAL, 111 | 112 | /* mathematic */ 113 | GDEXTENSION_VARIANT_OP_ADD, 114 | GDEXTENSION_VARIANT_OP_SUBTRACT, 115 | GDEXTENSION_VARIANT_OP_MULTIPLY, 116 | GDEXTENSION_VARIANT_OP_DIVIDE, 117 | GDEXTENSION_VARIANT_OP_NEGATE, 118 | GDEXTENSION_VARIANT_OP_POSITIVE, 119 | GDEXTENSION_VARIANT_OP_MODULE, 120 | GDEXTENSION_VARIANT_OP_POWER, 121 | 122 | /* bitwise */ 123 | GDEXTENSION_VARIANT_OP_SHIFT_LEFT, 124 | GDEXTENSION_VARIANT_OP_SHIFT_RIGHT, 125 | GDEXTENSION_VARIANT_OP_BIT_AND, 126 | GDEXTENSION_VARIANT_OP_BIT_OR, 127 | GDEXTENSION_VARIANT_OP_BIT_XOR, 128 | GDEXTENSION_VARIANT_OP_BIT_NEGATE, 129 | 130 | /* logic */ 131 | GDEXTENSION_VARIANT_OP_AND, 132 | GDEXTENSION_VARIANT_OP_OR, 133 | GDEXTENSION_VARIANT_OP_XOR, 134 | GDEXTENSION_VARIANT_OP_NOT, 135 | 136 | /* containment */ 137 | GDEXTENSION_VARIANT_OP_IN, 138 | GDEXTENSION_VARIANT_OP_MAX 139 | 140 | } GDExtensionVariantOperator; 141 | 142 | typedef void *GDExtensionVariantPtr; 143 | typedef const void *GDExtensionConstVariantPtr; 144 | typedef void *GDExtensionStringNamePtr; 145 | typedef const void *GDExtensionConstStringNamePtr; 146 | typedef void *GDExtensionStringPtr; 147 | typedef const void *GDExtensionConstStringPtr; 148 | typedef void *GDExtensionObjectPtr; 149 | typedef const void *GDExtensionConstObjectPtr; 150 | typedef void *GDExtensionTypePtr; 151 | typedef const void *GDExtensionConstTypePtr; 152 | typedef const void *GDExtensionMethodBindPtr; 153 | typedef int64_t GDExtensionInt; 154 | typedef uint8_t GDExtensionBool; 155 | typedef uint64_t GDObjectInstanceID; 156 | typedef void *GDExtensionRefPtr; 157 | typedef const void *GDExtensionConstRefPtr; 158 | 159 | /* VARIANT DATA I/O */ 160 | 161 | typedef enum { 162 | GDEXTENSION_CALL_OK, 163 | GDEXTENSION_CALL_ERROR_INVALID_METHOD, 164 | GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT, // Expected a different variant type. 165 | GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS, // Expected lower number of arguments. 166 | GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS, // Expected higher number of arguments. 167 | GDEXTENSION_CALL_ERROR_INSTANCE_IS_NULL, 168 | GDEXTENSION_CALL_ERROR_METHOD_NOT_CONST, // Used for const call. 169 | } GDExtensionCallErrorType; 170 | 171 | typedef struct { 172 | GDExtensionCallErrorType error; 173 | int32_t argument; 174 | int32_t expected; 175 | } GDExtensionCallError; 176 | 177 | typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionVariantPtr, GDExtensionTypePtr); 178 | typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionTypePtr, GDExtensionVariantPtr); 179 | typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result); 180 | typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count); 181 | typedef void (*GDExtensionPtrConstructor)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args); 182 | typedef void (*GDExtensionPtrDestructor)(GDExtensionTypePtr p_base); 183 | typedef void (*GDExtensionPtrSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_value); 184 | typedef void (*GDExtensionPtrGetter)(GDExtensionConstTypePtr p_base, GDExtensionTypePtr r_value); 185 | typedef void (*GDExtensionPtrIndexedSetter)(GDExtensionTypePtr p_base, GDExtensionInt p_index, GDExtensionConstTypePtr p_value); 186 | typedef void (*GDExtensionPtrIndexedGetter)(GDExtensionConstTypePtr p_base, GDExtensionInt p_index, GDExtensionTypePtr r_value); 187 | typedef void (*GDExtensionPtrKeyedSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionConstTypePtr p_value); 188 | typedef void (*GDExtensionPtrKeyedGetter)(GDExtensionConstTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionTypePtr r_value); 189 | typedef uint32_t (*GDExtensionPtrKeyedChecker)(GDExtensionConstVariantPtr p_base, GDExtensionConstVariantPtr p_key); 190 | typedef void (*GDExtensionPtrUtilityFunction)(GDExtensionTypePtr r_return, const GDExtensionConstTypePtr *p_args, int p_argument_count); 191 | 192 | typedef GDExtensionObjectPtr (*GDExtensionClassConstructor)(); 193 | 194 | typedef void *(*GDExtensionInstanceBindingCreateCallback)(void *p_token, void *p_instance); 195 | typedef void (*GDExtensionInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding); 196 | typedef GDExtensionBool (*GDExtensionInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDExtensionBool p_reference); 197 | 198 | typedef struct { 199 | GDExtensionInstanceBindingCreateCallback create_callback; 200 | GDExtensionInstanceBindingFreeCallback free_callback; 201 | GDExtensionInstanceBindingReferenceCallback reference_callback; 202 | } GDExtensionInstanceBindingCallbacks; 203 | 204 | /* EXTENSION CLASSES */ 205 | 206 | typedef void *GDExtensionClassInstancePtr; 207 | 208 | typedef GDExtensionBool (*GDExtensionClassSet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); 209 | typedef GDExtensionBool (*GDExtensionClassGet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); 210 | typedef uint64_t (*GDExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance); 211 | 212 | typedef struct { 213 | GDExtensionVariantType type; 214 | GDExtensionStringNamePtr name; 215 | GDExtensionStringNamePtr class_name; 216 | uint32_t hint; // Bitfield of `PropertyHint` (defined in `extension_api.json`). 217 | GDExtensionStringPtr hint_string; 218 | uint32_t usage; // Bitfield of `PropertyUsageFlags` (defined in `extension_api.json`). 219 | } GDExtensionPropertyInfo; 220 | 221 | typedef struct { 222 | GDExtensionStringNamePtr name; 223 | GDExtensionPropertyInfo return_value; 224 | uint32_t flags; // Bitfield of `GDExtensionClassMethodFlags`. 225 | int32_t id; 226 | 227 | /* Arguments: `default_arguments` is an array of size `argument_count`. */ 228 | uint32_t argument_count; 229 | GDExtensionPropertyInfo *arguments; 230 | 231 | /* Default arguments: `default_arguments` is an array of size `default_argument_count`. */ 232 | uint32_t default_argument_count; 233 | GDExtensionVariantPtr *default_arguments; 234 | } GDExtensionMethodInfo; 235 | 236 | typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); 237 | typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list); 238 | typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name); 239 | typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); 240 | typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); 241 | typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out); 242 | typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance); 243 | typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); 244 | typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); 245 | typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata); 246 | typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); 247 | typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name); 248 | 249 | typedef struct { 250 | GDExtensionBool is_virtual; 251 | GDExtensionBool is_abstract; 252 | GDExtensionClassSet set_func; 253 | GDExtensionClassGet get_func; 254 | GDExtensionClassGetPropertyList get_property_list_func; 255 | GDExtensionClassFreePropertyList free_property_list_func; 256 | GDExtensionClassPropertyCanRevert property_can_revert_func; 257 | GDExtensionClassPropertyGetRevert property_get_revert_func; 258 | GDExtensionClassNotification notification_func; 259 | GDExtensionClassToString to_string_func; 260 | GDExtensionClassReference reference_func; 261 | GDExtensionClassUnreference unreference_func; 262 | GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. 263 | GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. 264 | GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. 265 | GDExtensionClassGetRID get_rid_func; 266 | void *class_userdata; // Per-class user data, later accessible in instance bindings. 267 | } GDExtensionClassCreationInfo; 268 | 269 | typedef void *GDExtensionClassLibraryPtr; 270 | 271 | /* Method */ 272 | 273 | typedef enum { 274 | GDEXTENSION_METHOD_FLAG_NORMAL = 1, 275 | GDEXTENSION_METHOD_FLAG_EDITOR = 2, 276 | GDEXTENSION_METHOD_FLAG_CONST = 4, 277 | GDEXTENSION_METHOD_FLAG_VIRTUAL = 8, 278 | GDEXTENSION_METHOD_FLAG_VARARG = 16, 279 | GDEXTENSION_METHOD_FLAG_STATIC = 32, 280 | GDEXTENSION_METHOD_FLAGS_DEFAULT = GDEXTENSION_METHOD_FLAG_NORMAL, 281 | } GDExtensionClassMethodFlags; 282 | 283 | typedef enum { 284 | GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE, 285 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8, 286 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16, 287 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32, 288 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64, 289 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8, 290 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16, 291 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32, 292 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64, 293 | GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT, 294 | GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE 295 | } GDExtensionClassMethodArgumentMetadata; 296 | 297 | typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); 298 | typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); 299 | 300 | typedef struct { 301 | GDExtensionStringNamePtr name; 302 | void *method_userdata; 303 | GDExtensionClassMethodCall call_func; 304 | GDExtensionClassMethodPtrCall ptrcall_func; 305 | uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`. 306 | 307 | /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */ 308 | GDExtensionBool has_return_value; 309 | GDExtensionPropertyInfo *return_value_info; 310 | GDExtensionClassMethodArgumentMetadata return_value_metadata; 311 | 312 | /* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`. 313 | * Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. 314 | */ 315 | uint32_t argument_count; 316 | GDExtensionPropertyInfo *arguments_info; 317 | GDExtensionClassMethodArgumentMetadata *arguments_metadata; 318 | 319 | /* Default arguments: `default_arguments` is an array of size `default_argument_count`. */ 320 | uint32_t default_argument_count; 321 | GDExtensionVariantPtr *default_arguments; 322 | } GDExtensionClassMethodInfo; 323 | 324 | /* SCRIPT INSTANCE EXTENSION */ 325 | 326 | typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. 327 | 328 | typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); 329 | typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); 330 | typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); 331 | typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); 332 | typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid); 333 | 334 | typedef GDExtensionBool (*GDExtensionScriptInstancePropertyCanRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); 335 | typedef GDExtensionBool (*GDExtensionScriptInstancePropertyGetRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); 336 | 337 | typedef GDExtensionObjectPtr (*GDExtensionScriptInstanceGetOwner)(GDExtensionScriptInstanceDataPtr p_instance); 338 | typedef void (*GDExtensionScriptInstancePropertyStateAdd)(GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value, void *p_userdata); 339 | typedef void (*GDExtensionScriptInstanceGetPropertyState)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionScriptInstancePropertyStateAdd p_add_func, void *p_userdata); 340 | 341 | typedef const GDExtensionMethodInfo *(*GDExtensionScriptInstanceGetMethodList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); 342 | typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list); 343 | 344 | typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); 345 | 346 | typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); 347 | typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); 348 | typedef void (*GDExtensionScriptInstanceToString)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); 349 | 350 | typedef void (*GDExtensionScriptInstanceRefCountIncremented)(GDExtensionScriptInstanceDataPtr p_instance); 351 | typedef GDExtensionBool (*GDExtensionScriptInstanceRefCountDecremented)(GDExtensionScriptInstanceDataPtr p_instance); 352 | 353 | typedef GDExtensionObjectPtr (*GDExtensionScriptInstanceGetScript)(GDExtensionScriptInstanceDataPtr p_instance); 354 | typedef GDExtensionBool (*GDExtensionScriptInstanceIsPlaceholder)(GDExtensionScriptInstanceDataPtr p_instance); 355 | 356 | typedef void *GDExtensionScriptLanguagePtr; 357 | 358 | typedef GDExtensionScriptLanguagePtr (*GDExtensionScriptInstanceGetLanguage)(GDExtensionScriptInstanceDataPtr p_instance); 359 | 360 | typedef void (*GDExtensionScriptInstanceFree)(GDExtensionScriptInstanceDataPtr p_instance); 361 | 362 | typedef void *GDExtensionScriptInstancePtr; // Pointer to ScriptInstance. 363 | 364 | typedef struct { 365 | GDExtensionScriptInstanceSet set_func; 366 | GDExtensionScriptInstanceGet get_func; 367 | GDExtensionScriptInstanceGetPropertyList get_property_list_func; 368 | GDExtensionScriptInstanceFreePropertyList free_property_list_func; 369 | 370 | GDExtensionScriptInstancePropertyCanRevert property_can_revert_func; 371 | GDExtensionScriptInstancePropertyGetRevert property_get_revert_func; 372 | 373 | GDExtensionScriptInstanceGetOwner get_owner_func; 374 | GDExtensionScriptInstanceGetPropertyState get_property_state_func; 375 | 376 | GDExtensionScriptInstanceGetMethodList get_method_list_func; 377 | GDExtensionScriptInstanceFreeMethodList free_method_list_func; 378 | GDExtensionScriptInstanceGetPropertyType get_property_type_func; 379 | 380 | GDExtensionScriptInstanceHasMethod has_method_func; 381 | 382 | GDExtensionScriptInstanceCall call_func; 383 | GDExtensionScriptInstanceNotification notification_func; 384 | 385 | GDExtensionScriptInstanceToString to_string_func; 386 | 387 | GDExtensionScriptInstanceRefCountIncremented refcount_incremented_func; 388 | GDExtensionScriptInstanceRefCountDecremented refcount_decremented_func; 389 | 390 | GDExtensionScriptInstanceGetScript get_script_func; 391 | 392 | GDExtensionScriptInstanceIsPlaceholder is_placeholder_func; 393 | 394 | GDExtensionScriptInstanceSet set_fallback_func; 395 | GDExtensionScriptInstanceGet get_fallback_func; 396 | 397 | GDExtensionScriptInstanceGetLanguage get_language_func; 398 | 399 | GDExtensionScriptInstanceFree free_func; 400 | 401 | } GDExtensionScriptInstanceInfo; 402 | 403 | /* INTERFACE */ 404 | 405 | typedef struct { 406 | uint32_t version_major; 407 | uint32_t version_minor; 408 | uint32_t version_patch; 409 | const char *version_string; 410 | 411 | /* GODOT CORE */ 412 | 413 | void *(*mem_alloc)(size_t p_bytes); 414 | void *(*mem_realloc)(void *p_ptr, size_t p_bytes); 415 | void (*mem_free)(void *p_ptr); 416 | 417 | void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); 418 | void (*print_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); 419 | void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); 420 | void (*print_warning_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); 421 | void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); 422 | void (*print_script_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); 423 | 424 | uint64_t (*get_native_struct_size)(GDExtensionConstStringNamePtr p_name); 425 | 426 | /* GODOT VARIANT */ 427 | 428 | /* variant general */ 429 | void (*variant_new_copy)(GDExtensionVariantPtr r_dest, GDExtensionConstVariantPtr p_src); 430 | void (*variant_new_nil)(GDExtensionVariantPtr r_dest); 431 | void (*variant_destroy)(GDExtensionVariantPtr p_self); 432 | 433 | /* variant type */ 434 | void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); 435 | void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); 436 | void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid); 437 | void (*variant_set)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); 438 | void (*variant_set_named)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); 439 | void (*variant_set_keyed)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); 440 | void (*variant_set_indexed)(GDExtensionVariantPtr p_self, GDExtensionInt p_index, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid, GDExtensionBool *r_oob); 441 | void (*variant_get)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); 442 | void (*variant_get_named)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); 443 | void (*variant_get_keyed)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); 444 | void (*variant_get_indexed)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob); 445 | GDExtensionBool (*variant_iter_init)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid); 446 | GDExtensionBool (*variant_iter_next)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid); 447 | void (*variant_iter_get)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); 448 | GDExtensionInt (*variant_hash)(GDExtensionConstVariantPtr p_self); 449 | GDExtensionInt (*variant_recursive_hash)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_recursion_count); 450 | GDExtensionBool (*variant_hash_compare)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_other); 451 | GDExtensionBool (*variant_booleanize)(GDExtensionConstVariantPtr p_self); 452 | void (*variant_duplicate)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep); 453 | void (*variant_stringify)(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret); 454 | 455 | GDExtensionVariantType (*variant_get_type)(GDExtensionConstVariantPtr p_self); 456 | GDExtensionBool (*variant_has_method)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_method); 457 | GDExtensionBool (*variant_has_member)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); 458 | GDExtensionBool (*variant_has_key)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid); 459 | void (*variant_get_type_name)(GDExtensionVariantType p_type, GDExtensionStringPtr r_name); 460 | GDExtensionBool (*variant_can_convert)(GDExtensionVariantType p_from, GDExtensionVariantType p_to); 461 | GDExtensionBool (*variant_can_convert_strict)(GDExtensionVariantType p_from, GDExtensionVariantType p_to); 462 | 463 | /* ptrcalls */ 464 | GDExtensionVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDExtensionVariantType p_type); 465 | GDExtensionTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDExtensionVariantType p_type); 466 | GDExtensionPtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b); 467 | GDExtensionPtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash); 468 | GDExtensionPtrConstructor (*variant_get_ptr_constructor)(GDExtensionVariantType p_type, int32_t p_constructor); 469 | GDExtensionPtrDestructor (*variant_get_ptr_destructor)(GDExtensionVariantType p_type); 470 | void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error); 471 | GDExtensionPtrSetter (*variant_get_ptr_setter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); 472 | GDExtensionPtrGetter (*variant_get_ptr_getter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); 473 | GDExtensionPtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDExtensionVariantType p_type); 474 | GDExtensionPtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDExtensionVariantType p_type); 475 | GDExtensionPtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDExtensionVariantType p_type); 476 | GDExtensionPtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDExtensionVariantType p_type); 477 | GDExtensionPtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDExtensionVariantType p_type); 478 | void (*variant_get_constant_value)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionVariantPtr r_ret); 479 | GDExtensionPtrUtilityFunction (*variant_get_ptr_utility_function)(GDExtensionConstStringNamePtr p_function, GDExtensionInt p_hash); 480 | 481 | /* extra utilities */ 482 | void (*string_new_with_latin1_chars)(GDExtensionStringPtr r_dest, const char *p_contents); 483 | void (*string_new_with_utf8_chars)(GDExtensionStringPtr r_dest, const char *p_contents); 484 | void (*string_new_with_utf16_chars)(GDExtensionStringPtr r_dest, const char16_t *p_contents); 485 | void (*string_new_with_utf32_chars)(GDExtensionStringPtr r_dest, const char32_t *p_contents); 486 | void (*string_new_with_wide_chars)(GDExtensionStringPtr r_dest, const wchar_t *p_contents); 487 | void (*string_new_with_latin1_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); 488 | void (*string_new_with_utf8_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); 489 | void (*string_new_with_utf16_chars_and_len)(GDExtensionStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size); 490 | void (*string_new_with_utf32_chars_and_len)(GDExtensionStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size); 491 | void (*string_new_with_wide_chars_and_len)(GDExtensionStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size); 492 | /* Information about the following functions: 493 | * - The return value is the resulting encoded string length. 494 | * - The length returned is in characters, not in bytes. It also does not include a trailing zero. 495 | * - These functions also do not write trailing zero, If you need it, write it yourself at the position indicated by the length (and make sure to allocate it). 496 | * - Passing NULL in r_text means only the length is computed (again, without including trailing zero). 497 | * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL. 498 | * - p_max_write_length argument does not affect the return value, it's only to cap write length. 499 | */ 500 | GDExtensionInt (*string_to_latin1_chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length); 501 | GDExtensionInt (*string_to_utf8_chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length); 502 | GDExtensionInt (*string_to_utf16_chars)(GDExtensionConstStringPtr p_self, char16_t *r_text, GDExtensionInt p_max_write_length); 503 | GDExtensionInt (*string_to_utf32_chars)(GDExtensionConstStringPtr p_self, char32_t *r_text, GDExtensionInt p_max_write_length); 504 | GDExtensionInt (*string_to_wide_chars)(GDExtensionConstStringPtr p_self, wchar_t *r_text, GDExtensionInt p_max_write_length); 505 | char32_t *(*string_operator_index)(GDExtensionStringPtr p_self, GDExtensionInt p_index); 506 | const char32_t *(*string_operator_index_const)(GDExtensionConstStringPtr p_self, GDExtensionInt p_index); 507 | 508 | void (*string_operator_plus_eq_string)(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b); 509 | void (*string_operator_plus_eq_char)(GDExtensionStringPtr p_self, char32_t p_b); 510 | void (*string_operator_plus_eq_cstr)(GDExtensionStringPtr p_self, const char *p_b); 511 | void (*string_operator_plus_eq_wcstr)(GDExtensionStringPtr p_self, const wchar_t *p_b); 512 | void (*string_operator_plus_eq_c32str)(GDExtensionStringPtr p_self, const char32_t *p_b); 513 | 514 | /* XMLParser extra utilities */ 515 | 516 | GDExtensionInt (*xml_parser_open_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size); 517 | 518 | /* FileAccess extra utilities */ 519 | 520 | void (*file_access_store_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length); 521 | uint64_t (*file_access_get_buffer)(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length); 522 | 523 | /* WorkerThreadPool extra utilities */ 524 | 525 | int64_t (*worker_thread_pool_add_native_group_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description); 526 | int64_t (*worker_thread_pool_add_native_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description); 527 | 528 | /* Packed array functions */ 529 | 530 | uint8_t *(*packed_byte_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray 531 | const uint8_t *(*packed_byte_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray 532 | 533 | GDExtensionTypePtr (*packed_color_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedColorArray, returns Color ptr 534 | GDExtensionTypePtr (*packed_color_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedColorArray, returns Color ptr 535 | 536 | float *(*packed_float32_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat32Array 537 | const float *(*packed_float32_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat32Array 538 | double *(*packed_float64_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat64Array 539 | const double *(*packed_float64_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat64Array 540 | 541 | int32_t *(*packed_int32_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array 542 | const int32_t *(*packed_int32_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array 543 | int64_t *(*packed_int64_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array 544 | const int64_t *(*packed_int64_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array 545 | 546 | GDExtensionStringPtr (*packed_string_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedStringArray 547 | GDExtensionStringPtr (*packed_string_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedStringArray 548 | 549 | GDExtensionTypePtr (*packed_vector2_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr 550 | GDExtensionTypePtr (*packed_vector2_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr 551 | GDExtensionTypePtr (*packed_vector3_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr 552 | GDExtensionTypePtr (*packed_vector3_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr 553 | 554 | GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr 555 | GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr 556 | void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr 557 | void (*array_set_typed)(GDExtensionTypePtr p_self, GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr 558 | 559 | /* Dictionary functions */ 560 | 561 | GDExtensionVariantPtr (*dictionary_operator_index)(GDExtensionTypePtr p_self, GDExtensionConstVariantPtr p_key); // p_self should be an Dictionary ptr 562 | GDExtensionVariantPtr (*dictionary_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key); // p_self should be an Dictionary ptr 563 | 564 | /* OBJECT */ 565 | 566 | void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error); 567 | void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); 568 | void (*object_destroy)(GDExtensionObjectPtr p_o); 569 | GDExtensionObjectPtr (*global_get_singleton)(GDExtensionConstStringNamePtr p_name); 570 | 571 | void *(*object_get_instance_binding)(GDExtensionObjectPtr p_o, void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks); 572 | void (*object_set_instance_binding)(GDExtensionObjectPtr p_o, void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks); 573 | 574 | void (*object_set_instance)(GDExtensionObjectPtr p_o, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ 575 | 576 | GDExtensionObjectPtr (*object_cast_to)(GDExtensionConstObjectPtr p_object, void *p_class_tag); 577 | GDExtensionObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); 578 | GDObjectInstanceID (*object_get_instance_id)(GDExtensionConstObjectPtr p_object); 579 | 580 | /* REFERENCE */ 581 | 582 | GDExtensionObjectPtr (*ref_get_object)(GDExtensionConstRefPtr p_ref); 583 | void (*ref_set_object)(GDExtensionRefPtr p_ref, GDExtensionObjectPtr p_object); 584 | 585 | /* SCRIPT INSTANCE */ 586 | 587 | GDExtensionScriptInstancePtr (*script_instance_create)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); 588 | 589 | /* CLASSDB */ 590 | 591 | GDExtensionObjectPtr (*classdb_construct_object)(GDExtensionConstStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */ 592 | GDExtensionMethodBindPtr (*classdb_get_method_bind)(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash); 593 | void *(*classdb_get_class_tag)(GDExtensionConstStringNamePtr p_classname); 594 | 595 | /* CLASSDB EXTENSION */ 596 | 597 | /* Provided parameters for `classdb_register_extension_*` can be safely freed once the function returns. */ 598 | void (*classdb_register_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); 599 | void (*classdb_register_extension_class_method)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); 600 | void (*classdb_register_extension_class_integer_constant)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); 601 | void (*classdb_register_extension_class_property)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); 602 | void (*classdb_register_extension_class_property_group)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix); 603 | void (*classdb_register_extension_class_property_subgroup)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_subgroup_name, GDExtensionConstStringPtr p_prefix); 604 | void (*classdb_register_extension_class_signal)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count); 605 | void (*classdb_unregister_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ 606 | 607 | void (*get_library_path)(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path); 608 | 609 | } GDExtensionInterface; 610 | 611 | /* INITIALIZATION */ 612 | 613 | typedef enum { 614 | GDEXTENSION_INITIALIZATION_CORE, 615 | GDEXTENSION_INITIALIZATION_SERVERS, 616 | GDEXTENSION_INITIALIZATION_SCENE, 617 | GDEXTENSION_INITIALIZATION_EDITOR, 618 | GDEXTENSION_MAX_INITIALIZATION_LEVEL, 619 | } GDExtensionInitializationLevel; 620 | 621 | typedef struct { 622 | /* Minimum initialization level required. 623 | * If Core or Servers, the extension needs editor or game restart to take effect */ 624 | GDExtensionInitializationLevel minimum_initialization_level; 625 | /* Up to the user to supply when initializing */ 626 | void *userdata; 627 | /* This function will be called multiple times for each initialization level. */ 628 | void (*initialize)(void *userdata, GDExtensionInitializationLevel p_level); 629 | void (*deinitialize)(void *userdata, GDExtensionInitializationLevel p_level); 630 | } GDExtensionInitialization; 631 | 632 | /* Define a C function prototype that implements the function below and expose it to dlopen() (or similar). 633 | * This is the entry point of the GDExtension library and will be called on initialization. 634 | * It can be used to set up different init levels, which are called during various stages of initialization/shutdown. 635 | * The function name must be a unique one specified in the .gdextension config file. 636 | */ 637 | typedef GDExtensionBool (*GDExtensionInitializationFunction)(const GDExtensionInterface *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization); 638 | 639 | #ifdef __cplusplus 640 | } 641 | #endif 642 | 643 | #endif // GDEXTENSION_INTERFACE_H 644 | -------------------------------------------------------------------------------- /examples/register_custom_type.nim: -------------------------------------------------------------------------------- 1 | import nimrodot 2 | import nimrodot/classdb 3 | import nimrodot/builtins/variant 4 | import nimrodot/builtins/stringname 5 | import nimrodot/builtins/"string" 6 | import nimrodot/classes/node 7 | import nimrodot/classes/refcounted 8 | 9 | import std/options 10 | import std/typetraits 11 | 12 | type 13 | # Custom objects *must* be "ptr object". If no "of ..." is specified, 14 | # a default of "of Object" is assumed. The parent class may be another 15 | # custom class, but the inheritance chain of custom classes *must* start 16 | # with any Godot class. 17 | ExportedType {.customClass.} = ptr object of RefCounted 18 | someField: int 19 | 20 | # Likewise: {.classEnum.} 21 | SomeFlags {.classBitfield: ExportedType.} = enum 22 | Flag_A = 1 23 | Flag_B = 2 24 | Flag_C = 4 25 | Flag_D = 8 26 | 27 | # Unnamed constants 28 | const 29 | FOO_CONST* {.constant: ExportedType.} = 3 30 | BAR_CONST* {.constant: ExportedType.} = 5 31 | 32 | # Signals are registered as annotated prototypes 33 | proc something_happened(wasItImportant: bool) {.signal: ExportedType.} 34 | proc something_else_happened() {.signal: ExportedType.} 35 | 36 | # Virtuals are "implemented" by overriding the naked vtable for now 37 | var vtable: pointerBase (typeOf (gdVTablePointer(Node))) = gdVTablePointer(Node)[] 38 | 39 | # Override _process for example 40 | vtable.v_process = proc(self: Node; delta: float64) = 41 | discard 42 | 43 | # Needed internally 44 | proc gdVTablePointer*(_: typedesc[ExportedType]): auto = unsafeAddr vtable 45 | 46 | 47 | # Invoked by Godot on instance creation 48 | proc initType(self: ExportedType) {.ctor.} = 49 | self.someField = 1 50 | 51 | # Invoked by Godot on instance destruction 52 | proc destroyType(self: ExportedType) {.dtor.} = 53 | discard 54 | 55 | 56 | # Default parameters supported, as well as varargs[Variant] 57 | proc do_something*(self: ExportedType; param_a: String, param_b, param_c: int = 0): int16 {.classMethod.} = 58 | # ... 59 | 42 60 | 61 | # Methods for properties 62 | proc set_field(self: ExportedType; newVal: int) {.classMethod.} = 63 | self.someField = newVal 64 | 65 | proc get_field(self: ExportedType): int {.classMethod.} = 66 | self.someField 67 | 68 | # Static method, renamed via {.name.}. 69 | proc call_stat(a: int) {.staticMethod: ExportedType, name: "static_method".} = 70 | discard 71 | 72 | # Called by Godot to retrieve a default value for a given property. 73 | proc queryRevert(self: ExportedType; property: StringName): Option[Variant] {.revertQuery.} = 74 | if property == StringName("something"): 75 | some %true 76 | else: 77 | none Variant 78 | 79 | # Called by Godot to retrieve a list of all properties 80 | proc queryProperties(self: ExportedType; properties: var seq[GDExtensionPropertyInfo]) {.propertyQuery.} = 81 | addPropertyInfo[bool](properties, "something") 82 | 83 | # Dynamic properties, called for any non statically registered properties 84 | proc getProperty(self: ExportedType; name: StringName): Option[Variant] {.getProperty.} = 85 | # Discard 86 | none Variant 87 | 88 | proc setProperty(self: ExportedType; name: StringName; value: Variant): bool {.setProperty.} = 89 | discard 90 | 91 | # Notification handler 92 | proc handleNotification(self: ExportedType; what: int) {.notification.} = 93 | discard 94 | 95 | # Main extension entry point. May be called in multiple fashions: 96 | # - godotHooks() 97 | # - godotHooks(initLevel) 98 | # - godotHooks(symbolName) 99 | # - godotHooks(symbolName, initLevel) 100 | # 101 | # The symbol name controls how the GDExtension entry point is called. This is what needs to be 102 | # specified in the .gdextension file for Godot. 103 | # 104 | # The init level tells Godot how "far" to initialize the extension and can be one of the following: 105 | # - GDEXTENSION_INITIALIZATION_CORE 106 | # - GDEXTENSION_INITIALIZATION_SERVERS 107 | # - GDEXTENSION_INITIALIZATION_SCENE 108 | # - GDEXTENSION_INITIALIZATION_EDITOR 109 | # - GDEXTENSION_MAX_INITIALIZATION_LEVEL (same as GDEXTENSION_INITIALIZATION_EDITOR) 110 | # 111 | # The "initialize" and "deinitialize" hooks will be invoked once per level. 112 | # 113 | # Parameters left out assume these default values: 114 | # - initLevel: GDEXTENSION_MAX_INITIALIZATION_LEVEL 115 | # - symbolName: gdext_init 116 | godotHooks(GDEXTENSION_INITIALIZATION_SCENE): 117 | initialize(level): 118 | echo "Hello World from Godot @ " & $level 119 | 120 | if level == GDEXTENSION_INITIALIZATION_SCENE: 121 | classdb.register() 122 | 123 | # Missing higher level wrappers for static properties for now, so we manually 124 | # register them. here. 125 | classdb.registerPropertyGroup[ExportedType]("General Options", "gen_") 126 | classdb.registerProperty[ExportedType, int]("gen_field", "set_field", "get_field") 127 | 128 | deinitialize(level): 129 | echo "Bye World from Godot!" & $level 130 | 131 | -------------------------------------------------------------------------------- /nimrodot.nim: -------------------------------------------------------------------------------- 1 | import std/macros 2 | 3 | proc NimMain() {.cdecl, importc.} 4 | 5 | import nimrodot/ffi 6 | import nimrodot/interface_ptrs 7 | import nimrodot/ref_helper 8 | import nimrodot/classes/types/"object" 9 | import nimrodot/utils 10 | 11 | export ref_helper 12 | export ffi 13 | export interface_ptrs 14 | export utils 15 | 16 | proc getSingleton*[T: Object](name: string): T = 17 | var name = &name 18 | 19 | T(opaque: gdInterfacePtr.global_get_singleton(addr name)) 20 | 21 | macro godotHooks*( 22 | name: static[string]; 23 | level: static[GDExtensionInitializationLevel]; 24 | def: untyped) = 25 | 26 | let initIdent = name.ident() 27 | let globIdentIf = "gdInterfacePtr".ident() 28 | let globIdentTk = "gdTokenPtr".ident() 29 | 30 | def.expectKind(nnkStmtList) 31 | def[0].expectKind(nnkCall) 32 | def[1].expectKind(nnkCall) 33 | 34 | def[0][0].expectIdent("initialize") 35 | def[1][0].expectIdent("deinitialize") 36 | 37 | let initLevelParam = def[0][1] 38 | let initBody = def[0][2] 39 | 40 | let deinitLevelParam = def[1][1] 41 | let deinitBody = def[1][2] 42 | 43 | result = quote do: 44 | proc gdInit(userdata: pointer; `initLevelParam`: GDExtensionInitializationLevel) {.cdecl.} = 45 | `initBody` 46 | 47 | proc gdDeinit(userdata: pointer; `deinitLevelParam`: GDExtensionInitializationLevel) {.cdecl.} = 48 | `deinitBody` 49 | 50 | proc `initIdent`*( 51 | interf: ptr GDExtensionInterface; 52 | library: GDExtensionClassLibraryPtr; 53 | init: ptr GDExtensionInitialization): GDExtensionBool {.exportc, dynlib, cdecl.} = 54 | 55 | `globIdentIf` = interf 56 | `globIdentTk` = library 57 | 58 | NimMain() 59 | 60 | init.minimum_initialization_level = GDExtensionInitializationLevel(`level`) 61 | init.initialize = gdInit 62 | init.deinitialize = gdDeinit 63 | 64 | result = 1 65 | 66 | template godotHooks*(name: static[string]; def: untyped) = 67 | godotHooks(name, GDEXTENSION_MAX_INITIALIZATION_LEVEL, def) 68 | 69 | template godotHooks*(level: static[GDExtensionInitializationLevel]; def: untyped) = 70 | godotHooks("gdext_init", level, def) 71 | 72 | template godotHooks*(def: untyped) = 73 | godotHooks("gdext_init", GDEXTENSION_MAX_INITIALIZATION_LEVEL, def) 74 | 75 | -------------------------------------------------------------------------------- /nimrodot.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "chmod222" 5 | description = "Nim GDExtension Bindings" 6 | license = "MIT" 7 | 8 | # Dependencies 9 | 10 | requires "nim >= 1.9.3" 11 | 12 | task generateApi, "Generate the Godot API interface": 13 | exec("nim r bindgen/bindgen.nim") 14 | 15 | before install: 16 | generateApiTask() 17 | -------------------------------------------------------------------------------- /nimrodot/builtins/types/typedarray.nim: -------------------------------------------------------------------------------- 1 | # Builtin Class TypedArray[T] 2 | import ../../ffi 3 | import ../types/"array" 4 | 5 | type 6 | # For now 7 | TypedArray*[T] = Array 8 | 9 | func variantTypeId*(_: typedesc[Array]): GDExtensionVariantType = 10 | cast[GDExtensionVariantType](28) 11 | -------------------------------------------------------------------------------- /nimrodot/builtins/types/variant.nim: -------------------------------------------------------------------------------- 1 | # Builtin Class Variant (defined manually) 2 | import ../../ffi 3 | import ../../api 4 | 5 | import ../../interface_ptrs 6 | 7 | type 8 | Variant* = object 9 | opaque: array[variantSize, byte] 10 | 11 | proc `=destroy`(v: var Variant) = 12 | gdInterfacePtr.variant_destroy(unsafeAddr v) 13 | 14 | proc `=copy`(dest: var Variant; source: Variant) = 15 | `=destroy`(dest) 16 | dest.wasMoved() 17 | 18 | gdInterfacePtr.variant_new_copy(addr dest, unsafeAddr source) 19 | 20 | # Enums 21 | type 22 | Type* {.size: sizeof(cint).} = enum 23 | vtyNil = GDEXTENSION_VARIANT_TYPE_NIL, 24 | vtyBool = GDEXTENSION_VARIANT_TYPE_BOOL, 25 | vtyInt = GDEXTENSION_VARIANT_TYPE_INT, 26 | vtyFloat = GDEXTENSION_VARIANT_TYPE_FLOAT, 27 | vtyString = GDEXTENSION_VARIANT_TYPE_STRING, 28 | vtyVector2 = GDEXTENSION_VARIANT_TYPE_VECTOR2, 29 | vtyVector2i = GDEXTENSION_VARIANT_TYPE_VECTOR2I, 30 | vtyRect2 = GDEXTENSION_VARIANT_TYPE_RECT2, 31 | vtyRect2i = GDEXTENSION_VARIANT_TYPE_RECT2I, 32 | vtyVector3 = GDEXTENSION_VARIANT_TYPE_VECTOR3, 33 | vtyVector3i = GDEXTENSION_VARIANT_TYPE_VECTOR3I, 34 | vtyTransform2d = GDEXTENSION_VARIANT_TYPE_TRANSFORM2D, 35 | vtyVector4 = GDEXTENSION_VARIANT_TYPE_VECTOR4, 36 | vtyVector4i = GDEXTENSION_VARIANT_TYPE_VECTOR4I, 37 | vtyPlane = GDEXTENSION_VARIANT_TYPE_PLANE, 38 | vtyQuaternion = GDEXTENSION_VARIANT_TYPE_QUATERNION, 39 | vtyAABB = GDEXTENSION_VARIANT_TYPE_AABB, 40 | vtyBasis = GDEXTENSION_VARIANT_TYPE_BASIS, 41 | vtyTransform3D = GDEXTENSION_VARIANT_TYPE_TRANSFORM3D, 42 | vtyProjection = GDEXTENSION_VARIANT_TYPE_PROJECTION, 43 | vtyColor = GDEXTENSION_VARIANT_TYPE_COLOR, 44 | vtyStringName = GDEXTENSION_VARIANT_TYPE_STRING_NAME, 45 | vtyNodePath = GDEXTENSION_VARIANT_TYPE_NODE_PATH, 46 | vtyRID = GDEXTENSION_VARIANT_TYPE_RID, 47 | vtyObject = GDEXTENSION_VARIANT_TYPE_OBJECT, 48 | vtyCallable = GDEXTENSION_VARIANT_TYPE_CALLABLE, 49 | vtySignal = GDEXTENSION_VARIANT_TYPE_SIGNAL, 50 | vtyDictionary = GDEXTENSION_VARIANT_TYPE_DICTIONARY, 51 | vtyArray = GDEXTENSION_VARIANT_TYPE_ARRAY, 52 | vtyPackedByteArray = GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY, 53 | vtyPackedInt32Array = GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY, 54 | vtyPackedInt64Array = GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY, 55 | vtyPackedFloat32Array = GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY, 56 | vtyPackedFloat64Array = GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY, 57 | vtyPackedStringArray = GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY, 58 | vtyPackedVector2Array = GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY, 59 | vtyPackedVector3Array = GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY, 60 | vtyPackedColorArray = GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY 61 | 62 | CallErrorType* {.size: sizeof(cint).} = enum 63 | vceOk = GDEXTENSION_CALL_OK, 64 | vceInvalidMethod = GDEXTENSION_CALL_ERROR_INVALID_METHOD, 65 | vceInvalidArgument = GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT, 66 | vceTooManyArguments = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS, 67 | vceTooFewArguments = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS, 68 | vceInstanceIsNull = GDEXTENSION_CALL_ERROR_INSTANCE_IS_NULL, 69 | vceMethodNotConst = GDEXTENSION_CALL_ERROR_METHOD_NOT_CONST 70 | 71 | func variantTypeId*(_: typedesc[Variant]): GDExtensionVariantType = 72 | GDEXTENSION_VARIANT_TYPE_NIL 73 | 74 | func variantTypeId*(_: typedesc[SomeInteger]): GDExtensionVariantType = 75 | GDEXTENSION_VARIANT_TYPE_INT 76 | 77 | func variantTypeId*(_: typedesc[SomeFloat]): GDExtensionVariantType = 78 | GDEXTENSION_VARIANT_TYPE_FLOAT 79 | 80 | func variantTypeId*(_: typedesc[bool]): GDExtensionVariantType = 81 | GDEXTENSION_VARIANT_TYPE_BOOL 82 | 83 | import ../../classes/types/"object" 84 | import ../../ref_helper 85 | 86 | type 87 | AnyObject* = concept var t 88 | t of Object 89 | 90 | func variantTypeId*[T: AnyObject](_: typedesc[T]): GDExtensionVariantType = 91 | GDEXTENSION_VARIANT_TYPE_OBJECT 92 | 93 | func variantTypeId*[T: AnyObject](_: typedesc[Ref[T]]): GDExtensionVariantType = 94 | GDEXTENSION_VARIANT_TYPE_OBJECT 95 | 96 | # The variant module and all builtins already declare `variantTypeId` so that 97 | # we can map all manner of types into variants, but we also need to be able 98 | # to go back from a variant type ID into a specific binary type that we then 99 | # may downcast. 100 | 101 | import ../types 102 | import ../../ref_helper 103 | 104 | # Any numeric gets widened into the largest we could receive and then 105 | # casted back down, as we hope that Godot did respect our metadata. 106 | template mapBuiltinType*(_: typedesc[SomeInteger]): auto = int64 107 | template mapBuiltinType*(_: typedesc[SomeFloat]): auto = float64 108 | 109 | # Objects are all the same on the binary level (as far as GDExt is concerned) 110 | template mapBuiltinType*[T: AnyObject](_: typedesc[T]): auto = T 111 | template mapBuiltinType*[T: AnyObject](_: typedesc[Ref[T]]): auto = T 112 | 113 | # The builtins just map back to themselves 114 | template mapBuiltinType*(_: typedesc[bool]): auto = bool 115 | template mapBuiltinType*(_: typedesc[Variant]): auto = Variant 116 | 117 | template mapBuiltinType*(_: typedesc[Nil | void]): auto = Nil 118 | template mapBuiltinType*(_: typedesc[Signal]): auto = Signal 119 | template mapBuiltinType*(_: typedesc[Callable]): auto = Callable 120 | template mapBuiltinType*(_: typedesc[String]): auto = String 121 | template mapBuiltinType*(_: typedesc[Quaternion]): auto = Quaternion 122 | template mapBuiltinType*(_: typedesc[PackedFloat64Array]): auto = PackedFloat64Array 123 | template mapBuiltinType*(_: typedesc[Dictionary]): auto = Dictionary 124 | template mapBuiltinType*(_: typedesc[StringName]): auto = StringName 125 | template mapBuiltinType*(_: typedesc[Color]): auto = Color 126 | template mapBuiltinType*(_: typedesc[PackedStringArray]): auto = PackedStringArray 127 | template mapBuiltinType*(_: typedesc[Array]): auto = Array 128 | template mapBuiltinType*(_: typedesc[PackedInt32Array]): auto = PackedInt32Array 129 | template mapBuiltinType*(_: typedesc[Vector3i]): auto = Vector3i 130 | template mapBuiltinType*(_: typedesc[Basis]): auto = Basis 131 | template mapBuiltinType*(_: typedesc[NodePath]): auto = NodePath 132 | template mapBuiltinType*(_: typedesc[PackedFloat32Array]): auto = PackedFloat32Array 133 | template mapBuiltinType*(_: typedesc[RID]): auto = RID 134 | template mapBuiltinType*(_: typedesc[Vector2]): auto = Vector2 135 | template mapBuiltinType*(_: typedesc[Rect2i]): auto = Rect2i 136 | template mapBuiltinType*(_: typedesc[PackedVector2Array]): auto = PackedVector2Array 137 | template mapBuiltinType*(_: typedesc[AABB]): auto = AABB 138 | template mapBuiltinType*(_: typedesc[Vector4]): auto = Vector4 139 | template mapBuiltinType*(_: typedesc[Vector4i]): auto = Vector4i 140 | template mapBuiltinType*(_: typedesc[Nil]): auto = Nil 141 | template mapBuiltinType*(_: typedesc[Vector2i]): auto = Vector2i 142 | template mapBuiltinType*(_: typedesc[Plane]): auto = Plane 143 | template mapBuiltinType*(_: typedesc[Transform2D]): auto = Transform2D 144 | template mapBuiltinType*(_: typedesc[Transform3D]): auto = Transform3D 145 | template mapBuiltinType*(_: typedesc[Vector3]): auto = Vector3 146 | template mapBuiltinType*(_: typedesc[PackedColorArray]): auto = PackedColorArray 147 | template mapBuiltinType*(_: typedesc[PackedVector3Array]): auto = PackedVector3Array 148 | template mapBuiltinType*(_: typedesc[PackedByteArray]): auto = PackedByteArray 149 | template mapBuiltinType*(_: typedesc[Projection]): auto = Projection 150 | template mapBuiltinType*(_: typedesc[Rect2]): auto = Rect2 151 | template mapBuiltinType*(_: typedesc[PackedInt64Array]): auto = PackedInt64Array 152 | 153 | # If we did not hit any overload, there is a gap in our coverage and we must 154 | # handle that. 155 | #template mapBuiltinType*[T](_: typedesc[T]) = 156 | #{.error: "generic mapBuiltinType invoked: " & $type(T).} 157 | 158 | template maybeDowncast*[U](val: auto): U = 159 | # There's no harm to convert T to T, but it does get spammy with compiler hints 160 | when typeOf(val) is U: 161 | val 162 | elif U is Ref: 163 | # If our target type is a Ref, that means we got here via the overload 164 | # `[T: AnyObject](_: typedesc[Ref[T]])` and have to reverse it 165 | newRefShallow(val) 166 | else: 167 | U(val) -------------------------------------------------------------------------------- /nimrodot/builtins/variant.nim: -------------------------------------------------------------------------------- 1 | # Builtin Class Variant (Procs) [non-autogenerated] 2 | import std/sugar 3 | 4 | import ../interface_ptrs 5 | import ../ffi 6 | import ../utils 7 | import ./types/variant 8 | 9 | export variant 10 | 11 | import ./types 12 | 13 | type 14 | VariantCallException* = object of CatchableError 15 | code*: CallErrorType 16 | argument*: int32 17 | expected*: Type 18 | 19 | VariantCastException* = object of CatchableError 20 | source*: Type 21 | target*: Type 22 | 23 | VariantConvertException* = object of CatchableError 24 | source*: Type 25 | target*: Type 26 | 27 | proc newVariant*(): Variant = 28 | gdInterfacePtr.variant_new_nil(addr result) 29 | 30 | proc newVariant*(`from`: Variant): Variant = 31 | gdInterfacePtr.variant_new_copy(addr result, unsafeAddr `from`) 32 | 33 | import ../ref_helper 34 | 35 | proc newVariant*[T](`from`: T): Variant = 36 | when T is Variant: 37 | `from` 38 | elif T is Ref or T is AnyObject: 39 | var fromType {.global.} = gdInterfacePtr.get_variant_from_type_constructor( 40 | cast[GDExtensionVariantType](GDEXTENSION_VARIANT_TYPE_OBJECT)) 41 | 42 | # This works for both Ref[T] and naked objects, due to rc[]: Object (+ auto deref) 43 | # and Object[]: Object:Object without auto deref 44 | fromType(addr result, addr `from`[].opaque) 45 | else: 46 | type Bt = mapBuiltinType T 47 | 48 | var fromType {.global.} = gdInterfacePtr.get_variant_from_type_constructor( 49 | cast[GDExtensionVariantType](Bt.variantTypeId())) 50 | 51 | let fromCasted: Bt = maybeDowncast[Bt](`from`) 52 | 53 | fromType(addr result, unsafeAddr fromCasted) 54 | 55 | template `%`*(p: typed): Variant = p.newVariant() 56 | 57 | proc destroy*(self: sink Variant) = 58 | gdInterfacePtr.variant_destroy(unsafeAddr self) 59 | 60 | proc `$`*(variant: Variant): string = 61 | var gdString: String = default(String) 62 | 63 | gdInterfacePtr.variant_stringify(unsafeAddr variant, addr gdString) 64 | 65 | return $gdString 66 | 67 | # I apologize. 68 | proc raiseMeMaybe(callError: GDExtensionCallError) = 69 | if callError.error != GDEXTENSION_CALL_OK: 70 | var exc = newException(VariantCallException, "error during variant call") 71 | 72 | exc.code = CallErrorType(ord(callError.error)) 73 | exc.argument = callError.argument 74 | exc.expected = cast[Type](callError.expected) 75 | 76 | raise exc 77 | 78 | proc raiseIf[T](cond: GDExtensionBool; errorCond: bool; exc: typedesc[T]; message: string) = 79 | let got = cond == 1 80 | 81 | if got == errorCond: 82 | raise newException(T, message) 83 | 84 | proc call*(self: var Variant; meth: string; args: varargs[Variant, newVariant]): Variant = 85 | var callError: GDExtensionCallError 86 | 87 | var ffiArgs = collect(newSeqOfCap(len(args))): 88 | for i in 0..high(args): 89 | cast[GDExtensionConstVariantPtr](unsafeAddr args[i]) 90 | 91 | var methodName = &meth 92 | 93 | gdInterfacePtr.variant_call( 94 | cast[GDExtensionVariantPtr](addr self), 95 | cast[GDExtensionConstStringNamePtr](addr methodName), 96 | cast[ptr GDExtensionConstVariantPtr](if len(ffiArgs) > 0: addr ffiArgs[0] else: nil), 97 | len(ffiArgs), 98 | cast[GDExtensionVariantPtr](addr result), 99 | addr callError) 100 | 101 | callError.raiseMeMaybe() 102 | 103 | proc call*(ty: Type; meth: string; args: varargs[Variant, newVariant]): Variant = 104 | var callError: GDExtensionCallError 105 | 106 | var ffiArgs = collect(newSeqOfCap(len(args))): 107 | for i in 0..high(args): 108 | cast[GDExtensionConstVariantPtr](unsafeAddr args[i]) 109 | 110 | var methodName = &meth 111 | 112 | gdInterfacePtr.variant_call_static( 113 | cast[GDExtensionVariantType](ty), 114 | cast[GDExtensionConstStringNamePtr](addr methodName), 115 | cast[ptr GDExtensionConstVariantPtr](if len(ffiArgs) > 0: addr ffiArgs[0] else: nil), 116 | len(ffiArgs), 117 | cast[GDExtensionVariantPtr](addr result), 118 | addr callError) 119 | 120 | callError.raiseMeMaybe() 121 | 122 | proc get*(self: Variant; key: Variant): Variant = 123 | var isValid: GDExtensionBool 124 | 125 | gdInterfacePtr.variant_get( 126 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 127 | cast[GDExtensionConstVariantPtr](unsafeAddr key), 128 | cast[GDExtensionVariantPtr](addr result), 129 | addr isValid) 130 | 131 | isValid.raiseIf(false, ValueError, "invalid operation") 132 | 133 | proc get*(self: Variant; key: string): Variant = 134 | var isValid: GDExtensionBool 135 | 136 | var keyName = &key 137 | 138 | gdInterfacePtr.variant_get_named( 139 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 140 | cast[GDExtensionConstStringNamePtr](addr keyName), 141 | cast[GDExtensionVariantPtr](addr result), 142 | addr isValid) 143 | 144 | isValid.raiseIf(false, ValueError, "invalid operation") 145 | 146 | proc set*(self: var Variant; key, value: Variant) = 147 | var isValid: GDExtensionBool 148 | 149 | gdInterfacePtr.variant_set( 150 | cast[GDExtensionVariantPtr](addr self), 151 | cast[GDExtensionConstVariantPtr](unsafeAddr key), 152 | cast[GDExtensionConstVariantPtr](unsafeAddr value), 153 | addr isValid) 154 | 155 | isValid.raiseIf(false, ValueError, "invalid operation") 156 | 157 | proc set*(self: var Variant; key: string; value: Variant) = 158 | var isValid: GDExtensionBool 159 | 160 | var keyName = &key 161 | 162 | gdInterfacePtr.variant_set_named( 163 | cast[GDExtensionVariantPtr](addr self), 164 | cast[GDExtensionConstStringNamePtr](addr keyName), 165 | cast[GDExtensionConstVariantPtr](unsafeAddr value), 166 | addr isValid) 167 | 168 | isValid.raiseIf(false, ValueError, "invalid operation") 169 | 170 | proc `[]`*(self: Variant; idx: int64): Variant = 171 | var 172 | isValid: GDExtensionBool = 0 173 | isOOB: GDExtensionBool = 0 174 | 175 | gdInterfacePtr.variant_get_indexed( 176 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 177 | idx, 178 | cast[GDExtensionVariantPtr](addr result), 179 | addr isValid, 180 | addr isOob) 181 | 182 | isValid.raiseIf(false, ValueError, "invalid operation") 183 | isOOB.raiseIf(true, ValueError, "index out of bounds") 184 | 185 | proc `[]`*(self: Variant; key: Variant): Variant = 186 | var isValid: GDExtensionBool = 0 187 | 188 | gdInterfacePtr.variant_get_keyed( 189 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 190 | cast[GDExtensionConstVariantPtr](unsafeAddr key), 191 | cast[GDExtensionVariantPtr](addr result), 192 | addr isValid) 193 | 194 | isValid.raiseIf(false, ValueError, "invalid operation") 195 | 196 | proc `[]=`*(self: var Variant; idx: int64; value: Variant): Variant = 197 | var 198 | isValid: GDExtensionBool = 0 199 | isOOB: GDExtensionBool = 0 200 | 201 | gdInterfacePtr.variant_set_indexed( 202 | cast[GDExtensionVariantPtr](addr self), 203 | idx, 204 | cast[GDExtensionConstVariantPtr](unsafeAddr value), 205 | addr isValid, 206 | addr isOob) 207 | 208 | isValid.raiseIf(false, ValueError, "invalid operation") 209 | isOOB.raiseIf(true, ValueError, "index out of bounds") 210 | 211 | proc `[]=`*(self: var Variant; key, value: Variant): Variant = 212 | var isValid: GDExtensionBool = 0 213 | 214 | gdInterfacePtr.variant_set_keyed( 215 | cast[GDExtensionVariantPtr](addr self), 216 | cast[GDExtensionConstVariantPtr](unsafeAddr key), 217 | cast[GDExtensionConstVariantPtr](unsafeAddr value), 218 | addr isValid) 219 | 220 | isValid.raiseIf(false, ValueError, "invalid operation") 221 | 222 | proc hash*(self: Variant): int64 = 223 | gdInterfacePtr.variant_hash(unsafeAddr self) 224 | 225 | proc hash*(self: Variant; recursionCount: int64): int64 = 226 | gdInterfacePtr.variant_recursive_hash(unsafeAddr self, recursionCount) 227 | 228 | proc hashCompare*(a, b: Variant): bool = 229 | gdInterfacePtr.variant_hash_compare( 230 | cast[GDExtensionConstVariantPtr](unsafeAddr a), 231 | cast[GDExtensionConstVariantPtr](unsafeAddr b)) != 0 232 | 233 | converter booleanize*(self: Variant): bool = 234 | gdInterfacePtr.variant_booleanize(cast[GDExtensionConstVariantPtr](unsafeAddr self)) != 0 235 | 236 | proc duplicate*(self: Variant; deep: bool = true): Variant = 237 | gdInterfacePtr.variant_duplicate( 238 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 239 | cast[GDExtensionVariantPtr](addr result), 240 | cast[GDExtensionBool](deep)) 241 | 242 | proc getType*(self: Variant): Type = 243 | Type(int(gdInterfacePtr.variant_get_type( 244 | cast[GDExtensionConstVariantPtr](unsafeAddr self)))) 245 | 246 | proc hasMethod*(self: Variant; meth: string): bool = 247 | var methodName = &meth 248 | 249 | gdInterfacePtr.variant_has_method( 250 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 251 | cast[GDExtensionConstStringNamePtr](addr methodName)) != 0 252 | 253 | proc hasProperty*(ty: Type; prop: string): bool = 254 | var propName = &prop 255 | 256 | gdInterfacePtr.variant_has_member( 257 | cast[GDExtensionVariantType](ty), 258 | cast[GDExtensionConstStringNamePtr](addr propName)) != 0 259 | 260 | proc contains*(self: Variant; key: Variant): bool = 261 | var isValid: GDExtensionBool 262 | 263 | result = gdInterfacePtr.variant_has_key( 264 | cast[GDExtensionConstVariantPtr](unsafeAddr self), 265 | cast[GDExtensionConstVariantPtr](unsafeAddr key), 266 | addr isValid) != 0 267 | 268 | isValid.raiseIf(false, ValueError, "invalid operation") 269 | 270 | proc typeName*(ty: Type): String = 271 | gdInterfacePtr.variant_get_type_name( 272 | cast[GDExtensionVariantType](ty), 273 | cast[GDExtensionStringPtr](addr result)) 274 | 275 | proc canConvertTo*(a, b: Type; strict: bool = false): bool = 276 | if strict: 277 | gdInterfacePtr.variant_can_convert_strict( 278 | cast[GDExtensionVariantType](a), 279 | cast[GDExtensionVariantType](b)) != 0 280 | else: 281 | gdInterfacePtr.variant_can_convert( 282 | cast[GDExtensionVariantType](a), 283 | cast[GDExtensionVariantType](b)) != 0 284 | 285 | proc castTo*[T](self: Variant; _: typedesc[T]): T = 286 | when T is Variant: 287 | self 288 | else: 289 | type Bt = mapBuiltinType T 290 | 291 | if self.getType() != Type(Bt.variantTypeId.ord): 292 | var exc = newException(VariantCastException, 293 | "cannot cast variant of type " & $self.getType() & " to " & $Type(Bt.variantTypeId.ord)) 294 | 295 | exc.source = self.getType() 296 | exc.target = Type(Bt.variantTypeId.ord) 297 | 298 | raise exc 299 | 300 | 301 | let toType {.global.} = gdInterfacePtr.get_variant_to_type_constructor( 302 | cast[GDExtensionVariantType](Bt.variantTypeId())) 303 | 304 | var resultVarType: Bt 305 | 306 | toType( 307 | cast[GDExtensionTypePtr](addr resultVarType), 308 | cast[GDExtensionVariantPtr](unsafeAddr self)) 309 | 310 | return maybeDowncast[T](resultVarType) 311 | 312 | proc convertTo*[T](self: Variant; _: typedesc[T]; strict: bool = true): T = 313 | when T is Variant: 314 | self 315 | else: 316 | type Bt = mapBuiltinType T 317 | 318 | if not self.getType().canConvertTo(Type(Bt.variantTypeId.ord), strict): 319 | var exc = newException(VariantConvertException, 320 | "cannot convert variant of type " & $self.getType() & " to " & $Type(Bt.variantTypeId.ord)) 321 | 322 | exc.source = self.getType() 323 | exc.target = Type(Bt.variantTypeId.ord) 324 | 325 | raise exc 326 | 327 | var temp: Variant 328 | var args = [addr self] 329 | var error: GDExtensionCallError 330 | 331 | gdInterfacePtr.variant_construct( 332 | Bt.variantTypeId(), 333 | cast[GDExtensionVariantPtr](addr temp), 334 | cast[ptr GDExtensionVariantPtr](addr args), 335 | 1, 336 | addr error) 337 | 338 | error.raiseMeMaybe() 339 | 340 | return temp.castTo(T) 341 | 342 | # Needs testing 343 | iterator items*(self: Variant): Variant = 344 | let selfPtr = cast[GDExtensionConstVariantPtr](unsafeAddr self) 345 | 346 | var iter, item: Variant; 347 | var isValid: GDExtensionBool; 348 | 349 | discard gdInterfacePtr.variant_iter_init( 350 | selfPtr, 351 | cast[GDExtensionVariantPtr](addr iter), 352 | addr isValid) 353 | 354 | while isValid == 1: 355 | gdInterfacePtr.variant_iter_get(selfPtr, addr iter, addr item, addr isValid) 356 | 357 | yield self[item.castTo(int64)] 358 | 359 | if gdInterfacePtr.variant_iter_next(selfPtr, addr iter, addr isValid) == 0: 360 | break 361 | 362 | template invokeInner(operator: GDExtensionVariantOperator; a, b: untyped) = 363 | var isValid: GDExtensionBool; 364 | 365 | gdInterfacePtr.variant_evaluate(operator, 366 | cast[GDExtensionConstVariantPtr](a), 367 | cast[GDExtensionConstVariantPtr](b), 368 | cast[GDExtensionVariantPtr](addr result), 369 | addr isValid) 370 | 371 | isValid.raiseIf(false, ValueError, "invalid operation") 372 | 373 | template invoke(operator: GDExtensionVariantOperator; a, b: Variant) = 374 | operator.invokeInner(unsafeAddr a, unsafeAddr b) 375 | 376 | template invoke(operator: GDExtensionVariantOperator; a: Variant) = 377 | operator.invokeInner(unsafeAddr a, nil) 378 | 379 | 380 | proc `==`*(a, b: Variant): Variant = 381 | GDEXTENSION_VARIANT_OP_EQUAL.invoke(a, b) 382 | 383 | proc `!=`*(a, b: Variant): Variant = 384 | GDEXTENSION_VARIANT_OP_NOT_EQUAL.invoke(a, b) 385 | 386 | proc `<`*(a, b: Variant): Variant = 387 | GDEXTENSION_VARIANT_OP_LESS.invoke(a, b) 388 | 389 | proc `<=`*(a, b: Variant): Variant = 390 | GDEXTENSION_VARIANT_OP_LESS_EQUAL.invoke(a, b) 391 | 392 | proc `>`*(a, b: Variant): Variant = 393 | GDEXTENSION_VARIANT_OP_GREATER.invoke(a, b) 394 | 395 | proc `>=`*(a, b: Variant): Variant = 396 | GDEXTENSION_VARIANT_OP_GREATER_EQUAL.invoke(a, b) 397 | 398 | proc `+`*(a, b: Variant): Variant = 399 | GDEXTENSION_VARIANT_OP_ADD.invoke(a, b) 400 | 401 | proc `+`*(a: Variant): Variant = 402 | GDEXTENSION_VARIANT_OP_POSITIVE.invoke(a) 403 | 404 | proc `-`*(a, b: Variant): Variant = 405 | GDEXTENSION_VARIANT_OP_SUBTRACT.invoke(a, b) 406 | 407 | proc `-`*(a: Variant): Variant = 408 | GDEXTENSION_VARIANT_OP_NEGATE.invoke(a) 409 | 410 | proc `*`*(a, b: Variant): Variant = 411 | GDEXTENSION_VARIANT_OP_MULTIPLY.invoke(a, b) 412 | 413 | proc `/`*(a, b: Variant): Variant = 414 | GDEXTENSION_VARIANT_OP_DIVIDE.invoke(a, b) 415 | 416 | proc `%`*(a, b: Variant): Variant = 417 | GDEXTENSION_VARIANT_OP_MODULE.invoke(a, b) 418 | 419 | proc `**`*(a, b: Variant): Variant = 420 | GDEXTENSION_VARIANT_OP_POWER.invoke(a, b) 421 | 422 | proc `<<`*(a, b: Variant): Variant = 423 | GDEXTENSION_VARIANT_OP_SHIFT_LEFT.invoke(a, b) 424 | 425 | proc `>>`*(a, b: Variant): Variant = 426 | GDEXTENSION_VARIANT_OP_SHIFT_RIGHT.invoke(a, b) 427 | 428 | proc `&`*(a, b: Variant): Variant = 429 | GDEXTENSION_VARIANT_OP_BIT_AND.invoke(a, b) 430 | 431 | proc `|`*(a, b: Variant): Variant = 432 | GDEXTENSION_VARIANT_OP_BIT_OR.invoke(a, b) 433 | 434 | proc `^`*(a, b: Variant): Variant = 435 | GDEXTENSION_VARIANT_OP_BIT_XOR.invoke(a, b) 436 | 437 | proc `~`*(a: Variant): Variant = 438 | GDEXTENSION_VARIANT_OP_BIT_NEGATE.invoke(a) 439 | 440 | proc `and`*(a, b: Variant): Variant = 441 | GDEXTENSION_VARIANT_OP_AND.invoke(a, b) 442 | 443 | proc `or`*(a, b: Variant): Variant = 444 | GDEXTENSION_VARIANT_OP_OR.invoke(a, b) 445 | 446 | proc `xor`*(a, b: Variant): Variant = 447 | GDEXTENSION_VARIANT_OP_XOR.invoke(a, b) 448 | 449 | proc `not`*(a, b: Variant): Variant = 450 | GDEXTENSION_VARIANT_OP_NOT.invoke(a, b) 451 | 452 | proc `in`*(a, b: Variant): Variant = 453 | GDEXTENSION_VARIANT_OP_IN.invoke(a, b) -------------------------------------------------------------------------------- /nimrodot/classdb.nim: -------------------------------------------------------------------------------- 1 | import ../nimrodot 2 | 3 | import ./interface_ptrs 4 | import ./builtins/types 5 | import ./builtins/variant 6 | import ./builtins/"string" as str 7 | import ./classes/types/"object" 8 | import ./enums 9 | 10 | import std/[macros, genasts, tables, strutils, options, enumutils, typetraits, sugar] 11 | 12 | type 13 | ClassRegistration = object 14 | typeNode: NimNode 15 | parentNode: NimNode 16 | 17 | virtual: bool 18 | abstract: bool 19 | 20 | ctorFuncIdent: NimNode 21 | dtorFuncIdent: NimNode 22 | notificationHandlerIdent: NimNode 23 | revertQueryIdent: NimNode 24 | listPropertiesIdent: NimNode 25 | getPropertyIdent: NimNode 26 | setPropertyIdent: NimNode 27 | 28 | properties: OrderedTable[string, ClassProperty] 29 | methods: OrderedTable[string, MethodInfo] 30 | signals: OrderedTable[string, NimNode] 31 | 32 | enums: seq[EnumInfo] 33 | consts: seq[ConstInfo] 34 | 35 | ClassProperty = object 36 | setter: NimNode 37 | getter: NimNode 38 | 39 | MethodInfo = object 40 | symbol: NimNode 41 | defaultValues: seq[DefaultedArgument] 42 | virtual: bool = false 43 | 44 | DefaultedArgument = object 45 | binding: NimNode 46 | default: NimNode 47 | 48 | EnumInfo = object 49 | definition: NimNode 50 | isBitfield: bool 51 | 52 | ConstInfo = object 53 | name: NimNode 54 | value: int 55 | 56 | var classes* {.compileTime.} = initOrderedTable[string, ClassRegistration]() 57 | 58 | macro custom_class*(def: untyped) = 59 | def[0].expectKind(nnkPragmaExpr) 60 | def[0][0].expectKind(nnkIdent) 61 | 62 | def[2].expectKind(nnkPtrTy) 63 | def[2][0].expectKind(nnkObjectTy) 64 | 65 | # Unless specified otherwise, we derive from Godot's "Object" 66 | if def[2][0][1].kind == nnkEmpty: 67 | def[2][0][1] = newTree(nnkOfInherit, "Object".ident()) 68 | 69 | var 70 | abstract: bool = false 71 | virtual: bool = false 72 | 73 | for pragma in def[0][1]: 74 | if pragma.kind != nnkIdent: 75 | continue 76 | 77 | if pragma.strVal() == "gdvirtual": 78 | virtual = true 79 | elif pragma.strVal() == "abstract": 80 | abstract = true 81 | 82 | classes[def[0][0].strVal()] = ClassRegistration( 83 | typeNode: def[0][0], 84 | parentNode: def[2][0][1][0], 85 | 86 | virtual: virtual, 87 | abstract: abstract, 88 | 89 | ctorFuncIdent: newNilLit(), 90 | dtorFuncIdent: newNilLit(), 91 | notificationHandlerIdent: newNilLit(), 92 | revertQueryIdent: newNilLit(), 93 | listPropertiesIdent: newNilLit(), 94 | getPropertyIdent: newNilLit(), 95 | setPropertyIdent: newNilLit(), 96 | 97 | enums: @[]) 98 | 99 | if def[2][0][1][0].strVal() notin classes: 100 | # If we are deriving from a Godot class as opposed to one of our own, 101 | # we add in a field to store our runtime class information into. 102 | def[2][0][2] &= newIdentDefs("gdclassinfo".ident(), "pointer".ident()) 103 | 104 | def 105 | 106 | # Unfortunately, "virtual" is already taken 107 | template gdvirtual*() {.pragma.} 108 | template abstract*() {.pragma.} 109 | template name*(rename: string) {.pragma.} 110 | 111 | template expectClassReceiverProc(def: typed) = 112 | ## Helper function to assert that a proc definition with `x: T` as the 113 | ## first parameter has been provided. 114 | def.expectKind(nnkProcDef) 115 | def[3][1][^2].expectKind(nnkSym) 116 | 117 | template className(def: typed): string = 118 | def[3][1][^2].strVal() 119 | 120 | macro ctor*(def: typed) = 121 | def.expectClassReceiverProc() 122 | 123 | classes[def.className].ctorFuncIdent = def[0] 124 | 125 | def 126 | 127 | macro dtor*(def: typed) = 128 | def.expectClassReceiverProc() 129 | 130 | classes[def.className].dtorFuncIdent = def[0] 131 | 132 | def 133 | 134 | macro classMethod*(def: typed) = 135 | def.expectClassReceiverProc() 136 | 137 | var defaults: seq[DefaultedArgument] = @[] 138 | 139 | if len(def[3]) > 1: 140 | for identDef in def[3][2..^1]: 141 | if identDef[^1].kind == nnkEmpty: 142 | continue 143 | 144 | for binding in identDef[0..^3]: 145 | defaults &= DefaultedArgument( 146 | binding: binding, 147 | default: identDef[^1]) 148 | 149 | var virtual = false 150 | var exportName = def[0].strVal() 151 | 152 | # macros.hasCustomPragma somehow always return false here 153 | for pragma in def[4]: 154 | if pragma.kind == nnkSym and pragma.strVal() == "gdvirtual": 155 | virtual = true 156 | elif pragma.kind == nnkExprColonExpr and pragma[0].strVal() == "name": 157 | exportName = pragma[1].strVal() 158 | 159 | classes[def.className].methods[exportName] = MethodInfo( 160 | symbol: def[0], 161 | defaultValues: defaults, 162 | virtual: virtual, 163 | ) 164 | 165 | def 166 | 167 | macro staticMethod*(T: typedesc; def: typed) = 168 | def.expectKind(nnkProcDef) 169 | 170 | var defaults: seq[DefaultedArgument] = @[] 171 | 172 | if len(def[3]) > 1: 173 | for identDef in def[3][2..^1]: 174 | if identDef[^1].kind == nnkEmpty: 175 | continue 176 | 177 | for binding in identDef[0..^3]: 178 | defaults &= DefaultedArgument( 179 | binding: binding, 180 | default: identDef[^1]) 181 | 182 | var exportName = def[0].strVal() 183 | 184 | for pragma in def[4]: 185 | if pragma.kind == nnkExprColonExpr and pragma[0].strVal() == "name": 186 | exportName = pragma[1].strVal() 187 | 188 | classes[$T].methods[exportName] = MethodInfo( 189 | symbol: def[0], 190 | defaultValues: defaults, 191 | virtual: false, 192 | ) 193 | 194 | def 195 | 196 | macro notification*(def: typed) = 197 | def.expectClassReceiverProc() 198 | 199 | classes[def.className].notificationHandlerIdent = def[0] 200 | 201 | def 202 | 203 | macro signal*(T: typedesc; def: typed) = 204 | def[3][0].expectKind(nnkEmpty) 205 | 206 | classes[$T].signals[def[0].strVal()] = newTree(nnkProcTy, def[3], newEmptyNode()) 207 | 208 | macro property*(def: typed) = 209 | def.expectKind(nnkProcDef) 210 | 211 | def[0].expectKind(nnkSym) 212 | 213 | let isSetter = def[0].strVal().endsWith('=') 214 | 215 | if isSetter: 216 | # Property setter function 217 | def[3][0].expectKind(nnkEmpty) 218 | def[3][1][1].expectKind(nnkVarTy) 219 | 220 | def[3][2][1].expectIdent("Variant") # for now 221 | 222 | else: 223 | # Property getter function 224 | def[3][0].expectKind(nnkSym) 225 | def[3][0].expectIdent("Variant") # for now 226 | def[3][1][1].expectKind(nnkVarTy) 227 | 228 | let classType = def[3][1][1][0] 229 | let propertyName = if isSetter: def[0].strVal()[0..^2] else: def[0].strVal() 230 | 231 | var p = classes[classType.strVal()].properties.mgetOrPut(propertyName, default(ClassProperty)) 232 | 233 | if isSetter: 234 | p.setter = def[0] 235 | else: 236 | p.getter = def[0] 237 | 238 | def 239 | 240 | macro revertQuery*(def: typed) = 241 | def.expectKind(nnkProcDef) 242 | 243 | def[0].expectKind(nnkSym) 244 | def[3].expectLen(3) 245 | 246 | def[3][0][0].expectIdent("Option") 247 | def[3][0][1].expectIdent("Variant") 248 | 249 | def[3][1][1].expectKind(nnkSym) 250 | 251 | def[3][2][1].expectIdent("StringName") 252 | 253 | let classType = def[3][1][1] 254 | 255 | classes[classType.strVal()].revertQueryIdent = def[0] 256 | 257 | def 258 | 259 | macro propertyQuery*(def: typed) = 260 | def.expectClassReceiverProc() 261 | 262 | def[3].expectLen(3) 263 | 264 | # No return 265 | def[3][0].expectKind(nnkEmpty) 266 | 267 | # var seq[GDExtensionPropretyInfo] 268 | def[3][2][^2].expectKind(nnkVarTy) 269 | def[3][2][^2][0].expectKind(nnkBracketExpr) 270 | def[3][2][^2][0][0].expectIdent("seq") 271 | def[3][2][^2][0][1].expectIdent("GDExtensionPropertyInfo") 272 | 273 | let classType = def[3][1][1] 274 | 275 | classes[classType.strVal()].listPropertiesIdent = def[0] 276 | 277 | def 278 | 279 | macro getProperty*(def: typed) = 280 | def.expectClassReceiverProc() 281 | let classType = def[3][1][1] 282 | 283 | classes[classType.strVal()].getPropertyIdent = def[0] 284 | 285 | def 286 | 287 | macro setProperty*(def: typed) = 288 | def.expectClassReceiverProc() 289 | let classType = def[3][1][1] 290 | 291 | classes[classType.strVal()].setPropertyIdent = def[0] 292 | 293 | def 294 | 295 | proc classEnumImpl(T: NimNode; isBitfield: bool; def: NimNode): NimNode = 296 | def.expectKind(nnkTypeDef) 297 | def[2].expectKind(nnkEnumTy) 298 | 299 | classes[$T].enums &= EnumInfo( 300 | definition: def, 301 | isBitfield: isBitfield) 302 | 303 | def 304 | 305 | macro classEnum*(T: typedesc; def: untyped) = 306 | result = classEnumImpl(T, false, def) 307 | 308 | macro classBitfield*(T: typedesc; def: untyped) = 309 | result = classEnumImpl(T, true, def) 310 | 311 | macro constant*(T: typedesc; def: typed) = 312 | def.expectKind(nnkConstSection) 313 | 314 | for constDef in def: 315 | constDef[2].expectKind(nnkIntLit) 316 | 317 | classes[$T].consts &= ConstInfo( 318 | name: constDef[0], 319 | value: constDef[2].intVal()) 320 | 321 | def 322 | 323 | # 324 | # Lower level interaction with the ClassDB interface 325 | # 326 | 327 | # Since we deal with a lot of compile time know stuff, this comes in 328 | # useful quite often. As the function is generated once per string, 329 | # we have our own interning of interned strings. 330 | proc staticStringName(s: static[string]): ptr StringName = 331 | var interned {.global.}: StringName = s 332 | 333 | addr interned 334 | 335 | proc gdClassName(_: typedesc): ptr StringName = staticStringName("") 336 | proc gdClassName[T: AnyObject](_: typedesc[T]): ptr StringName = staticStringName($T) 337 | 338 | include internal/typeinfo 339 | include internal/bindwrapper 340 | 341 | # Used in create_instance 342 | proc create_callback(token, instance: pointer): pointer {.cdecl.} = nil 343 | proc free_callback(token, instance, binding: pointer) {.cdecl.} = discard 344 | proc reference_callback(token, instance: pointer; reference: GDExtensionBool): GDExtensionBool {.cdecl.} = 1 345 | 346 | var nopOpBinding = GDExtensionInstanceBindingCallbacks( 347 | create_callback: create_callback, 348 | free_callback: free_callback, 349 | reference_callback: reference_callback) 350 | 351 | # The RuntimeClassRegistration[T] structure lives statically, once per class, and contains 352 | # the function callbacks into "userland", so to so. This is where all the callbacks into the 353 | # users code get stored. 354 | type 355 | ConstructorFunc[T] = proc(obj: T) 356 | DestructorFunc[T] = proc(obj: T) 357 | 358 | NotificationHandlerFunc[T] = proc(obj: T; what: int) 359 | RevertQueryFunc[T] = proc(obj: T; propertyName: StringName): Option[Variant] 360 | PropertyListFunc[T] = proc(obj: T; properties: var seq[GDExtensionPropertyInfo]) 361 | 362 | PropertyGetterFunc[T] = proc(obj: T; propertyName: StringName): Option[Variant] 363 | PropertySetterFunc[T] = proc(obj: T; propertyName: StringName; value: Variant): bool 364 | 365 | RuntimeClassRegistration[T] = object 366 | lastGodotAncestor: StringName = "Object" 367 | 368 | ctor: ConstructorFunc[T] 369 | dtor: DestructorFunc[T] 370 | 371 | notifierFunc: NotificationHandlerFunc[T] 372 | revertFunc: RevertQueryFunc[T] 373 | propertyListFunc: PropertyListFunc[T] 374 | 375 | getFunc: PropertyGetterFunc[T] 376 | setFunc: PropertySetterFunc[T] 377 | 378 | # Once again we fall back to this old trick. 379 | proc retrieveRuntimeClassInformation[T](): ptr RuntimeClassRegistration[T] = 380 | var rcr {.global.}: RuntimeClassRegistration[T] 381 | 382 | addr rcr 383 | 384 | 385 | proc create_instance[T, P](userdata: pointer): pointer {.cdecl.} = 386 | type 387 | TObj = pointerBase T 388 | 389 | var nimInst = cast[T](gdInterfacePtr.mem_alloc(sizeof(TObj).csize_t)) 390 | var rcr = cast[ptr RuntimeClassRegistration[T]](userdata) 391 | 392 | var className = ($T).StringName 393 | var lastNativeClassName = rcr.lastGodotAncestor 394 | 395 | mixin gdVTablePointer 396 | 397 | # We construct the parent class and store it into our opaque pointer field, so we have 398 | # a handle from Godot, for Godot. 399 | let obj = gdInterfacePtr.classdb_construct_object(addr lastNativeClassName) 400 | nimInst[] = TObj( 401 | opaque: obj, 402 | vtable: gdVTablePointer(T), 403 | gdclassinfo: userdata 404 | ) 405 | 406 | rcr.ctor(nimInst) 407 | 408 | # We tell Godot what the actual type for our object is and bind our native class to 409 | # its native class. 410 | gdInterfacePtr.object_set_instance(nimInst.opaque, addr className, nimInst) 411 | gdInterfacePtr.object_set_instance_binding(nimInst.opaque, gdTokenPtr, nimInst, addr nopOpBinding) 412 | 413 | nimInst.opaque 414 | 415 | proc free_instance[T, P](userdata: pointer; instance: GDExtensionClassInstancePtr) {.cdecl.} = 416 | var nimInst = cast[T](instance) 417 | var rcr = cast[ptr RuntimeClassRegistration[T]](userdata) 418 | 419 | rcr.dtor(nimInst) 420 | 421 | gdInterfacePtr.mem_free(nimInst) 422 | 423 | proc instance_to_string[T](instance: GDExtensionClassInstancePtr; 424 | valid: ptr GDExtensionBool; 425 | str: GDExtensionStringPtr) {.cdecl.} = 426 | var nimInst = cast[ptr T](instance) 427 | 428 | when compiles($nimInst[]): 429 | gdInterfacePtr.string_new_with_utf8_chars(str, cstring($nimInst)) 430 | valid[] = 1 431 | else: 432 | valid[] = 0 433 | 434 | proc instance_notification[T](instance: GDExtensionClassInstancePtr; 435 | what: int32) {.cdecl.} = 436 | var nimInst = cast[T](instance) 437 | let regInst = cast[ptr RuntimeClassRegistration[T]](nimInst.gdclassinfo) 438 | 439 | if regInst.notifierFunc.isNil(): 440 | return 441 | 442 | regInst.notifierFunc(nimInst, int(what)) 443 | 444 | macro vtableEntries[T](vptr: ptr T): auto = 445 | ## Give a pointer to some VTable, it generates a tuple of the following layout: 446 | ## `(fieldName: (name: "_godot_name", fnPtr: ...), ...)` 447 | let vtDef = vptr.getType()[1].getTypeImpl() 448 | 449 | result = newTree(nnkTupleConstr) 450 | 451 | for field in vtDef[2]: 452 | result &= newTree(nnkExprColonExpr, field[0], 453 | newTree(nnkTupleConstr, 454 | newTree(nnkExprColonExpr, ident"name", newCall(ident"StringName", field[1][1][0][1])), 455 | newTree(nnkExprColonExpr, ident"fnPtr", newDotExpr(vptr, field[0])))) 456 | 457 | proc instance_virt_query[T](userdata: pointer; 458 | methodName: GDExtensionConstStringNamePtr): GDExtensionClassCallVirtual 459 | {.cdecl.} = 460 | var methodName = cast[ptr StringName](methodName) 461 | 462 | # This relies on the fact that the vtable is initialized so that all parent 463 | # virtual functions are either already registered or overriden. 464 | let vtfields {.global.} = vtableEntries(gdVTablePointer(T)) 465 | 466 | # These are cached per instance by Godot, so looping through naively is fine. 467 | for entry, fn in vtfields.fieldPairs(): 468 | if fn.name == methodName[]: 469 | if fn.fnPtr.isNil(): 470 | return nil 471 | else: 472 | # We return an anonymous proc with a matching signature that does 473 | # about the same thing as invoke_method_ptrcall() below, only with 474 | # a "baked in" callable ptr. This works because `vtfields` is statically 475 | # known and `fieldPairs` is statically unrolled and `fn.fnPtr` is staticaly 476 | # known as well. 477 | return proc( 478 | instance: GDExtensionClassInstancePtr; 479 | args: ptr GDExtensionConstTypePtr; 480 | returnPtr: GDExtensionTypePtr) {.cdecl.} = 481 | invoke_ptrcall[T](fn.fnPtr, false, instance, args, returnPtr) 482 | nil 483 | 484 | proc can_property_revert[T](instance: GDExtensionClassInstancePtr; 485 | name: GDExtensionConstStringNamePtr): GDExtensionBool {.cdecl.} = 486 | var nimInst = cast[T](instance) 487 | var prop = cast[ptr StringName](name) 488 | let regInst = cast[ptr RuntimeClassRegistration[T]](nimInst.gdclassinfo) 489 | 490 | if regInst.revertFunc.isNil(): 491 | return 0 492 | 493 | GDExtensionBool(regInst.revertFunc(nimInst, prop[]).isSome()) 494 | 495 | proc property_revert[T](instance: GDExtensionClassInstancePtr; 496 | name: GDExtensionConstStringNamePtr; 497 | ret: GDExtensionVariantPtr): GDExtensionBool {.cdecl.} = 498 | var nimInst = cast[T](instance) 499 | var prop = cast[ptr StringName](name) 500 | var retPtr = cast[ptr Variant](ret) 501 | let regInst = cast[ptr RuntimeClassRegistration[T]](nimInst.gdclassinfo) 502 | 503 | if regInst.revertFunc.isNil(): 504 | return 0 505 | 506 | let revertValue = regInst.revertFunc(nimInst, prop[]) 507 | 508 | if revertValue.isNone(): 509 | return 0 510 | 511 | retPtr[] = revertValue.unsafeGet() 512 | 513 | return 1 514 | 515 | type 516 | PropertyInfo*[T] = object 517 | name: StringName 518 | hint: StringName = "" 519 | propertyType: typedesc[T] 520 | propertyUsage: uint32 = uint32(pufDefault) 521 | propertyHint: uint32 = uint32(phiNone) 522 | 523 | proc addPropertyInfo*[T](list: var seq[GDExtensionPropertyInfo]; info: PropertyInfo[T]) = 524 | when T is AnyObject: 525 | var classNamePtr = gdClassName(T) 526 | else: 527 | var classNamePtr = staticStringName("") 528 | 529 | var namePtr = create(StringName) 530 | var hintPtr = create(StringName) 531 | 532 | namePtr[] = info.name 533 | hintPtr[] = info.hint 534 | 535 | list &= GDExtensionPropertyInfo( 536 | name: namePtr, 537 | `type`: T.variantTypeId(), 538 | class_name: classNamePtr, 539 | hint: info.propertyHint, 540 | hint_string: hintPtr, 541 | usage: info.propertyUsage) 542 | 543 | proc addPropertyInfo*[T](list: var seq[GDExtensionPropertyInfo]; name: StringName; hint: StringName = "") = 544 | list.addPropertyInfo(PropertyInfo[T](name: name, hint: hint)) 545 | 546 | type 547 | # GDExtension does not tell us (in free_class_properties), how many properties 548 | # we gave it in list_class_properties, but we have to iterate over it to free 549 | # the names we allocated previously. So we use the old C trick of allocating the 550 | # list with a little prefix to store our count in, followed by the actual list payload, 551 | # give Godot the offset pointer to `elems` and calculate the reverse when it's time to 552 | # free the list. 553 | # 554 | # This is slightly more expensive than letting the library user cache a list of their 555 | # properties and returning it in order to guarantee the fields are kept alive until 556 | # the free callback (like godot-cpp does), but it's also much more convenient and unless 557 | # get_property_list() is invoked in a hot loop it shouldn't make a difference. 558 | LenPrefixedPropertyInfo = object 559 | count: uint32 560 | elems: UncheckedArray[GDExtensionPropertyInfo] 561 | 562 | proc list_class_properties[T](instance: GDExtensionClassInstancePtr; 563 | count: ptr uint32): ptr GDExtensionPropertyInfo {.cdecl.} = 564 | var nimInst = cast[T](instance) 565 | let regInst = cast[ptr RuntimeClassRegistration[T]](nimInst.gdclassinfo) 566 | 567 | var properties = newSeq[GDExtensionPropertyInfo]() 568 | 569 | if not regInst.propertyListFunc.isNil(): 570 | regInst.propertyListFunc(nimInst, properties) 571 | 572 | count[] = uint32 len(properties) 573 | 574 | if regInst.propertyListFunc.isNil() or len(properties) == 0: 575 | return nil 576 | 577 | let size = sizeOf(LenPrefixedPropertyInfo) + (sizeOf(GDExtensionPropertyInfo) * len(properties)) 578 | let prefixed = cast[ptr LenPrefixedPropertyInfo](alloc(size)) 579 | 580 | prefixed[].count = count[] 581 | 582 | var propertyInfos = cast[ptr UncheckedArray[GDExtensionPropertyInfo]](addr prefixed[].elems) 583 | 584 | for i, property in properties: 585 | propertyInfos[i] = property 586 | 587 | result = cast[ptr GDExtensionPropertyInfo](addr prefixed[].elems) 588 | 589 | proc free_class_properties[T](instance: GDExtensionClassInstancePtr; 590 | list: ptr GDExtensionPropertyInfo) {.cdecl.} = 591 | let offset = offsetOf(LenPrefixedPropertyInfo, elems) 592 | let prefixed = cast[ptr LenPrefixedPropertyInfo](cast[pointer](cast[int](list) - offset)) 593 | 594 | if not list.isNil(): 595 | for i in 0..prefixed[].count - 1: 596 | `=destroy`(prefixed[].elems[i].name) 597 | `=destroy`(prefixed[].elems[i].hint_string) 598 | 599 | dealloc(prefixed[].elems[i].name) 600 | dealloc(prefixed[].elems[i].hint_string) 601 | 602 | dealloc(list) 603 | 604 | proc property_set[T](instance: GDExtensionClassInstancePtr; 605 | name: GDExtensionConstStringNamePtr; 606 | value: GDExtensionConstVariantPtr): GDExtensionBool {.cdecl.} = 607 | var nimInst = cast[T](instance) 608 | let regInst = cast[ptr RuntimeClassRegistration[T]](nimInst.gdclassinfo) 609 | var prop = cast[ptr StringName](name) 610 | var value = cast[ptr Variant](value) 611 | 612 | result = 0 613 | 614 | if not regInst.setFunc.isNil(): 615 | result = GDExtensionBool(regInst.setFunc(nimInst, prop[], value[])) 616 | 617 | proc property_get[T](instance: GDExtensionClassInstancePtr; 618 | name: GDExtensionConstStringNamePtr; 619 | value: GDExtensionVariantPtr): GDExtensionBool {.cdecl.} = 620 | var nimInst = cast[T](instance) 621 | let regInst = cast[ptr RuntimeClassRegistration[T]](nimInst.gdclassinfo) 622 | 623 | var prop = cast[ptr StringName](name) 624 | var retValue = cast[ptr Variant](value) 625 | 626 | var value = none Variant 627 | 628 | result = 0 629 | 630 | if not regInst.getFunc.isNil(): 631 | value = regInst.getFunc(nimInst, prop[]) 632 | result = GDExtensionBool(value.isSome()) 633 | 634 | if value.isSome(): 635 | retValue[] = value.unsafeGet() 636 | 637 | proc registerClass*[T, P]( 638 | lastNative: StringName, 639 | ctorFunc: ConstructorFunc[T]; 640 | dtorFunc: DestructorFunc[T]; 641 | notification: NotificationHandlerFunc[T]; 642 | revertQuery: RevertQueryFunc[T]; 643 | listProperties: PropertyListFunc[T]; 644 | getProperty: PropertyGetterFunc[T]; 645 | setProperty: PropertySetterFunc[T]; 646 | abstract, virtual: bool = false) = 647 | 648 | var className: StringName = $T 649 | var parentClassName: StringName = $P 650 | 651 | when T isnot AnyObject: 652 | {.warning: "T really should derive (directly or indrectly) from `Object`.".} 653 | 654 | var rcrPtr = retrieveRuntimeClassInformation[T]() 655 | 656 | rcrPtr[] = RuntimeClassRegistration[T]( 657 | ctor: ctorFunc, 658 | dtor: dtorFunc, 659 | notifierFunc: notification, 660 | lastGodotAncestor: lastNative, 661 | revertFunc: revertQuery, 662 | propertyListFunc: listProperties, 663 | getFunc: getProperty, 664 | setfunc: setProperty 665 | ) 666 | 667 | var creationInfo = GDExtensionClassCreationInfo( 668 | is_virtual: GDExtensionBool(virtual), 669 | is_abstract: GDExtensionBool(abstract), 670 | 671 | set_func: property_set[T], 672 | get_func: property_get[T], 673 | 674 | get_property_list_func: list_class_properties[T], 675 | free_property_list_func: free_class_properties[T], 676 | 677 | property_can_revert_func: can_property_revert[T], 678 | property_get_revert_func: property_revert[T], 679 | 680 | notification_func: instance_notification[T], 681 | to_string_func: instance_to_string[T], 682 | reference_func: nil, 683 | unreference_func: nil, 684 | 685 | create_instance_func: create_instance[T, P], # default ctor 686 | free_instance_func: free_instance[T, P], # dtor 687 | get_virtual_func: instance_virt_query[T], 688 | get_rid_func: nil, 689 | 690 | class_userdata: rcrPtr) 691 | 692 | gdInterfacePtr.classdb_register_extension_class( 693 | gdTokenPtr, 694 | addr className, 695 | addr parentClassName, 696 | addr creationInfo) 697 | 698 | proc invoke_method*[T, M](userdata: pointer; 699 | instance: GDExtensionClassInstancePtr; 700 | args: ptr GDExtensionConstVariantPtr; 701 | argc: GDExtensionInt; 702 | ret: GDExtensionVariantPtr; 703 | error: ptr GDExtensionCallError) {.cdecl.} = 704 | 705 | invoke_bindcall[T](cast[M](userdata), false, instance, args, argc, ret, error) 706 | 707 | proc invoke_static_method*[T, M](userdata: pointer; 708 | instance: GDExtensionClassInstancePtr; 709 | args: ptr GDExtensionConstVariantPtr; 710 | argc: GDExtensionInt; 711 | ret: GDExtensionVariantPtr; 712 | error: ptr GDExtensionCallError) {.cdecl.} = 713 | 714 | invoke_bindcall[T](cast[M](userdata), true, instance, args, argc, ret, error) 715 | 716 | proc invoke_method_ptrcall*[T, M]( 717 | userdata: pointer; 718 | instance: GDExtensionClassInstancePtr; 719 | args: ptr GDExtensionConstTypePtr; 720 | returnPtr: GDExtensionTypePtr) {.cdecl.} = 721 | 722 | invoke_ptrcall[T](cast[M](userdata), false, instance, args, returnPtr) 723 | 724 | proc invoke_static_method_ptrcall*[T, M]( 725 | userdata: pointer; 726 | instance: GDExtensionClassInstancePtr; 727 | args: ptr GDExtensionConstTypePtr; 728 | returnPtr: GDExtensionTypePtr) {.cdecl.} = 729 | 730 | invoke_ptrcall[T](cast[M](userdata), true, instance, args, returnPtr) 731 | 732 | # Murmur3-32 is used to calculate the method hashes, we may need to replicate those. 733 | func murmur3(input: uint32; seed: uint32 = 0x7F07C65): uint32 = 734 | var input = input 735 | var seed = seed 736 | 737 | input *= 0xCC9E2D51'u32 738 | input = (input shl 15) or (input shr 17) 739 | input *= 0x1B873593'u32 740 | 741 | seed = seed xor input 742 | seed = (seed shl 13) or (seed shr 19) 743 | seed = seed * 5 + 0xE6546b64'u32 744 | 745 | seed 746 | 747 | func fmix32(input: uint32): uint32 = 748 | result = input 749 | 750 | result = result xor (result shr 16) 751 | result *= 0x85EBCA6B'u32 752 | result = result xor (result shr 13) 753 | result *= 0xC2B2AE35'u32 754 | result = result xor (result shr 16) 755 | 756 | proc calculateHash( 757 | returnValue: Option[GDExtensionPropertyInfo]; 758 | args: openArray[GDExtensionPropertyInfo]; 759 | defaults: openArray[Variant]; 760 | flags: set[GDExtensionClassMethodFlags]): uint32 = 761 | result = murmur3(uint32(returnValue.isSome())) 762 | result = murmur3(uint32(args.len()), result) 763 | 764 | if returnValue.isSome(): 765 | let clsName = cast[ptr StringName](returnValue.unsafeGet().class_name) 766 | 767 | result = murmur3(uint32(returnValue.unsafeGet().`type`), result) 768 | 769 | if (clsName != staticStringName("")): 770 | result = murmur3(uint32(clsName[].String.hash()), result) 771 | 772 | for arg in args: 773 | let clsName = cast[ptr StringName](arg.class_name) 774 | 775 | result = murmur3(uint32(arg.`type`), result) 776 | 777 | if (clsName != staticStringName("")): 778 | result = murmur3(uint32(clsName[].String.hash()), result) 779 | 780 | result = murmur3(uint32(len(defaults)), result) 781 | 782 | for default in defaults: 783 | result = murmur3(uint32(default.hash()), result) 784 | 785 | result = murmur3(uint32(GDEXTENSION_METHOD_FLAG_CONST in flags), result) 786 | result = murmur3(uint32(GDEXTENSION_METHOD_FLAG_VARARG in flags), result) 787 | result = fmix32(result) 788 | 789 | func packBits(flags: set[GDExtensionClassMethodFlags]): uint32 = 790 | for flag in flags: 791 | result = result or uint32(flag) 792 | 793 | proc registerMethod*[T]( 794 | name: static[string]; 795 | callable: auto; 796 | defaults: auto; 797 | virtual: bool = false) = 798 | var className: StringName = $T 799 | var methodName: StringName = name 800 | 801 | type 802 | M = typeOf callable 803 | 804 | # Collect parameter and return value properties 805 | {.hint[ConvFromXtoItselfNotNeeded]: off.} 806 | {.warning[HoleEnumConv]: off.} 807 | 808 | let procProperties = M.getProcProps(T) 809 | 810 | # Pack flags set[] into uint32 and add virtual 811 | var methodFlags: set[GDExtensionClassMethodFlags] = procProperties.pflags 812 | 813 | {.warning[HoleEnumConv]: on.} 814 | {.hint[ConvFromXtoItselfNotNeeded]: on.} 815 | 816 | if virtual: 817 | methodFlags.incl GDEXTENSION_METHOD_FLAG_VIRTUAL 818 | 819 | # Collect default values 820 | var defaultVariants = newSeq[Variant]() 821 | 822 | # fieldPairs() doesn't play well with collect(), so we can't be fancy here 823 | for param, default in defaults.fieldPairs(): 824 | defaultVariants &= %default 825 | 826 | var defaultVariantPtrs = collect(newSeqOfCap(len(defaultVariants))): 827 | for i in 0..high(defaultVariants): 828 | cast[ptr GDExtensionVariantPtr](addr defaultVariants[i]) 829 | 830 | const (bindcall, ptrcall) = when M.isStatic(T): 831 | (invoke_static_method[T, M], 832 | invoke_static_method_ptrcall[T, M]) 833 | else: 834 | (invoke_method[T, M], 835 | invoke_method_ptrcall[T, M]) 836 | 837 | let returnInfo = procProperties.retval 838 | 839 | var methodInfo = GDExtensionClassMethodInfo( 840 | name: addr methodName, 841 | method_userdata: callable, 842 | 843 | call_func: bindcall, 844 | ptrcall_func: ptrcall, 845 | 846 | method_flags: methodFlags.packBits(), 847 | 848 | has_return_value: GDExtensionBool(returnInfo.isSome()), 849 | return_value_info: returnInfo.map((i) => addr i[0]).get(nil), 850 | return_value_metadata: returnInfo.map((i) => i[1]).get(GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE), 851 | 852 | argument_count: uint32(procProperties.pargc), 853 | arguments_info: cast[ptr GDExtensionPropertyInfo](addr procProperties.pargs), 854 | arguments_metadata: cast[ptr GDExtensionClassMethodArgumentMetadata](addr procProperties.pmeta), 855 | 856 | default_argument_count: uint32(len(defaultVariantPtrs)), 857 | default_arguments: if len(defaultVariantPtrs) > 0: 858 | cast[ptr GDExtensionVariantPtr](addr defaultVariantPtrs[0]) 859 | else: 860 | nil, 861 | ) 862 | 863 | gdInterfacePtr.classdb_register_extension_class_method( 864 | gdTokenPtr, 865 | addr className, 866 | addr methodInfo) 867 | 868 | iterator possiblyHoleyItems[E: enum](_: typedesc[E]): E = 869 | when E is HoleyEnum: 870 | for elem in enumutils.items(E): yield elem 871 | else: 872 | for elem in E: yield elem 873 | 874 | proc registerClassEnum*[T, E: enum](t: typedesc[E]; isBitfield: bool = false) = 875 | var className: StringName = $T 876 | var enumName: StringName = $E 877 | 878 | {.warning[HoleEnumConv]: off.} 879 | 880 | for value in possiblyHoleyItems(E): 881 | var fieldName: StringName = $value 882 | 883 | gdInterfacePtr.classdb_register_extension_class_integer_constant( 884 | gdTokenPtr, 885 | addr className, 886 | addr enumName, 887 | addr fieldName, 888 | GDExtensionInt(ord(value)), 889 | GDExtensionBool(isBitfield)) 890 | 891 | {.warning[HoleEnumConv]: on.} 892 | 893 | proc registerClassConstant*[T](name: string; value: int) = 894 | var className: StringName = $T 895 | var constName: StringName = name 896 | 897 | gdInterfacePtr.classdb_register_extension_class_integer_constant( 898 | gdTokenPtr, 899 | addr className, 900 | staticStringName(""), 901 | addr constName, 902 | GDExtensionInt(value), 903 | GDExtensionBool(false)) 904 | 905 | proc registerProperty*[T, P](name, setter, getter: string) = 906 | var className: StringName = $T 907 | var propertyName: StringName = name 908 | 909 | var setterName: StringName = setter 910 | var getterName: StringName = getter 911 | 912 | var nameName: StringName = name 913 | 914 | var info = getPropertyInfo[P]() 915 | info.name = addr nameName 916 | 917 | gdInterfacePtr.classdb_register_extension_class_property( 918 | gdTokenPtr, 919 | addr className, 920 | addr info, 921 | addr setterName, 922 | addr getterName) 923 | 924 | proc registerPropertyGroup*[T](name, prefix: string) = 925 | var className: StringName = $T 926 | var groupName: StringName = $T 927 | var prefixName: StringName = $T 928 | 929 | gdInterfacePtr.classdb_register_extension_class_property_group( 930 | gdTokenPtr, 931 | addr className, 932 | addr groupName, 933 | addr prefixName) 934 | 935 | proc registerPropertySubgroup*[T](name, prefix: string) = 936 | var className: StringName = $T 937 | var groupName: StringName = $T 938 | var prefixName: StringName = $T 939 | 940 | gdInterfacePtr.classdb_register_extension_class_property_subgroup( 941 | gdTokenPtr, 942 | addr className, 943 | addr groupName, 944 | addr prefixName) 945 | 946 | proc registerSignal*[T](name: string; protoype: typedesc[proc]) = 947 | var className: StringName = $T 948 | var signalName: StringName = name 949 | 950 | # Collect parameter and return value properties 951 | {.hint[ConvFromXtoItselfNotNeeded]: off.} 952 | 953 | let procProperties = protoype.getProcProps(T) 954 | 955 | {.hint[ConvFromXtoItselfNotNeeded]: on.} 956 | 957 | gdInterfacePtr.classdb_register_extension_class_signal( 958 | gdTokenPtr, 959 | addr className, 960 | addr signalName, 961 | cast[ptr GDExtensionPropertyInfo](addr procProperties.pargs), 962 | procProperties.pargc) 963 | 964 | proc generateDefaultsTuple(mi: MethodInfo): NimNode = 965 | result = newTree(nnkTupleConstr) 966 | 967 | for default in mi.defaultValues: 968 | result &= newTree(nnkExprColonExpr, 969 | default.binding, 970 | default.default) 971 | 972 | macro register*() = 973 | result = newStmtList() 974 | 975 | for className, regInfo in classes: 976 | # Because we (apparently) cannot cleanly derive from our own classes, we establish 977 | # the latest class that is native to Godot and "derive" from that. The instance is 978 | # still registered to the correct parent class type, but everything after the 979 | # last Godot class is handled Nim-side. 980 | var lastAncestor = regInfo.parentNode.strVal() 981 | 982 | while lastAncestor in classes: 983 | lastAncestor = classes[lastAncestor].parentNode.strVal() 984 | 985 | result.add genAst( 986 | lastAncestor, 987 | T = regInfo.typeNode, 988 | P = regInfo.parentNode, 989 | ctor = regInfo.ctorFuncIdent, 990 | dtor = regInfo.dtorFuncIdent, 991 | notification = regInfo.notificationHandlerIdent, 992 | revertQuery = regInfo.revertQueryIdent, 993 | listProperties = regInfo.listPropertiesIdent, 994 | getProp = regInfo.getPropertyIdent, 995 | setProp = regInfo.setPropertyIdent, 996 | isAbstract = regInfo.abstract, 997 | isVirtual = regInfo.virtual) do: 998 | 999 | registerClass[T, P]( 1000 | lastAncestor, 1001 | ctor, 1002 | dtor, 1003 | notification, 1004 | revertQuery, 1005 | listProperties, 1006 | getProp, 1007 | setProp, 1008 | isAbstract, 1009 | isVirtual) 1010 | 1011 | for methodName, methodInfo in regInfo.methods: 1012 | result.add genAst( 1013 | T = regInfo.typeNode, 1014 | methodName, 1015 | methodSymbol = methodInfo.symbol, 1016 | defaultArgs = methodInfo.generateDefaultsTuple, 1017 | isVirtual = methodInfo.virtual) do: 1018 | 1019 | registerMethod[T](methodName, methodSymbol, defaultArgs, isVirtual) 1020 | 1021 | for enumDef in regInfo.enums: 1022 | result.add genAst( 1023 | T = regInfo.typeNode, 1024 | E = enumDef.definition[0][0], 1025 | isBitfield = enumDef.isBitfield) do: 1026 | 1027 | registerClassEnum[T, E](E, isBitfield) 1028 | 1029 | for constDef in regInfo.consts: 1030 | result.add genAst( 1031 | T = regInfo.typeNode, 1032 | name = constDef.name.strVal(), 1033 | value = constDef.value) do: 1034 | 1035 | registerClassConstant[T](name, value) 1036 | 1037 | for signalName, signalDef in regInfo.signals: 1038 | result.add genAst( 1039 | T = regInfo.typeNode, 1040 | name = signalName, 1041 | value = signalDef) do: 1042 | 1043 | registerSignal[T](name, value) -------------------------------------------------------------------------------- /nimrodot/classes/internal/instance_funcs.nim: -------------------------------------------------------------------------------- 1 | import std/typetraits 2 | 3 | import ../../utils 4 | import ../../ffi 5 | import ../../interface_ptrs 6 | 7 | export ffi 8 | export utils 9 | 10 | proc makeInstanceFunctions*[T](_: typedesc[T]): GDExtensionInstanceBindingCallbacks = 11 | type 12 | TObj = pointerBase T 13 | 14 | proc create_callback(token, instance: pointer): pointer {.cdecl.} = 15 | result = gdInterfacePtr.mem_alloc(csize_t sizeOf(`TObj`)) 16 | 17 | cast[T](result)[] = `TObj`(opaque: instance, vtable: T.gdVTablePointer()) 18 | 19 | proc free_callback(token, instance, binding: pointer) {.cdecl.} = 20 | gdInterfacePtr.mem_free(binding) 21 | 22 | proc reference_callback(token, instance: pointer; reference: GDExtensionBool): GDExtensionBool {.cdecl.} = 23 | 1 24 | 25 | GDExtensionInstanceBindingCallbacks( 26 | create_callback: create_callback, 27 | free_callback: free_callback, 28 | reference_callback: reference_callback) -------------------------------------------------------------------------------- /nimrodot/gdffi.nim: -------------------------------------------------------------------------------- 1 | import std/[macros, genasts, options] 2 | 3 | import ./interface_ptrs 4 | import ./ffi 5 | import ./utils 6 | 7 | import ./builtins/variant 8 | import ./builtins/types 9 | 10 | import ./classes/types/"object" 11 | 12 | export utils.`&` 13 | 14 | type 15 | ReturnInfo = object 16 | isVoid: bool 17 | 18 | fullType: NimNode 19 | 20 | ParamInfo = object 21 | isStatic: bool 22 | 23 | fullType: NimNode 24 | genericConstraint: NimNode 25 | binding: NimNode 26 | 27 | func resolveReturn(prototype: NimNode): ReturnInfo = 28 | if prototype[3][0].kind == nnkEmpty: 29 | result.isVoid = true 30 | else: 31 | result.fullType = prototype[3][0] 32 | 33 | func resolveSelf(prototype: NimNode): ParamInfo = 34 | result.genericConstraint = newEmptyNode() 35 | 36 | if len(prototype[3]) > 1 and prototype[3][1][0] == "_".ident(): 37 | result.fullType = prototype[3][1][1][1] 38 | result.isStatic = true 39 | else: 40 | result.isStatic = false 41 | result.binding = prototype[3][1][0] 42 | 43 | # Check for `[T: U](self: Ref[T])` 44 | if prototype[2].kind != nnkEmpty: 45 | result.genericConstraint = prototype[2][0][1] 46 | 47 | if prototype[3][1][^2].kind == nnkVarTy: 48 | result.fullType = prototype[3][1][1][0] 49 | else: 50 | result.fullType = prototype[3][1][1] 51 | 52 | func isRefCountWrapper(s: ParamInfo | ReturnInfo): bool = 53 | s.fullType.kind == nnkBracketExpr and s.fullType[0].strVal == "Ref" 54 | 55 | func reduceType(s: ParamInfo | ReturnInfo): NimNode = 56 | when s is ParamInfo: 57 | if s.genericConstraint.kind != nnkEmpty: 58 | return s.genericConstraint 59 | 60 | if s.fullType.kind == nnkBracketExpr: 61 | s.fullType[1] 62 | else: 63 | s.fullType 64 | 65 | func reducefield(s: ParamInfo; field: string): NimNode = 66 | if s.isStatic: 67 | # Static methods take a nil pointer 68 | newNilLit() 69 | elif s.isRefCountWrapper: 70 | # Ref[T] wrappers reduce to self[].opaque 71 | newDotExpr(newTree(nnkBracketExpr, s.binding), field.ident()) 72 | else: 73 | # Everything else reduces to self.opaque 74 | newDotExpr(s.binding, field.ident()) 75 | 76 | func reduceVtPtr(s: ParamInfo): NimNode = 77 | s.reduceField("vtable") 78 | 79 | func reducePtr(s: ParamInfo): NimNode = 80 | s.reduceField("opaque") 81 | 82 | func reducePtr(r: ReturnInfo): NimNode = 83 | if r.isVoid: 84 | # No result pointer for void procs 85 | newNilLit() 86 | else: 87 | # We delegate to the nim compiler via template because we need type info here 88 | newCall("getResultPtr".ident()) 89 | 90 | func reduceAddr(s: ParamInfo): NimNode = 91 | if s.isStatic: 92 | newNilLit() 93 | elif s.isRefCountWrapper: 94 | newCall("addr".ident(), newDotExpr(newTree(nnkBracketExpr, s.binding), "opaque".ident())) 95 | else: 96 | newCall("getSelfPtr".ident(), s.binding) 97 | 98 | # Called in the second run-around of the macro expansion once type information 99 | # is available to determine if we are dealing with an object type or a builtin 100 | template getResultPtr*(): pointer {.dirty.} = 101 | when compiles(result.opaque): 102 | addr result.opaque 103 | elif compiles(result): 104 | addr result 105 | else: 106 | nil 107 | 108 | template getSelfPtr*(binding: typed): pointer {.dirty.} = 109 | when compiles(binding.opaque): 110 | addr binding.opaque 111 | elif compiles(binding): 112 | addr binding 113 | else: 114 | nil 115 | 116 | template getParamPtr*(p: untyped): pointer {.dirty.} = 117 | when compiles(p.opaque): 118 | addr p.opaque 119 | else: 120 | addr p 121 | 122 | 123 | func getVarArgs(prototype: NimNode): Option[NimNode] = 124 | if len(prototype[3]) > 1: 125 | let lastArg = prototype[3][^1] 126 | 127 | if lastArg[1].kind == nnkBracketExpr and lastArg[1][0].strVal == "varargs": 128 | return some lastArg 129 | 130 | none NimNode 131 | 132 | template getArgPointer*[T](p: T): pointer = 133 | when p is AnyObject: 134 | if p.isNil(): 135 | nil 136 | else: 137 | p.opaque 138 | else: 139 | addr p 140 | 141 | func genArgsList(prototype: NimNode; argc: ptr int; ignoreFirst: bool = false): NimNode = 142 | result = newTree(nnkBracket) 143 | 144 | # If the first parameter is a typedesc[T] marker, skip it 145 | var skip = if len(prototype[3]) > 1 and prototype[3][1][0] == "_".ident(): 146 | 2 147 | else: 148 | # We ignore the first arg for (non-builtin) class methods because the instance 149 | # pointer is passed as separate argument. 150 | if ignoreFirst: 2 else: 1 151 | 152 | # Do not include the varargs here, they are handled specially 153 | let drop = if getVarArgs(prototype).isSome(): 1 else: 0 154 | 155 | if skip > len(prototype[3]) - 1: 156 | return 157 | 158 | for formalArgs in prototype[3][skip..^(1 + drop)]: 159 | for formalArg in formalArgs[0..^3]: 160 | inc argc[] 161 | 162 | result.add genAst(formalArg) do: 163 | getArgPointer(formalArg) 164 | 165 | result = genAst(arrayLit = result, argc = argc[]) do: 166 | (array[argc, GDExtensionConstTypePtr])(arrayLit) 167 | 168 | func getNameFromProto(proto: NimNode): string = 169 | if proto[0].kind in {nnkIdent, nnkSym}: 170 | # "funcname" 171 | proto[0].strVal() 172 | elif proto[0][1].kind in {nnkIdent, nnkSym}: 173 | # "funcname*" 174 | proto[0][1].strVal() 175 | else: 176 | # `quoted` 177 | proto[0][1][0].strVal() 178 | 179 | # We need to put these into dedicated functions rather than `block:` statements due to 180 | # bad codegen with destructors present. 181 | proc getUtilityFunctionPtr(fun: static[string]; hash: static[int64]): GDExtensionPtrUtilityFunction = 182 | var gdFuncName = &fun 183 | 184 | gdInterfacePtr.variant_get_ptr_utility_function(addr gdFuncName, hash) 185 | 186 | proc getBuiltinMethodPtr[T](meth: static[string]; hash: static[int64]): GDExtensionPtrBuiltInMethod = 187 | var gdFuncName = &meth 188 | 189 | gdInterfacePtr.variant_get_ptr_builtin_method(T.variantTypeId, addr gdFuncName, hash) 190 | 191 | proc getClassMethodBindPtr*(cls, meth: static[string]; hash: static[int64]): GDExtensionMethodBindPtr = 192 | var gdClassName = &cls 193 | var gdMethName = &meth 194 | 195 | gdInterfacePtr.classdb_get_method_bind(addr gdClassName, addr gdMethName, hash) 196 | 197 | 198 | macro gd_utility*(hash: static[int64]; prototype: untyped) = 199 | ## Implement a Godot utility function based upon the given proc declaration's name 200 | ## and a hash value derived from the API description. 201 | 202 | let functionName = prototype.getNameFromProto() 203 | 204 | var argc: int 205 | 206 | let resultPtr = prototype.resolveReturn().reducePtr() 207 | let varArgs = prototype.getVarArgs() 208 | let args = prototype.genArgsList(addr argc) 209 | 210 | result = prototype 211 | 212 | if varArgs.isNone(): 213 | result[^1] = genAst(functionName, hash, args, argc, resultPtr) do: 214 | var p {.global.} = getUtilityFunctionPtr(functionName, hash) 215 | var argPtrs: array[argc, GDExtensionConstTypePtr] = args 216 | 217 | p( 218 | cast[GDExtensionTypePtr](resultPtr), 219 | cast[ptr GDExtensionConstTypePtr](addr argPtrs), 220 | cint(argc)) 221 | else: 222 | let varArgId = varArgs.unsafeGet()[0] 223 | 224 | result[^1] = genAst(functionName, hash, args, argc, varArgId, resultPtr) do: 225 | var p {.global.} = getUtilityFunctionPtr(functionName, hash) 226 | var argPtrs = @args 227 | 228 | for i in 0..high(varArgId): 229 | argPtrs &= getArgPointer varArgId[i] 230 | 231 | p( 232 | cast[GDExtensionTypePtr](resultPtr), 233 | cast[ptr GDExtensionConstTypePtr](addr argPtrs[0]), 234 | cint(argc + len(varArgId))) 235 | 236 | # Builtins (Variant) 237 | 238 | macro gd_builtin_ctor*(ty: typed; idx: static[int]; prototype: untyped) = 239 | ## Implement a Godot builtin constructor for the given type and 240 | ## constructor index. 241 | 242 | var argc: int 243 | let args = prototype.genArgsList(addr argc) 244 | 245 | result = prototype 246 | result[^1] = genAst(ty, idx, args, argc, result = ident"result") do: 247 | var p {.global.} = gdInterfacePtr.variant_get_ptr_constructor( 248 | ty.variantTypeId, int32(idx)) 249 | 250 | var argPtrs: array[argc, GDExtensionConstTypePtr] = args 251 | 252 | p(addr result, cast[ptr GDExtensionConstTypePtr](addr argPtrs)) 253 | 254 | macro gd_builtin_dtor*(ty: typed; prototype: untyped) = 255 | ## Implement a Godot builtin destructor for the given type. 256 | 257 | let selfPtr = prototype.resolveSelf().reducePtr() 258 | 259 | result = prototype 260 | result[^1] = genAst(ty, selfPtr) do: 261 | var p {.global.} = gdInterfacePtr.variant_get_ptr_destructor( 262 | ty.variantTypeId) 263 | 264 | p(cast[GDExtensionTypePtr](selfPtr)) 265 | 266 | 267 | macro gd_builtin_method*(ty: typed; hash: static[int64]; prototype: untyped) = 268 | ## Implement a Godot builtin method for the given type, based upon the 269 | ## proc declaration's name and a hash value derived from the API description. 270 | 271 | let functionName = prototype.getNameFromProto() 272 | 273 | var argc: int 274 | 275 | let selfPtr = prototype.resolveSelf().reduceAddr() 276 | let resultPtr = prototype.resolveReturn().reducePtr() 277 | 278 | let args = prototype.genArgsList(addr argc, true) 279 | let varArgs = prototype.getVarArgs() 280 | 281 | result = prototype 282 | 283 | if varArgs.isNone(): 284 | result[^1] = genAst(ty, functionName, hash, argc, args, selfPtr, resultPtr) do: 285 | var p {.global.} = getBuiltinMethodPtr[ty](functionName, hash) 286 | var argPtrs: array[argc, GDExtensionConstTypePtr] = args 287 | 288 | p( 289 | cast[GDExtensionTypePtr](selfPtr), 290 | cast[ptr GDExtensionConstTypePtr](addr argPtrs), 291 | cast[GDExtensionTypePtr](resultPtr), 292 | cint(argc)) 293 | else: 294 | let varArgId = varArgs.unsafeGet()[0] 295 | 296 | result[^1] = genAst(ty, functionName, hash, argc, args, varArgId, selfPtr, resultPtr) do: 297 | var p {.global.} = getBuiltinMethodPtr[ty](functionName, hash) 298 | var argPtrs = @args 299 | 300 | for i in 0..high(varArgId): 301 | argPtrs &= getArgPointer varArgId[i] 302 | 303 | p( 304 | cast[GDExtensionTypePtr](selfPtr), 305 | cast[ptr GDExtensionConstTypePtr](if argPtrs.len() > 0: addr argPtrs[0] else: nil), 306 | cast[GDExtensionTypePtr](resultPtr), 307 | cint(argPtrs.len())) 308 | 309 | macro gd_builtin_set*(ty: typed; prototype: untyped) = 310 | let propertyName = prototype[0][1][0].strVal() 311 | 312 | let selfPtr = prototype.resolveSelf().reduceAddr() 313 | let valPtr = prototype[3][2][0] 314 | 315 | result = prototype 316 | result[^1] = genAst(propertyName, ty, selfPtr, valPtr): 317 | var p {.global.} = block: 318 | var gdFuncName = &propertyName 319 | 320 | gdInterfacePtr.variant_get_ptr_setter(ty.variantTypeId, addr gdFuncName) 321 | 322 | p( 323 | cast[GDExtensionTypePtr](selfPtr), 324 | cast[GDExtensionConstTypePtr](unsafeAddr valPtr)) 325 | 326 | func isKeyedIndex(node: NimNode): bool = 327 | node[3][2][^2].strVal == "Variant" 328 | 329 | func indexParams(proto: NimNode; setter: bool; fn, idxType, idxNode: ptr NimNode) = 330 | let idx = proto[3][2][0] 331 | 332 | if proto.isKeyedIndex(): 333 | fn[] = (if setter: "variant_get_ptr_keyed_setter" else: "variant_get_ptr_keyed_getter").ident() 334 | idxType[] = "GDExtensionConstTypePtr".bindSym() 335 | idxNode[] = newCall("unsafeAddr".ident(), idx) 336 | else: 337 | fn[] = (if setter: "variant_get_ptr_indexed_setter" else: "variant_get_ptr_indexed_getter").ident() 338 | idxType[] = "GDExtensionInt".bindSym() 339 | idxNode[] = idx 340 | 341 | macro gd_builtin_index_get*(ty: typed; prototype: untyped) = 342 | var fn: NimNode 343 | var idxType: NimNode 344 | var idxNode: NimNode 345 | 346 | prototype.indexParams(false, addr fn, addr idxType, addr idxNode) 347 | 348 | let selfPtr = prototype.resolveSelf().reduceAddr() 349 | let resultPtr = prototype.resolveReturn().reducePtr() 350 | 351 | result = prototype 352 | result[^1] = genAst(fn, ty, selfPtr, idxType, idxNode, resultPtr) do: 353 | var p {.global.} = gdInterfacePtr.fn(ty.variantTypeId) 354 | 355 | p( 356 | cast[GDExtensionConstTypePtr](selfPtr), 357 | cast[idxType](idxNode), 358 | cast[GDExtensionTypePtr](resultPtr)) 359 | 360 | macro gd_builtin_index_set*(ty: typed; prototype: untyped) = 361 | var fn: NimNode 362 | var idxType: NimNode 363 | var idxNode: NimNode 364 | 365 | prototype.indexParams(true, addr fn, addr idxType, addr idxNode) 366 | 367 | let selfPtr = prototype.resolveSelf().reduceAddr() 368 | let valId = prototype[3][^1][^3] 369 | 370 | result = prototype 371 | result[^1] = genAst(fn, ty, selfPtr, idxType, idxNode, valId) do: 372 | var p {.global.} = gdInterfacePtr.fn(ty.variantTypeId) 373 | 374 | p( 375 | cast[GDExtensionConstTypePtr](selfPtr), 376 | cast[idxType](idxNode), 377 | cast[GDExtensionConstTypePtr](unsafeAddr valId)) 378 | 379 | func toOperatorId(oper: string; unary: bool): GDExtensionVariantOperator = 380 | case oper 381 | of "==": result = GDEXTENSION_VARIANT_OP_EQUAL 382 | of "!=": result = GDEXTENSION_VARIANT_OP_NOT_EQUAL 383 | of "<": result = GDEXTENSION_VARIANT_OP_LESS 384 | of "<=": result = GDEXTENSION_VARIANT_OP_LESS_EQUAL 385 | of ">": result = GDEXTENSION_VARIANT_OP_GREATER 386 | of ">=": result = GDEXTENSION_VARIANT_OP_GREATER_EQUAL 387 | of "+": result = if not unary: GDEXTENSION_VARIANT_OP_ADD else: GDEXTENSION_VARIANT_OP_POSITIVE 388 | of "-": result = if not unary: GDEXTENSION_VARIANT_OP_SUBTRACT else: GDEXTENSION_VARIANT_OP_NEGATE 389 | of "*": result = GDEXTENSION_VARIANT_OP_MULTIPLY 390 | of "/": result = GDEXTENSION_VARIANT_OP_DIVIDE 391 | of "%": result = GDEXTENSION_VARIANT_OP_MODULE 392 | of "**": result = GDEXTENSION_VARIANT_OP_POWER 393 | of "<<": result = GDEXTENSION_VARIANT_OP_SHIFT_LEFT 394 | of ">>": result = GDEXTENSION_VARIANT_OP_SHIFT_RIGHT 395 | of "&": result = GDEXTENSION_VARIANT_OP_BIT_AND 396 | of "|": result = GDEXTENSION_VARIANT_OP_BIT_OR 397 | of "^": result = GDEXTENSION_VARIANT_OP_BIT_XOR 398 | of "~": result = GDEXTENSION_VARIANT_OP_BIT_NEGATE 399 | of "and": result = GDEXTENSION_VARIANT_OP_AND 400 | of "or": result = GDEXTENSION_VARIANT_OP_OR 401 | of "xor": result = GDEXTENSION_VARIANT_OP_XOR 402 | of "not": result = GDEXTENSION_VARIANT_OP_NOT 403 | of "in": result = GDEXTENSION_VARIANT_OP_IN 404 | else: 405 | debugEcho "Unknown operator " & oper 406 | assert false 407 | 408 | macro gd_builtin_operator*(ty: typed; prototype: untyped) = 409 | let isUnary = len(prototype[3]) < 3 and len(prototype[3][1]) < 4 410 | 411 | let lhsPtr = newCall("unsafeAddr".ident(), prototype[3][1][0]) 412 | let lhsTyp = prototype[3][1][^2] 413 | 414 | var rhsPtr = newNilLit() 415 | var rhsTyp = "Variant".bindSym() 416 | 417 | if not isUnary: 418 | rhsPtr = newCall("unsafeAddr".ident(), prototype[3][^1][^3]) 419 | rhsTyp = prototype[3][^1][^2] 420 | 421 | let rawOperatorName = prototype[0][1][0].strVal() 422 | let operatorId = rawOperatorName.toOperatorId(isUnary) 423 | 424 | result = prototype 425 | result[^1] = genAst(operatorId, lhsTyp, rhsTyp, lhsPtr, rhsPtr, result = ident"result") do: 426 | var p {.global.} = gdInterfacePtr.variant_get_ptr_operator_evaluator( 427 | cast[GDExtensionVariantOperator](operatorId), 428 | lhsTyp.variantTypeId, 429 | rhsTyp.variantTypeId) 430 | 431 | p(lhsPtr, rhsPtr, addr result) 432 | 433 | 434 | # Classes 435 | import ../nimrodot/ref_helper 436 | 437 | template constructResultObject[T](dest: typedesc[Ref[T]]; raw: T): Ref[T] = 438 | newRefShallow(raw) 439 | 440 | template constructResultObject[T](dest: typedesc[T]; raw: T): T = 441 | raw 442 | 443 | macro gd_class_ctor*(prototype: untyped) = 444 | let selfType = prototype.resolveReturn().reduceType() 445 | let fullSelfType = prototype.resolveReturn().fullType 446 | let selfTypeStr = selfType.strVal() 447 | 448 | result = prototype 449 | result[^1] = genAst(selfType, selfTypeStr, fullSelfType, result = ident"result") do: 450 | var name = &selfTypeStr 451 | 452 | constructResultObject( 453 | fullSelfType, 454 | cast[selfType](gdInterfacePtr.object_get_instance_binding( 455 | gdInterfacePtr.classdb_construct_object(addr name), 456 | gdTokenPtr, 457 | selfType.gdInstanceBindingCallbacks))) 458 | 459 | macro gd_class_singleton*(prototype: untyped) = 460 | let selfType = prototype.resolveReturn().reduceType() 461 | 462 | result = prototype 463 | result[^1] = genAst(selfType) do: 464 | var name = selfType.gdClassName() 465 | 466 | cast[selfType](gdInterfacePtr.object_get_instance_binding( 467 | gdInterfacePtr.global_get_singleton(addr name), 468 | gdTokenPtr, 469 | selfType.gdInstanceBindingCallbacks)) 470 | 471 | macro gd_class_method*(hash: static[int64]; prototype: untyped) = 472 | var argc: int 473 | let args = prototype.genArgsList(addr argc, true) 474 | 475 | var s = prototype.resolveSelf() 476 | var r = prototype.resolveReturn() 477 | 478 | result = prototype 479 | result[^1] = genAst( 480 | selfType = s.reduceType(), 481 | selfPtr = s.reducePtr(), 482 | resultPtr = r.reducePtr(), 483 | methodName = prototype.getNameFromProto(), 484 | hash, argc, args): 485 | 486 | var p {.global.} = getClassMethodBindPtr($selfType, methodName, hash) 487 | var fixedArgs: array[argc, GDExtensionConstTypePtr] = args 488 | 489 | gdInterfacePtr.object_method_bind_ptrcall( 490 | p, 491 | cast[GDExtensionObjectPtr](selfPtr), 492 | cast[ptr GDExtensionConstTypePtr](addr fixedArgs), 493 | cast[GDExtensionTypePtr](resultPtr)) 494 | 495 | macro gd_class_method_obj*(hash: static[int64]; prototype: untyped) = 496 | var argc: int 497 | let args = prototype.genArgsList(addr argc, true) 498 | 499 | var s = prototype.resolveSelf() 500 | var r = prototype.resolveReturn() 501 | 502 | result = prototype 503 | result[^1] = genAst( 504 | selfType = s.reduceType(), 505 | selfPtr = s.reducePtr(), 506 | methodName = prototype.getNameFromProto(), 507 | retType = r.reduceType(), 508 | fullRetType = r.fullType, 509 | hash, argc, args): 510 | 511 | var p {.global.} = getClassMethodBindPtr($selfType, methodName, hash) 512 | var fixedArgs: array[argc, GDExtensionConstTypePtr] = args 513 | 514 | var resultPtr: pointer = nil 515 | 516 | gdInterfacePtr.object_method_bind_ptrcall( 517 | p, 518 | cast[GDExtensionObjectPtr](selfPtr), 519 | cast[ptr GDExtensionConstTypePtr](addr fixedArgs), 520 | addr resultPtr) 521 | 522 | let instancePtr = cast[retType](gdInterfacePtr.object_get_instance_binding( 523 | resultPtr, 524 | gdTokenPtr, 525 | retType.gdInstanceBindingCallbacks)) 526 | 527 | constructResultObject(fullRetType, instancePtr) 528 | 529 | template gd_name*(n: string) {.pragma.} 530 | 531 | macro gd_class_method_virtual*( 532 | name: static[string]; 533 | vtableField: typed; 534 | prototype: untyped) = 535 | 536 | let vtableType = vtableField[0] 537 | let selfPtr = prototype.resolveSelf().reduceVtPtr() 538 | 539 | # Generate a new call passing all arguments passed to this function into the 540 | # function pointer from the vtable 541 | let fnPtr = genSym(ident="fnPtr") 542 | var delegatedCall = newCall(fnPtr) 543 | 544 | for bindings in prototype[3][1..^1]: 545 | for def in bindings[0..^3]: 546 | delegatedCall &= def 547 | 548 | result = prototype 549 | result[^1] = genAst(fnPtr, selfPtr, vtableType, vtableField = vtableField[1], delegatedCall) do: 550 | let vtablePtr = cast[ptr vtableType](selfPtr) 551 | let fnPtr = vtablePtr.vtableField 552 | 553 | if fnPtr.isNil(): 554 | return 555 | 556 | delegatedCall 557 | 558 | macro gd_builtin_get*(ty: typed; prototype: untyped) = 559 | let propertyName = prototype.getNameFromProto() 560 | 561 | let selfPtr = prototype.resolveSelf().reduceAddr() 562 | let resultPtr = prototype.resolveReturn().reducePtr() 563 | 564 | result = prototype 565 | result[^1] = genAst(propertyName, ty, selfPtr, resultPtr): 566 | var p {.global.} = block: 567 | var gdFuncName = &propertyName 568 | 569 | gdInterfacePtr.variant_get_ptr_getter(ty.variantTypeId, addr gdFuncName) 570 | 571 | p( 572 | cast[GDExtensionConstTypePtr](selfPtr), 573 | cast[GDExtensionTypePtr](resultPtr)) 574 | 575 | # Constants 576 | 577 | proc gd_constant*[K, T](name: static[string]): T = 578 | var gdName = &name 579 | var resVariant: Variant 580 | 581 | gdInterfacePtr.variant_get_constant_value( 582 | cast[GDExtensionVariantType](T.variantTypeId), 583 | addr gdName, 584 | addr resVariant) 585 | 586 | resVariant.castTo(K) -------------------------------------------------------------------------------- /nimrodot/hooks.nim: -------------------------------------------------------------------------------- 1 | import std/genasts 2 | import std/macros 3 | 4 | import ./interface_ptrs 5 | 6 | macro generateBuiltinHooks*(T: typedesc; copyCtorIdx: static[int]) = 7 | result = genAst(T, copyCtorIdx): 8 | proc `=destroy`*(st: var T) = 9 | var p {.global.} = gdInterfacePtr.variant_get_ptr_destructor( 10 | T.variantTypeId) 11 | 12 | p(cast[GDExtensionTypePtr](addr st)) 13 | 14 | proc `=copy`*(a: var T; b: T) = 15 | `=destroy`(a) 16 | a.wasMoved() 17 | 18 | var copyCtor {.global.} = gdInterfacePtr.variant_get_ptr_constructor( 19 | T.variantTypeId, copyCtorIdx) 20 | 21 | let args: array[1, GDExtensionConstTypePtr] = [ 22 | cast[GDExtensionConstTypePtr](unsafeAddr b) 23 | ] 24 | 25 | copyCtor(addr a, cast[ptr GDExtensionConstTypePtr](unsafeAddr args)) -------------------------------------------------------------------------------- /nimrodot/interface_ptrs.nim: -------------------------------------------------------------------------------- 1 | import ./ffi 2 | 3 | var gdInterfacePtr*: ptr GDExtensionInterface = nil 4 | var gdTokenPtr*: GDExtensionClassLibraryPtr = nil 5 | -------------------------------------------------------------------------------- /nimrodot/internal/bindwrapper.nim: -------------------------------------------------------------------------------- 1 | type 2 | CallMarshallingError = object of CatchableError 3 | argument: int32 4 | expected: int32 5 | 6 | proc raiseArgError(argpos: int32, expectedType: GDExtensionVariantType) = 7 | var err = newException(CallMarshallingError, "") 8 | 9 | err.argument = argpos 10 | err.expected = expectedType.int32 11 | 12 | raise err 13 | 14 | proc tryCastTo*[T](arg: Variant; _: typedesc[T]; argPos: var int32): T = 15 | try: 16 | result = arg.castTo(T) 17 | except VariantCastException: 18 | raiseArgError(argPos, T.variantTypeId) 19 | 20 | inc argPos 21 | 22 | # TODO: We need some more complex type converters. For most of these we can 23 | # simply blit over whatever Godot gives us, but for things like Ref[T] 24 | # we need to may need to use ref_get_object and ref_set_object. 25 | func argFromPointer[T](p: GDExtensionConstTypePtr): T = 26 | copyMem(addr result, p, sizeOf(T)) 27 | 28 | # Call a function with a number of Variant pointers (bindcall) 29 | macro callBindFunc( 30 | def: typed; 31 | argsArray: ptr UncheckedArray[ptr Variant]; 32 | argc: typed; 33 | argPos: int32; 34 | self: typed = nil): auto = 35 | let typedFunc = def.getTypeInst()[0] 36 | 37 | var argsStart = 1 38 | 39 | result = newTree(nnkCall, def) 40 | 41 | if len(typedFunc) > 1: 42 | if self.kind != nnkNilLit: 43 | result &= newTree(nnkBracketExpr, self) 44 | 45 | inc argsStart 46 | 47 | for i, arg in typedFunc[argsStart..^1]: 48 | if arg[^2].isVarArg(): 49 | # If we hit a varargs[T] parameter, generate a preamble statement to fill a seq[] 50 | # and wrap our original result into a block statement. 51 | let varArgsId = genSym(nskVar, "vargs") 52 | 53 | result.add varArgsId 54 | 55 | return genAst(T = arg[^2][1], start = i, argsArray, argc, varArgsId, doCall = result) do: 56 | block: 57 | var varArgsId = newSeqOfCap[typeOf T](argc - start) 58 | 59 | for i in start .. argc - 1: 60 | varArgsId &= maybeDowncast[T](argsArray[i][].tryCastTo(mapBuiltinType(typeOf T), argPos)) 61 | inc argPos 62 | 63 | doCall 64 | 65 | result.add genAst(T = arg[^2], argsArray, i) do: 66 | maybeDowncast[T](argsArray[i][].tryCastTo(mapBuiltinType(typeOf T), argPos)) 67 | 68 | # Call a function with a number of builtin pointers (ptrcall) 69 | # 70 | # This is highly unsafe of course, but at this point we have to trust Godot not 71 | # to send us bad data. If it does, we do the same thing it does when we do that: 72 | # crash. 73 | func stuffArguments( 74 | call: NimNode; 75 | typedFunc: NimNode; 76 | offset: static[int]; 77 | argsArray: NimNode) = 78 | 79 | for i, arg in typedFunc[offset..^1]: 80 | let argBody = if arg[^2].isVarArg(): 81 | # Cannot be done using ptrcall, so we leave it empty in case 82 | # a vararg function is ever called using ptrcall. 83 | break 84 | else: 85 | genAst(T = arg[^2], argsArray, i): 86 | maybeDowncast[T](argFromPointer[mapBuiltinType(typeOf T)](argsArray[i])) 87 | 88 | call &= argBody 89 | 90 | macro callPtrFunc( 91 | def: typed; 92 | self: typed; 93 | argsArray: ptr UncheckedArray[GDExtensionConstTypePtr]): auto = 94 | let typedFunc = def.getTypeInst()[0] 95 | 96 | result = newTree(nnkCall, def) 97 | 98 | if len(typedFunc) > 1: 99 | # In case our receiver func takes a parent object of T, we cast it. 100 | result &= newTree(nnkCast, typedFunc[1][^2], self) 101 | result.stuffArguments(typedFunc, 2, argsArray) 102 | 103 | macro callPtrFunc( 104 | def: typed; 105 | argsArray: ptr UncheckedArray[GDExtensionConstTypePtr]): auto = 106 | let typedFunc = def.getTypeInst()[0] 107 | 108 | result = newTree(nnkCall, def) 109 | 110 | if len(typedFunc) > 1: 111 | result.stuffArguments(typedFunc, 1, argsArray) 112 | 113 | proc invoke_ptrcall[T]( 114 | callable: auto; 115 | isStatic: static[bool]; 116 | instance: GDExtensionClassInstancePtr; 117 | args: ptr GDExtensionConstTypePtr; 118 | returnPtr: GDExtensionTypePtr) {.cdecl.} = 119 | 120 | let argArray = cast[ptr UncheckedArray[GDExtensionConstTypePtr]](args) 121 | 122 | type R = callable.procReturn() 123 | 124 | # Ugly double duplication here, sorry. 125 | when isStatic: 126 | when R is void: 127 | callable.callPtrFunc(argArray) 128 | else: 129 | cast[ptr mapBuiltinType(typeOf R)](returnPtr)[] = callable.callPtrFunc(argArray) 130 | else: 131 | let nimInst = cast[ptr T](instance) 132 | 133 | when R is void: 134 | callable.callPtrFunc(nimInst, argArray) 135 | else: 136 | cast[ptr mapBuiltinType(typeOf R)](returnPtr)[] = callable.callPtrFunc(nimInst, argArray) 137 | 138 | proc invoke_bindcall[T](callable: auto; 139 | isStatic: static[bool]; 140 | instance: GDExtensionClassInstancePtr; 141 | args: ptr GDExtensionConstVariantPtr; 142 | argc: GDExtensionInt; 143 | ret: GDExtensionVariantPtr; 144 | error: ptr GDExtensionCallError) {.cdecl.} = 145 | 146 | let argArray = cast[ptr UncheckedArray[ptr Variant]](args) 147 | var argPos = 0'i32 148 | 149 | type 150 | R = callable.procReturn() 151 | 152 | const arity = callable.procArity(isStatic) 153 | 154 | if argc < arity.argCount: 155 | error[].error = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS 156 | error[].argument = int32(arity.argCount) 157 | error[].expected = int32(arity.argCount) 158 | 159 | return 160 | elif argc > arity.argCount and not arity.variadic: 161 | error[].error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS 162 | error[].argument = int32(arity.argCount) 163 | error[].expected = int32(arity.argCount) 164 | 165 | return 166 | 167 | error[].error = GDEXTENSION_CALL_OK 168 | 169 | try: 170 | # Yet more ugly double duplication here. 171 | when isStatic: 172 | when R is void: 173 | callable.callBindFunc(argArray, argc, argPos) 174 | else: 175 | let returnValue = cast[ptr Variant](ret) 176 | 177 | returnValue[] = %maybeDowncast[mapBuiltinType R](callable.callBindFunc(argArray, argc, argPos)) 178 | else: 179 | let nimInst = cast[ptr T](instance) 180 | 181 | when R is void: 182 | callable.callBindFunc(argArray, argc, argPos, nimInst) 183 | else: 184 | let returnValue = cast[ptr Variant](ret) 185 | 186 | returnValue[] = %maybeDowncast[mapBuiltinType R](callable.callBindFunc(argArray, argc, argPos, nimInst)) 187 | 188 | except CallMarshallingError as cme: 189 | error[].error = GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT 190 | error[].argument = cme.argument 191 | error[].expected = cme.expected 192 | 193 | except CatchableError: 194 | # For the lack of a better option 195 | error[].error = GDEXTENSION_CALL_ERROR_INVALID_METHOD 196 | -------------------------------------------------------------------------------- /nimrodot/internal/typeinfo.nim: -------------------------------------------------------------------------------- 1 | # This is a very boilerplatey part that would poison the readability of the 2 | # classdb module, so we yank it in. 3 | 4 | # Generic helpers 5 | macro procReturn(p: typed): typedesc = 6 | ## Given a proc type, return its return type (or typedesc[void]) 7 | let procType = p.getTypeImpl() 8 | 9 | procType.expectKind(nnkProcTy) 10 | 11 | if procType[0][0].kind == nnkEmpty: 12 | genAst: void 13 | else: 14 | genAst(R = procType[0][0]): R 15 | 16 | macro procArity(p: typed; isStatic: static[bool]): auto = 17 | ## Given a proc type, return its arity 18 | let procType = p.getTypeImpl() 19 | 20 | procType.expectKind(nnkProcTy) 21 | 22 | var minArgc = 0 23 | var variadic = false 24 | 25 | for arg in procType[0][1..^1]: 26 | if arg[1].kind == nnkBracketExpr and arg[1][0].strVal() == "varargs": 27 | variadic = true 28 | 29 | break 30 | 31 | inc minArgc 32 | 33 | if not isStatic: 34 | dec minArgc 35 | 36 | genAst(minArgc, isVar = variadic): 37 | (argCount: minArgc, variadic: isvar) 38 | 39 | # Property Helpers 40 | func propertyHint(_: typedesc): auto = phiNone 41 | func propertyUsage(_: typedesc): auto = pufDefault 42 | func propertyUsage(_: typedesc[Variant]): auto = ord(pufDefault) or ord(pufNilIsVariant) 43 | 44 | # Type Metadata Helpers 45 | func typeMetaData(_: typedesc): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE 46 | func typeMetaData(_: typedesc[int8]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 47 | func typeMetaData(_: typedesc[int16]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16 48 | func typeMetaData(_: typedesc[int32]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32 49 | func typeMetaData(_: typedesc[int64]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64 50 | func typeMetaData(_: typedesc[uint8]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8 51 | func typeMetaData(_: typedesc[uint16]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16 52 | func typeMetaData(_: typedesc[uint32]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32 53 | func typeMetaData(_: typedesc[uint64]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64 54 | func typeMetaData(_: typedesc[float32]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT 55 | func typeMetaData(_: typedesc[float64 | float]): auto = GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE 56 | 57 | func typeMetaData(_: typedesc[int | uint]): auto = 58 | if (sizeOf int) == 4: 59 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32 60 | else: 61 | GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64 62 | 63 | # isVarArg and getProcProps are used registerMethod in order to collect all 64 | # the information Godot wants out of the to be registered funtion pointer. 65 | func isVarArg(m: NimNode): bool = 66 | result = m.kind == nnkBracketExpr and m[0].strVal() == "varargs" 67 | 68 | func isSelf(ft: NimNode; T: NimNode): bool = 69 | T.getTypeInst()[1] == ft 70 | 71 | macro isStatic(M: typed; T: typedesc): bool = 72 | let procDef = M.getType()[1].getTypeImpl()[0] 73 | 74 | var isStatic = true 75 | 76 | if len(procDef) > 1: 77 | isStatic = not procDef[1][^2].isSelf(T) 78 | 79 | genAst(isStatic): 80 | isStatic 81 | 82 | proc getPropertyInfo*[T](name: static[string] = ""): GDExtensionPropertyInfo = 83 | GDExtensionPropertyInfo( 84 | `type`: variantTypeId(T), 85 | name: staticStringName(name), 86 | class_name: gdClassName(T), 87 | hint: uint32(propertyHint(T)), 88 | hint_string: staticStringName(""), 89 | usage: uint32(propertyUsage(T))) 90 | 91 | 92 | macro getProcProps(M: typed; T: typedesc): auto = 93 | let procDef = M.getType()[1].getTypeImpl()[0] 94 | 95 | var argc = 0 96 | 97 | var args = newTree(nnkBracket) 98 | var argsMeta = newTree(nnkBracket) 99 | var isStatic = true 100 | var isVararg = false 101 | var procFlags = newTree(nnkCurly, ident"GDEXTENSION_METHOD_FLAGS_DEFAULT") 102 | 103 | let rval = if procDef[0].kind == nnkEmpty: 104 | genAst() do: 105 | none (GDExtensionPropertyInfo, GDExtensionClassMethodArgumentMetadata) 106 | else: 107 | genAst(R = procDef[0]) do: 108 | some (getPropertyInfo[R](), typeMetaData(typeOf R)) 109 | 110 | if len(procDef) > 1: 111 | let offset = if procDef[1][^2].isSelf(T): 2 else: 1 112 | 113 | isStatic = offset == 1 114 | 115 | for defs in procDef[offset..^1]: 116 | for binding in defs[0..^3]: 117 | if defs[^2].isVarArg: 118 | isVararg = true 119 | 120 | break 121 | 122 | inc argc 123 | 124 | argsMeta.add genAst(P = defs[^2]) do: 125 | typeMetaData(typeOf P) 126 | 127 | args.add genAst(n = binding.strVal(), P = defs[^2]) do: 128 | getPropertyInfo[P](n) 129 | 130 | if isStatic: procFlags &= ident"GDEXTENSION_METHOD_FLAG_STATIC" 131 | if isVararg: procFlags &= ident"GDEXTENSION_METHOD_FLAG_VARARG" 132 | 133 | result = genAst( 134 | procArgc = argc, 135 | procArgs = args, 136 | procArgsMeta = argsMeta, 137 | procFlags, 138 | rval): 139 | tuple[pargc: GDExtensionInt, 140 | pargs: array[procArgc, GDExtensionPropertyInfo], 141 | pmeta: array[procArgc, GDExtensionClassMethodArgumentMetadata], 142 | retval: Option[(GDExtensionPropertyInfo, GDExtensionClassMethodArgumentMetadata)], 143 | pflags: system.set[GDExtensionClassMethodFlags]]( 144 | pargc: GDExtensionInt(procArgc), 145 | pargs: procArgs, 146 | pmeta: procArgsMeta, 147 | retval: rval, 148 | pflags: procFlags) -------------------------------------------------------------------------------- /nimrodot/ref_helper.nim: -------------------------------------------------------------------------------- 1 | type 2 | Ref*[T] = object 3 | ## A managed reference to a Godot class (extension class or user defined). 4 | reference: T 5 | 6 | Owned*[T] = object 7 | ## A managed, unique reference to a Godot class (extension class or user defined). 8 | reference: T 9 | 10 | import ./interface_ptrs 11 | import ./utils 12 | import ./ffi 13 | import ./builtins/types/stringname 14 | import ./classes/types/"object" 15 | 16 | # Re-declare these so we don't have to cyclic import refcounted.nim and gdffi. 17 | # XXX: KEEP IN SYNC. 18 | proc getClassMethodBindPtr(cls, meth: static[string]; hash: static[int64]): GDExtensionMethodBindPtr = 19 | var gdClassName = &cls 20 | var gdMethName = &meth 21 | 22 | gdInterfacePtr.classdb_get_method_bind(addr gdClassName, addr gdMethName, hash) 23 | 24 | proc upRef[T](self: T): bool = 25 | let mb {.global.} = getClassMethodBindPtr("RefCounted", "reference", 2240911060) 26 | 27 | gdInterfacePtr.object_method_bind_ptrcall( 28 | mb, 29 | self.opaque, 30 | nil, 31 | addr result) 32 | 33 | proc downRef[T](self: T): bool = 34 | let mb {.global.} = getClassMethodBindPtr("RefCounted", "unreference", 2240911060) 35 | 36 | gdInterfacePtr.object_method_bind_ptrcall( 37 | mb, 38 | self.opaque, 39 | nil, 40 | addr result) 41 | 42 | 43 | proc `=destroy`*[T](r: var Ref[T]) = 44 | if r.reference == nil: 45 | return 46 | 47 | if r[].downRef(): 48 | gdInterfacePtr.object_destroy(r.reference.opaque) 49 | 50 | r.reference = nil 51 | 52 | proc `=sink`*[T](dest: var Ref[T]; source: Ref[T]) = 53 | `=destroy`(dest) 54 | 55 | dest.wasMoved() 56 | dest.reference = source.reference 57 | 58 | proc `=copy`*[T](dest: var Ref[T]; source: Ref[T]) = 59 | if dest.reference == source.reference: 60 | return 61 | 62 | discard source[].upRef() 63 | 64 | `=destroy`(dest) 65 | dest.wasMoved() 66 | dest.reference = source.reference 67 | 68 | proc newRefShallow*[T](reference: T): Ref[T] = 69 | ## Create a shallow reference to T. That is, a reference that doesn't 70 | ## count its own construction. We use this in one place: where Godot 71 | ## returns us a refcounted object from a return value, in which case 72 | ## the first reference is implied and this Ref[] will be the initial 73 | ## owner. 74 | assert T is ptr 75 | 76 | Ref[T](reference: reference) 77 | 78 | proc newRef*[T](reference: T): Ref[T] = 79 | ## Create a reference to T, incrementing the reference count upon doing so. 80 | result = newRefShallow(reference) 81 | discard result[].upRef() 82 | 83 | proc castRef*[T, U](r: sink Ref[T]; _: typedesc[U]): Ref[U] = 84 | ## Casts a Ref[T] to a Ref[U], raising an exception in case the transition 85 | ## is not allowed. 86 | 87 | # TBD: Cache these, since they never change for all possible U. 88 | var clsName = U.gdClassName() 89 | var clsTag = gdInterfacePtr.classdb_get_class_tag(clsName) 90 | 91 | let castedPtr = gdInterfacePtr.object_cast_to(r.reference.opaque, clsTag) 92 | 93 | if castedPtr.isNil(): 94 | raise newException(ValueError, "Cannot cast object to type " & $T & " to " & $U) 95 | 96 | # N.B. we don't make use of castedPtr for now, since it's the same as the original. 97 | newRef(cast[U](r.reference)) 98 | 99 | proc `[]`*[T](r: Ref[T]): lent T = 100 | ## Return a lent reference to the contained value. 101 | r.reference 102 | 103 | converter toObject*[T](x: sink Ref[T]): Object = 104 | x[] 105 | 106 | # Owned[T] implementation 107 | 108 | proc makeOwned*[T](reference: T): Owned[T] = 109 | assert T is ptr 110 | 111 | Owned[T](reference: reference) 112 | 113 | proc `=destroy`*[T](r: var Owned[T]) = 114 | if r.reference == nil: 115 | return 116 | 117 | gdInterfacePtr.object_destroy(r.reference.opaque) 118 | r.reference = nil 119 | 120 | proc `=sink`*[T](dest: var Owned[T]; source: Owned[T]) = 121 | `=destroy`(dest) 122 | 123 | dest.wasMoved() 124 | dest.reference = source.reference 125 | 126 | proc `=copy`*[T](dest: var Owned[T]; source: Owned[T]) {.error.} 127 | 128 | proc `[]`*[T: ptr](r: Owned[T]): lent T = r.reference -------------------------------------------------------------------------------- /nimrodot/utils.nim: -------------------------------------------------------------------------------- 1 | import ./interface_ptrs 2 | import ./ffi 3 | 4 | import ./builtins/types/["string", stringname] 5 | 6 | # Use Godot's "StringName(String)" constructor. 7 | proc stringToStringName(str: String): StringName = 8 | var ctor {.global.} = # let's hope this doesn't change v 9 | gdInterfacePtr.variant_get_ptr_constructor(GDEXTENSION_VARIANT_TYPE_STRING_NAME, 2) 10 | 11 | var args = [unsafeAddr str] 12 | 13 | ctor(cast[GDExtensionTypePtr](addr result), cast[ptr GDExtensionConstTypePtr](addr args)) 14 | 15 | 16 | proc `$`*(str: String): string = 17 | let strLen = gdInterfacePtr.string_to_utf8_chars(unsafeAddr str, nil, 0) 18 | result = newString(strLen) 19 | 20 | if strLen > 0: 21 | discard gdInterfacePtr.string_to_utf8_chars( 22 | unsafeAddr str, 23 | cast[cstring](addr result[0]), 24 | strLen) 25 | 26 | proc newString*(native: string): String = 27 | gdInterfacePtr.string_new_with_utf8_chars(addr result, cstring(native)) 28 | 29 | proc newStringName*(native: string): StringName = 30 | var interm = newString(native) 31 | 32 | gdInterfacePtr.string_new_with_utf8_chars(addr interm, cstring(native)) 33 | result = stringToStringName(interm) 34 | 35 | 36 | converter toStringName*(src: string): StringName = 37 | newStringName(src) 38 | 39 | converter toString*(src: string): String = 40 | newString(src) 41 | 42 | # Convenience converter that mirrors Godot's syntax. 43 | template `&`*(native: string): StringName = 44 | newStringName(native) 45 | 46 | # TODO: For all variants, add `$` with variant -> str conversion --------------------------------------------------------------------------------