cbuilderdecls.nim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. type VarKind = enum
  2. Local
  3. Global
  4. Threadvar
  5. Const
  6. AlwaysConst ## const even on C++
  7. proc addVarHeader(builder: var Builder, kind: VarKind) =
  8. ## adds modifiers for given var kind:
  9. ## Local has no modifier
  10. ## Global has `static` modifier
  11. ## Const has `static NIM_CONST` modifier
  12. ## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
  13. ## Threadvar is unimplemented
  14. case kind
  15. of Local: discard
  16. of Global:
  17. builder.add("static ")
  18. of Const:
  19. builder.add("static NIM_CONST ")
  20. of AlwaysConst:
  21. builder.add("static const ")
  22. of Threadvar:
  23. doAssert false, "unimplemented"
  24. proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
  25. ## adds a variable declaration to the builder
  26. builder.addVarHeader(kind)
  27. builder.add(typ)
  28. builder.add(" ")
  29. builder.add(name)
  30. if initializer.len != 0:
  31. builder.add(" = ")
  32. builder.add(initializer)
  33. builder.add(";\n")
  34. template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
  35. ## adds a variable declaration to the builder, with the `body` building the type
  36. builder.addVarHeader(kind)
  37. body
  38. builder.add(" ")
  39. builder.add(name)
  40. builder.add(";\n")
  41. template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
  42. typeBody, initializerBody: typed) =
  43. ## adds a variable declaration to the builder, with `typeBody` building the type, and
  44. ## `initializerBody` building the initializer. initializer must be provided
  45. builder.addVarHeader(kind)
  46. typeBody
  47. builder.add(" ")
  48. builder.add(name)
  49. builder.add(" = ")
  50. initializerBody
  51. builder.add(";\n")
  52. proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
  53. ## adds an array variable declaration to the builder
  54. builder.addVarHeader(kind)
  55. builder.add(elementType)
  56. builder.add(" ")
  57. builder.add(name)
  58. builder.add("[")
  59. builder.addInt(len)
  60. builder.add("]")
  61. if initializer.len != 0:
  62. builder.add(" = ")
  63. builder.add(initializer)
  64. builder.add(";\n")
  65. template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
  66. ## adds an array variable declaration to the builder with the initializer built according to `body`
  67. builder.addVarHeader(kind)
  68. builder.add(elementType)
  69. builder.add(" ")
  70. builder.add(name)
  71. builder.add("[")
  72. builder.addInt(len)
  73. builder.add("] = ")
  74. body
  75. builder.add(";\n")
  76. template addTypedef(builder: var Builder, name: string, typeBody: typed) =
  77. ## adds a typedef declaration to the builder with name `name` and type as
  78. ## built in `typeBody`
  79. builder.add("typedef ")
  80. typeBody
  81. builder.add(" ")
  82. builder.add(name)
  83. builder.add(";\n")
  84. type
  85. StructInitializerKind = enum
  86. siOrderedStruct ## struct constructor, but without named fields on C
  87. siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
  88. siArray ## array constructor
  89. siWrapper ## wrapper for a single field, generates it verbatim
  90. StructInitializer = object
  91. ## context for building struct initializers, i.e. `{ field1, field2 }`
  92. kind: StructInitializerKind
  93. ## if true, fields will not be named, instead values are placed in order
  94. needsComma: bool
  95. proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
  96. ## starts building a struct initializer, i.e. braced initializer list
  97. result = StructInitializer(kind: kind, needsComma: false)
  98. if kind != siWrapper:
  99. builder.add("{")
  100. template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
  101. ## adds a field to a struct initializer, with the value built in `valueBody`
  102. if constr.needsComma:
  103. assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
  104. builder.add(", ")
  105. else:
  106. constr.needsComma = true
  107. case constr.kind
  108. of siArray, siWrapper:
  109. # no name, can just add value
  110. valueBody
  111. of siOrderedStruct:
  112. # no name, can just add value on C
  113. assert name.len != 0, "name has to be given for struct initializer field"
  114. valueBody
  115. of siNamedStruct:
  116. assert name.len != 0, "name has to be given for struct initializer field"
  117. builder.add(".")
  118. builder.add(name)
  119. builder.add(" = ")
  120. valueBody
  121. proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
  122. ## finishes building a struct initializer
  123. if constr.kind != siWrapper:
  124. builder.add("}")
  125. template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
  126. ## builds a struct initializer, i.e. `{ field1, field2 }`
  127. ## a `var StructInitializer` must be declared and passed as a parameter so
  128. ## that it can be used with `addField`
  129. constr = builder.initStructInitializer(kind)
  130. body
  131. builder.finishStructInitializer(constr)
  132. proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
  133. ## adds a field inside a struct/union type
  134. obj.add('\t')
  135. obj.add(typ)
  136. obj.add(" ")
  137. obj.add(name)
  138. if isFlexArray:
  139. obj.add("[SEQ_DECL_SIZE]")
  140. if initializer.len != 0:
  141. obj.add(initializer)
  142. obj.add(";\n")
  143. proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
  144. ## adds an array field inside a struct/union type
  145. obj.add('\t')
  146. obj.add(elementType)
  147. obj.add(" ")
  148. obj.add(name)
  149. obj.add("[")
  150. obj.addInt(len)
  151. obj.add("]")
  152. if initializer.len != 0:
  153. obj.add(initializer)
  154. obj.add(";\n")
  155. proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
  156. ## adds an field inside a struct/union type, based on an `skField` symbol
  157. obj.add('\t')
  158. if field.alignment > 0:
  159. obj.add("NIM_ALIGN(")
  160. obj.addInt(field.alignment)
  161. obj.add(") ")
  162. obj.add(typ)
  163. if sfNoalias in field.flags:
  164. obj.add(" NIM_NOALIAS")
  165. obj.add(" ")
  166. obj.add(name)
  167. if isFlexArray:
  168. obj.add("[SEQ_DECL_SIZE]")
  169. if field.bitsize != 0:
  170. obj.add(":")
  171. obj.addInt(field.bitsize)
  172. if initializer.len != 0:
  173. obj.add(initializer)
  174. obj.add(";\n")
  175. type
  176. BaseClassKind = enum
  177. ## denotes how and whether or not the base class/RTTI should be stored
  178. bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
  179. StructBuilderInfo = object
  180. ## context for building `struct` types
  181. baseKind: BaseClassKind
  182. named: bool
  183. preFieldsLen: int
  184. proc structOrUnion(t: PType): Snippet =
  185. let t = t.skipTypes({tyAlias, tySink})
  186. if tfUnion in t.flags: "union"
  187. else: "struct"
  188. proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
  189. result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
  190. obj.add("struct")
  191. if result.named:
  192. obj.add(" ")
  193. obj.add(name)
  194. if baseType.len != 0:
  195. if m.compileToCpp:
  196. result.baseKind = bcCppInherit
  197. else:
  198. result.baseKind = bcSupField
  199. if result.baseKind == bcCppInherit:
  200. obj.add(" : public ")
  201. obj.add(baseType)
  202. obj.add(" ")
  203. obj.add("{\n")
  204. result.preFieldsLen = obj.len
  205. if result.baseKind == bcSupField:
  206. obj.addField(name = "Sup", typ = baseType)
  207. proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
  208. if info.baseKind == bcNone and info.preFieldsLen == obj.len:
  209. # no fields were added, add dummy field
  210. obj.addField(name = "dummy", typ = "char")
  211. if info.named:
  212. obj.add("};\n")
  213. else:
  214. obj.add("}")
  215. template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
  216. ## builds a struct type not based on a Nim type with fields according to `body`,
  217. ## `name` can be empty to build as a type expression and not a statement
  218. let info = startSimpleStruct(obj, m, name, baseType)
  219. body
  220. finishSimpleStruct(obj, m, info)
  221. proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
  222. result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
  223. if tfPacked in t.flags:
  224. if hasAttribute in CC[m.config.cCompiler].props:
  225. obj.add(structOrUnion(t))
  226. obj.add(" __attribute__((__packed__))")
  227. else:
  228. obj.add("#pragma pack(push, 1)\n")
  229. obj.add(structOrUnion(t))
  230. else:
  231. obj.add(structOrUnion(t))
  232. if result.named:
  233. obj.add(" ")
  234. obj.add(name)
  235. if t.kind == tyObject:
  236. if t.baseClass == nil:
  237. if lacksMTypeField(t):
  238. result.baseKind = bcNone
  239. elif optTinyRtti in m.config.globalOptions:
  240. result.baseKind = bcNoneTinyRtti
  241. else:
  242. result.baseKind = bcNoneRtti
  243. elif m.compileToCpp:
  244. result.baseKind = bcCppInherit
  245. else:
  246. result.baseKind = bcSupField
  247. elif baseType.len != 0:
  248. if m.compileToCpp:
  249. result.baseKind = bcCppInherit
  250. else:
  251. result.baseKind = bcSupField
  252. if result.baseKind == bcCppInherit:
  253. obj.add(" : public ")
  254. obj.add(baseType)
  255. obj.add(" ")
  256. obj.add("{\n")
  257. result.preFieldsLen = obj.len
  258. case result.baseKind
  259. of bcNone:
  260. # rest of the options add a field or don't need it due to inheritance,
  261. # we need to add the dummy field for uncheckedarray ahead of time
  262. # so that it remains trailing
  263. if t.itemId notin m.g.graph.memberProcsPerType and
  264. t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
  265. t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
  266. # only consists of flexible array field, add *initial* dummy field
  267. obj.addField(name = "dummy", typ = "char")
  268. of bcCppInherit: discard
  269. of bcNoneRtti:
  270. obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
  271. of bcNoneTinyRtti:
  272. obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
  273. of bcSupField:
  274. obj.addField(name = "Sup", typ = baseType)
  275. proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
  276. if info.baseKind == bcNone and info.preFieldsLen == obj.len and
  277. t.itemId notin m.g.graph.memberProcsPerType:
  278. # no fields were added, add dummy field
  279. obj.addField(name = "dummy", typ = "char")
  280. if info.named:
  281. obj.add("};\n")
  282. else:
  283. obj.add("}")
  284. if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
  285. obj.add("#pragma pack(pop)\n")
  286. template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
  287. ## builds a struct type directly based on `typ` with fields according to `body`,
  288. ## `name` can be empty to build as a type expression and not a statement
  289. let info = startStruct(obj, m, typ, name, baseType)
  290. body
  291. finishStruct(obj, m, typ, info)
  292. template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
  293. ## adds a field with a `struct { ... }` type, building the fields according to `body`
  294. obj.add('\t')
  295. if tfPacked in parentTyp.flags:
  296. if hasAttribute in CC[m.config.cCompiler].props:
  297. obj.add("struct __attribute__((__packed__)) {\n")
  298. else:
  299. obj.add("#pragma pack(push, 1)\nstruct {")
  300. else:
  301. obj.add("struct {\n")
  302. body
  303. obj.add("} ")
  304. obj.add(fieldName)
  305. obj.add(";\n")
  306. if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
  307. result.add("#pragma pack(pop)\n")
  308. template addAnonUnion(obj: var Builder; body: typed) =
  309. ## adds an anonymous union i.e. `union { ... };` with fields according to `body`
  310. obj.add "union{\n"
  311. body
  312. obj.add("};\n")