cbuilderdecls.nim 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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 addVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string,
  42. typ: Snippet, initializerBody: typed) =
  43. ## adds a variable declaration to the builder, with
  44. ## `initializerBody` building the initializer. initializer must be provided
  45. builder.addVarHeader(kind)
  46. builder.add(typ)
  47. builder.add(" ")
  48. builder.add(name)
  49. builder.add(" = ")
  50. initializerBody
  51. builder.add(";\n")
  52. template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
  53. typeBody, initializerBody: typed) =
  54. ## adds a variable declaration to the builder, with `typeBody` building the type, and
  55. ## `initializerBody` building the initializer. initializer must be provided
  56. builder.addVarHeader(kind)
  57. typeBody
  58. builder.add(" ")
  59. builder.add(name)
  60. builder.add(" = ")
  61. initializerBody
  62. builder.add(";\n")
  63. proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
  64. ## adds an array variable declaration to the builder
  65. builder.addVarHeader(kind)
  66. builder.add(elementType)
  67. builder.add(" ")
  68. builder.add(name)
  69. builder.add("[")
  70. builder.addIntValue(len)
  71. builder.add("]")
  72. if initializer.len != 0:
  73. builder.add(" = ")
  74. builder.add(initializer)
  75. builder.add(";\n")
  76. template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
  77. ## adds an array variable declaration to the builder with the initializer built according to `body`
  78. builder.addVarHeader(kind)
  79. builder.add(elementType)
  80. builder.add(" ")
  81. builder.add(name)
  82. builder.add("[")
  83. builder.addIntValue(len)
  84. builder.add("] = ")
  85. body
  86. builder.add(";\n")
  87. template addTypedef(builder: var Builder, name: string, typeBody: typed) =
  88. ## adds a typedef declaration to the builder with name `name` and type as
  89. ## built in `typeBody`
  90. builder.add("typedef ")
  91. typeBody
  92. builder.add(" ")
  93. builder.add(name)
  94. builder.add(";\n")
  95. proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
  96. builder.add("typedef ")
  97. builder.add(CallingConvToStr[callConv])
  98. builder.add("_PTR(")
  99. builder.add(rettype)
  100. builder.add(", ")
  101. builder.add(name)
  102. builder.add(")")
  103. builder.add(params)
  104. builder.add(";\n")
  105. template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, typeBody: typed) =
  106. ## adds an array typedef declaration to the builder with name `name`,
  107. ## length `len`, and element type as built in `typeBody`
  108. builder.add("typedef ")
  109. typeBody
  110. builder.add(" ")
  111. builder.add(name)
  112. builder.add("[")
  113. builder.addIntValue(len)
  114. builder.add("];\n")
  115. type
  116. StructInitializerKind = enum
  117. siOrderedStruct ## struct constructor, but without named fields on C
  118. siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
  119. siArray ## array constructor
  120. siWrapper ## wrapper for a single field, generates it verbatim
  121. StructInitializer = object
  122. ## context for building struct initializers, i.e. `{ field1, field2 }`
  123. kind: StructInitializerKind
  124. ## if true, fields will not be named, instead values are placed in order
  125. needsComma: bool
  126. proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
  127. ## starts building a struct initializer, i.e. braced initializer list
  128. result = StructInitializer(kind: kind, needsComma: false)
  129. if kind != siWrapper:
  130. builder.add("{")
  131. template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
  132. ## adds a field to a struct initializer, with the value built in `valueBody`
  133. if constr.needsComma:
  134. assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
  135. builder.add(", ")
  136. else:
  137. constr.needsComma = true
  138. case constr.kind
  139. of siArray, siWrapper:
  140. # no name, can just add value
  141. valueBody
  142. of siOrderedStruct:
  143. # no name, can just add value on C
  144. assert name.len != 0, "name has to be given for struct initializer field"
  145. valueBody
  146. of siNamedStruct:
  147. assert name.len != 0, "name has to be given for struct initializer field"
  148. builder.add(".")
  149. builder.add(name)
  150. builder.add(" = ")
  151. valueBody
  152. proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
  153. ## finishes building a struct initializer
  154. if constr.kind != siWrapper:
  155. builder.add("}")
  156. template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
  157. ## builds a struct initializer, i.e. `{ field1, field2 }`
  158. ## a `var StructInitializer` must be declared and passed as a parameter so
  159. ## that it can be used with `addField`
  160. constr = builder.initStructInitializer(kind)
  161. body
  162. builder.finishStructInitializer(constr)
  163. proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
  164. ## adds a field inside a struct/union type
  165. obj.add('\t')
  166. obj.add(typ)
  167. obj.add(" ")
  168. obj.add(name)
  169. if isFlexArray:
  170. obj.add("[SEQ_DECL_SIZE]")
  171. if initializer.len != 0:
  172. obj.add(initializer)
  173. obj.add(";\n")
  174. proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
  175. ## adds an array field inside a struct/union type
  176. obj.add('\t')
  177. obj.add(elementType)
  178. obj.add(" ")
  179. obj.add(name)
  180. obj.add("[")
  181. obj.addIntValue(len)
  182. obj.add("]")
  183. if initializer.len != 0:
  184. obj.add(initializer)
  185. obj.add(";\n")
  186. proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
  187. ## adds an field inside a struct/union type, based on an `skField` symbol
  188. obj.add('\t')
  189. if field.alignment > 0:
  190. obj.add("NIM_ALIGN(")
  191. obj.addIntValue(field.alignment)
  192. obj.add(") ")
  193. obj.add(typ)
  194. if sfNoalias in field.flags:
  195. obj.add(" NIM_NOALIAS")
  196. obj.add(" ")
  197. obj.add(name)
  198. if isFlexArray:
  199. obj.add("[SEQ_DECL_SIZE]")
  200. if field.bitsize != 0:
  201. obj.add(":")
  202. obj.addIntValue(field.bitsize)
  203. if initializer.len != 0:
  204. obj.add(initializer)
  205. obj.add(";\n")
  206. proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
  207. obj.add(CallingConvToStr[callConv])
  208. obj.add("_PTR(")
  209. obj.add(rettype)
  210. obj.add(", ")
  211. obj.add(name)
  212. obj.add(")")
  213. obj.add(params)
  214. obj.add(";\n")
  215. type
  216. BaseClassKind = enum
  217. ## denotes how and whether or not the base class/RTTI should be stored
  218. bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
  219. StructBuilderInfo = object
  220. ## context for building `struct` types
  221. baseKind: BaseClassKind
  222. named: bool
  223. preFieldsLen: int
  224. proc structOrUnion(t: PType): Snippet =
  225. let t = t.skipTypes({tyAlias, tySink})
  226. if tfUnion in t.flags: "union"
  227. else: "struct"
  228. proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
  229. result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
  230. obj.add("struct")
  231. if result.named:
  232. obj.add(" ")
  233. obj.add(name)
  234. if baseType.len != 0:
  235. if m.compileToCpp:
  236. result.baseKind = bcCppInherit
  237. else:
  238. result.baseKind = bcSupField
  239. if result.baseKind == bcCppInherit:
  240. obj.add(" : public ")
  241. obj.add(baseType)
  242. obj.add(" ")
  243. obj.add("{\n")
  244. result.preFieldsLen = obj.buf.len
  245. if result.baseKind == bcSupField:
  246. obj.addField(name = "Sup", typ = baseType)
  247. proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
  248. if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len:
  249. # no fields were added, add dummy field
  250. obj.addField(name = "dummy", typ = "char")
  251. if info.named:
  252. obj.add("};\n")
  253. else:
  254. obj.add("}")
  255. template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
  256. ## builds a struct type not based on a Nim type with fields according to `body`,
  257. ## `name` can be empty to build as a type expression and not a statement
  258. let info = startSimpleStruct(obj, m, name, baseType)
  259. body
  260. finishSimpleStruct(obj, m, info)
  261. proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
  262. result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
  263. if tfPacked in t.flags:
  264. if hasAttribute in CC[m.config.cCompiler].props:
  265. obj.add(structOrUnion(t))
  266. obj.add(" __attribute__((__packed__))")
  267. else:
  268. obj.add("#pragma pack(push, 1)\n")
  269. obj.add(structOrUnion(t))
  270. else:
  271. obj.add(structOrUnion(t))
  272. if result.named:
  273. obj.add(" ")
  274. obj.add(name)
  275. if t.kind == tyObject:
  276. if t.baseClass == nil:
  277. if lacksMTypeField(t):
  278. result.baseKind = bcNone
  279. elif optTinyRtti in m.config.globalOptions:
  280. result.baseKind = bcNoneTinyRtti
  281. else:
  282. result.baseKind = bcNoneRtti
  283. elif m.compileToCpp:
  284. result.baseKind = bcCppInherit
  285. else:
  286. result.baseKind = bcSupField
  287. elif baseType.len != 0:
  288. if m.compileToCpp:
  289. result.baseKind = bcCppInherit
  290. else:
  291. result.baseKind = bcSupField
  292. if result.baseKind == bcCppInherit:
  293. obj.add(" : public ")
  294. obj.add(baseType)
  295. obj.add(" ")
  296. obj.add("{\n")
  297. result.preFieldsLen = obj.buf.len
  298. case result.baseKind
  299. of bcNone:
  300. # rest of the options add a field or don't need it due to inheritance,
  301. # we need to add the dummy field for uncheckedarray ahead of time
  302. # so that it remains trailing
  303. if t.itemId notin m.g.graph.memberProcsPerType and
  304. t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
  305. t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
  306. # only consists of flexible array field, add *initial* dummy field
  307. obj.addField(name = "dummy", typ = "char")
  308. of bcCppInherit: discard
  309. of bcNoneRtti:
  310. obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
  311. of bcNoneTinyRtti:
  312. obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
  313. of bcSupField:
  314. obj.addField(name = "Sup", typ = baseType)
  315. proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
  316. if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len and
  317. t.itemId notin m.g.graph.memberProcsPerType:
  318. # no fields were added, add dummy field
  319. obj.addField(name = "dummy", typ = "char")
  320. if info.named:
  321. obj.add("};\n")
  322. else:
  323. obj.add("}")
  324. if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
  325. obj.add("#pragma pack(pop)\n")
  326. template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
  327. ## builds a struct type directly based on `typ` with fields according to `body`,
  328. ## `name` can be empty to build as a type expression and not a statement
  329. let info = startStruct(obj, m, typ, name, baseType)
  330. body
  331. finishStruct(obj, m, typ, info)
  332. template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
  333. ## adds a field with a `struct { ... }` type, building the fields according to `body`
  334. obj.add('\t')
  335. if tfPacked in parentTyp.flags:
  336. if hasAttribute in CC[m.config.cCompiler].props:
  337. obj.add("struct __attribute__((__packed__)) {\n")
  338. else:
  339. obj.add("#pragma pack(push, 1)\nstruct {")
  340. else:
  341. obj.add("struct {\n")
  342. body
  343. obj.add("} ")
  344. obj.add(fieldName)
  345. obj.add(";\n")
  346. if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
  347. result.add("#pragma pack(pop)\n")
  348. template addAnonUnion(obj: var Builder; body: typed) =
  349. ## adds an anonymous union i.e. `union { ... };` with fields according to `body`
  350. obj.add "union{\n"
  351. body
  352. obj.add("};\n")
  353. type DeclVisibility = enum
  354. None
  355. Extern
  356. ExternC
  357. ImportLib
  358. ExportLib
  359. ExportLibVar
  360. Private
  361. StaticProc
  362. template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
  363. ## adds a declaration as in `declBody` with the given visibility
  364. case visibility
  365. of None: discard
  366. of Extern:
  367. builder.add("extern ")
  368. of ExternC:
  369. builder.add("extern \"C\" ")
  370. of ImportLib:
  371. builder.add("N_LIB_IMPORT ")
  372. of ExportLib:
  373. builder.add("N_LIB_EXPORT ")
  374. of ExportLibVar:
  375. builder.add("N_LIB_EXPORT_VAR ")
  376. of Private:
  377. builder.add("N_LIB_PRIVATE ")
  378. of StaticProc:
  379. builder.add("static ")
  380. declBody
  381. type ProcParamBuilder = object
  382. needsComma: bool
  383. proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
  384. result = ProcParamBuilder(needsComma: false)
  385. builder.add("(")
  386. proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
  387. if params.needsComma:
  388. builder.add(")")
  389. else:
  390. builder.add("void)")
  391. template cgDeclFrmt*(s: PSym): string =
  392. s.constraint.strVal
  393. proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
  394. if params.needsComma:
  395. builder.add(", ")
  396. else:
  397. params.needsComma = true
  398. builder.add(typ)
  399. builder.add(" ")
  400. builder.add(name)
  401. proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
  402. if params.needsComma:
  403. builder.add(", ")
  404. else:
  405. params.needsComma = true
  406. var modifiedTyp = typ
  407. if sfNoalias in param.flags:
  408. modifiedTyp.add(" NIM_NOALIAS")
  409. if sfCodegenDecl notin param.flags:
  410. builder.add(modifiedTyp)
  411. builder.add(" ")
  412. builder.add(param.loc.snippet)
  413. else:
  414. builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
  415. proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
  416. if params.needsComma:
  417. builder.add(", ")
  418. else:
  419. params.needsComma = true
  420. builder.add(typ)
  421. proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
  422. # does not exist in NIFC, needs to be proc pragma
  423. if params.needsComma:
  424. builder.add(", ")
  425. else:
  426. params.needsComma = true
  427. builder.add("...")
  428. template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
  429. params = initProcParamBuilder(builder)
  430. body
  431. finishProcParamBuilder(builder, params)
  432. proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
  433. # on nifc should build something like (proc name params type pragmas
  434. # with no body given
  435. let noreturn = isNoReturn(m, prc)
  436. if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
  437. builder.add("__declspec(naked) ")
  438. if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
  439. builder.add("__declspec(noreturn) ")
  440. builder.add(CallingConvToStr[prc.typ.callConv])
  441. builder.add("(")
  442. builder.add(rettype)
  443. builder.add(", ")
  444. builder.add(name)
  445. builder.add(")")
  446. builder.add(params)
  447. if addAttributes:
  448. if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
  449. builder.add(" __attribute__((naked))")
  450. if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
  451. builder.add(" __attribute__((noreturn))")
  452. proc finishProcHeaderAsProto(builder: var Builder) =
  453. builder.add(";\n")
  454. template finishProcHeaderWithBody(builder: var Builder, body: typed) =
  455. builder.add(" {\n")
  456. body
  457. builder.add("}\n\n")
  458. proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
  459. isStatic = false, ignoreAttributes = false) =
  460. # on nifc, builds full variable
  461. if isStatic:
  462. builder.add("static ")
  463. let noreturn = isNoReturn(m, prc)
  464. if not ignoreAttributes:
  465. if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
  466. builder.add("__declspec(naked) ")
  467. if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
  468. builder.add("__declspec(noreturn) ")
  469. builder.add(CallingConvToStr[prc.typ.callConv])
  470. builder.add("_PTR(")
  471. builder.add(rettype)
  472. builder.add(", ")
  473. builder.add(name)
  474. builder.add(")")
  475. builder.add(params)
  476. if not ignoreAttributes:
  477. if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
  478. builder.add(" __attribute__((naked))")
  479. if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
  480. builder.add(" __attribute__((noreturn))")
  481. # ensure we are just adding a variable:
  482. builder.add(";\n")