jsgen.nim 105 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This is the JavaScript code generator.
  10. discard """
  11. The JS code generator contains only 2 tricks:
  12. Trick 1
  13. -------
  14. Some locations (for example 'var int') require "fat pointers" (`etyBaseIndex`)
  15. which are pairs (array, index). The derefence operation is then 'array[index]'.
  16. Check `mapType` for the details.
  17. Trick 2
  18. -------
  19. It is preferable to generate '||' and '&&' if possible since that is more
  20. idiomatic and hence should be friendlier for the JS JIT implementation. However
  21. code like `foo and (let bar = baz())` cannot be translated this way. Instead
  22. the expressions need to be transformed into statements. `isSimpleExpr`
  23. implements the required case distinction.
  24. """
  25. import
  26. ast, trees, magicsys, options,
  27. nversion, msgs, idents, types,
  28. ropes, ccgutils, wordrecg, renderer,
  29. cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
  30. transf, injectdestructors, sourcemap, astmsgs, backendpragmas
  31. import pipelineutils
  32. import std/[json, sets, math, tables, intsets]
  33. import std/strutils except addf
  34. when defined(nimPreviewSlimSystem):
  35. import std/[assertions, syncio]
  36. import std/formatfloat
  37. type
  38. TJSGen = object of PPassContext
  39. module: PSym
  40. graph: ModuleGraph
  41. config: ConfigRef
  42. sigConflicts: CountTable[SigHash]
  43. initProc: PProc
  44. BModule = ref TJSGen
  45. TJSTypeKind = enum # necessary JS "types"
  46. etyNone, # no type
  47. etyNull, # null type
  48. etyProc, # proc type
  49. etyBool, # bool type
  50. etySeq, # Nim seq or string type
  51. etyInt, # JavaScript's int
  52. etyFloat, # JavaScript's float
  53. etyString, # JavaScript's string
  54. etyObject, # JavaScript's reference to an object
  55. etyBaseIndex # base + index needed
  56. TResKind = enum
  57. resNone, # not set
  58. resExpr, # is some complex expression
  59. resVal, # is a temporary/value/l-value
  60. resCallee # expression is callee
  61. TCompRes = object
  62. kind: TResKind
  63. typ: TJSTypeKind
  64. res: Rope # result part; index if this is an
  65. # (address, index)-tuple
  66. address: Rope # address of an (address, index)-tuple
  67. tmpLoc: Rope # tmp var which stores the (address, index)
  68. # pair to prevent multiple evals.
  69. # the tmp is initialized upon evaling the
  70. # address.
  71. # might be nil.
  72. # (see `maybeMakeTemp`)
  73. TBlock = object
  74. id: int # the ID of the label; positive means that it
  75. # has been used (i.e. the label should be emitted)
  76. isLoop: bool # whether it's a 'block' or 'while'
  77. PGlobals = ref object of RootObj
  78. typeInfo, constants, code: Rope
  79. forwarded: seq[PSym]
  80. generatedSyms: IntSet
  81. typeInfoGenerated: IntSet
  82. unique: int # for temp identifier generation
  83. inSystem: bool
  84. PProc = ref TProc
  85. TProc = object
  86. procDef: PNode
  87. prc: PSym
  88. globals, locals, body: Rope
  89. options: TOptions
  90. optionsStack: seq[TOptions]
  91. module: BModule
  92. g: PGlobals
  93. generatedParamCopies: IntSet
  94. beforeRetNeeded: bool
  95. unique: int # for temp identifier generation
  96. blocks: seq[TBlock]
  97. extraIndent: int
  98. up: PProc # up the call chain; required for closure support
  99. declaredGlobals: IntSet
  100. previousFileName: string # For frameInfo inside templates.
  101. template config*(p: PProc): ConfigRef = p.module.config
  102. proc indentLine(p: PProc, r: Rope): Rope =
  103. var p = p
  104. var ind = 0
  105. while true:
  106. inc ind, p.blocks.len + p.extraIndent
  107. if p.up == nil or p.up.prc != p.prc.owner:
  108. break
  109. p = p.up
  110. result = repeat(' ', ind*2) & r
  111. template line(p: PProc, added: string) =
  112. p.body.add(indentLine(p, rope(added)))
  113. template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) =
  114. p.body.add(indentLine(p, ropes.`%`(frmt, args)))
  115. template nested(p, body) =
  116. inc p.extraIndent
  117. body
  118. dec p.extraIndent
  119. proc newGlobals(): PGlobals =
  120. result = PGlobals(forwarded: @[],
  121. generatedSyms: initIntSet(),
  122. typeInfoGenerated: initIntSet()
  123. )
  124. proc initCompRes(): TCompRes =
  125. result = TCompRes(address: "", res: "",
  126. tmpLoc: "", typ: etyNone, kind: resNone
  127. )
  128. proc rdLoc(a: TCompRes): Rope {.inline.} =
  129. if a.typ != etyBaseIndex:
  130. result = a.res
  131. else:
  132. result = "$1[$2]" % [a.address, a.res]
  133. proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
  134. options: TOptions): PProc =
  135. result = PProc(
  136. blocks: @[],
  137. optionsStack: if module.initProc != nil: module.initProc.optionsStack
  138. else: @[],
  139. options: options,
  140. module: module,
  141. procDef: procDef,
  142. g: globals,
  143. extraIndent: int(procDef != nil))
  144. if procDef != nil: result.prc = procDef[namePos].sym
  145. proc initProcOptions(module: BModule): TOptions =
  146. result = module.config.options
  147. if PGlobals(module.graph.backend).inSystem:
  148. result.excl(optStackTrace)
  149. proc newInitProc(globals: PGlobals, module: BModule): PProc =
  150. result = newProc(globals, module, nil, initProcOptions(module))
  151. proc declareGlobal(p: PProc; id: int; r: Rope) =
  152. if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
  153. p.locals.addf("global $1;$n", [r])
  154. const
  155. MappedToObject = {tyObject, tyArray, tyTuple, tyOpenArray,
  156. tySet, tyVarargs}
  157. proc mapType(typ: PType): TJSTypeKind =
  158. let t = skipTypes(typ, abstractInst)
  159. case t.kind
  160. of tyVar, tyRef, tyPtr:
  161. if skipTypes(t.elementType, abstractInst).kind in MappedToObject:
  162. result = etyObject
  163. else:
  164. result = etyBaseIndex
  165. of tyPointer:
  166. # treat a tyPointer like a typed pointer to an array of bytes
  167. result = etyBaseIndex
  168. of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
  169. # tyLent is no-op as JS has pass-by-reference semantics
  170. result = mapType(skipModifier t)
  171. of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
  172. of tyBool: result = etyBool
  173. of tyFloat..tyFloat128: result = etyFloat
  174. of tySet: result = etyObject # map a set to a table
  175. of tyString, tySequence: result = etySeq
  176. of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs, tyUncheckedArray:
  177. result = etyObject
  178. of tyNil: result = etyNull
  179. of tyGenericParam, tyGenericBody, tyGenericInvocation,
  180. tyNone, tyFromExpr, tyForward, tyEmpty,
  181. tyUntyped, tyTyped, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass,
  182. tyAnd, tyOr, tyNot, tyAnything, tyVoid:
  183. result = etyNone
  184. of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst,
  185. tySink, tyOwned:
  186. result = mapType(typ.skipModifier)
  187. of tyStatic:
  188. if t.n != nil: result = mapType(skipModifier t)
  189. else: result = etyNone
  190. of tyProc: result = etyProc
  191. of tyCstring: result = etyString
  192. of tyConcept, tyIterable:
  193. raiseAssert "unreachable"
  194. proc mapType(p: PProc; typ: PType): TJSTypeKind =
  195. result = mapType(typ)
  196. proc mangleName(m: BModule, s: PSym): Rope =
  197. proc validJsName(name: string): bool =
  198. result = true
  199. const reservedWords = ["abstract", "await", "boolean", "break", "byte",
  200. "case", "catch", "char", "class", "const", "continue", "debugger",
  201. "default", "delete", "do", "double", "else", "enum", "export", "extends",
  202. "false", "final", "finally", "float", "for", "function", "goto", "if",
  203. "implements", "import", "in", "instanceof", "int", "interface", "let",
  204. "long", "native", "new", "null", "package", "private", "protected",
  205. "public", "return", "short", "static", "super", "switch", "synchronized",
  206. "this", "throw", "throws", "transient", "true", "try", "typeof", "var",
  207. "void", "volatile", "while", "with", "yield"]
  208. case name
  209. of reservedWords:
  210. return false
  211. else:
  212. discard
  213. if name[0] in {'0'..'9'}: return false
  214. for chr in name:
  215. if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
  216. return false
  217. result = s.loc.r
  218. if result == "":
  219. if s.kind == skField and s.name.s.validJsName:
  220. result = rope(s.name.s)
  221. elif s.kind == skTemp:
  222. result = rope(mangle(s.name.s))
  223. else:
  224. var x = newStringOfCap(s.name.s.len)
  225. var i = 0
  226. while i < s.name.s.len:
  227. let c = s.name.s[i]
  228. case c
  229. of 'A'..'Z', 'a'..'z', '_', '0'..'9':
  230. x.add c
  231. else:
  232. x.add("HEX" & toHex(ord(c), 2))
  233. inc i
  234. result = rope(x)
  235. # From ES5 on reserved words can be used as object field names
  236. if s.kind != skField:
  237. if m.config.hcrOn:
  238. # When hot reloading is enabled, we must ensure that the names
  239. # of functions and types will be preserved across rebuilds:
  240. result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config))
  241. else:
  242. result.add("_")
  243. result.add(rope(s.id))
  244. s.loc.r = result
  245. proc escapeJSString(s: string): string =
  246. result = newStringOfCap(s.len + s.len shr 2)
  247. result.add("\"")
  248. for c in items(s):
  249. case c
  250. of '\l': result.add("\\n")
  251. of '\r': result.add("\\r")
  252. of '\t': result.add("\\t")
  253. of '\b': result.add("\\b")
  254. of '\a': result.add("\\a")
  255. of '\e': result.add("\\e")
  256. of '\v': result.add("\\v")
  257. of '\\': result.add("\\\\")
  258. of '\"': result.add("\\\"")
  259. else: result.add(c)
  260. result.add("\"")
  261. proc makeJSString(s: string, escapeNonAscii = true): Rope =
  262. if escapeNonAscii:
  263. result = strutils.escape(s).rope
  264. else:
  265. result = escapeJSString(s).rope
  266. proc makeJsNimStrLit(s: string): Rope =
  267. var x = newStringOfCap(4*s.len+1)
  268. x.add "["
  269. var i = 0
  270. if i < s.len:
  271. x.addInt int64(s[i])
  272. inc i
  273. while i < s.len:
  274. x.add ","
  275. x.addInt int64(s[i])
  276. inc i
  277. x.add "]"
  278. result = rope(x)
  279. include jstypes
  280. proc gen(p: PProc, n: PNode, r: var TCompRes)
  281. proc genStmt(p: PProc, n: PNode)
  282. proc genProc(oldProc: PProc, prc: PSym): Rope
  283. proc genConstant(p: PProc, c: PSym)
  284. proc useMagic(p: PProc, name: string) =
  285. if name.len == 0: return
  286. var s = magicsys.getCompilerProc(p.module.graph, name)
  287. if s != nil:
  288. internalAssert p.config, s.kind in {skProc, skFunc, skMethod, skConverter}
  289. if not p.g.generatedSyms.containsOrIncl(s.id):
  290. let code = genProc(p, s)
  291. p.g.constants.add(code)
  292. else:
  293. if p.prc != nil:
  294. globalError(p.config, p.prc.info, "system module needs: " & name)
  295. else:
  296. rawMessage(p.config, errGenerated, "system module needs: " & name)
  297. proc isSimpleExpr(p: PProc; n: PNode): bool =
  298. # calls all the way down --> can stay expression based
  299. case n.kind
  300. of nkCallKinds, nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr,
  301. nkObjConstr, nkBracket, nkCurly,
  302. nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr,
  303. nkConv, nkHiddenStdConv, nkHiddenSubConv:
  304. for c in n:
  305. if not p.isSimpleExpr(c): return false
  306. result = true
  307. of nkStmtListExpr:
  308. for i in 0..<n.len-1:
  309. if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
  310. result = isSimpleExpr(p, n.lastSon)
  311. else:
  312. result = n.isAtom
  313. proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
  314. inc(p.unique)
  315. result = "Temporary$1" % [rope(p.unique)]
  316. if defineInLocals:
  317. p.locals.add(p.indentLine("var $1;$n" % [result]))
  318. proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
  319. assert r.kind == resNone
  320. var x, y: TCompRes = default(TCompRes)
  321. if p.isSimpleExpr(a) and p.isSimpleExpr(b):
  322. gen(p, a, x)
  323. gen(p, b, y)
  324. r.kind = resExpr
  325. r.res = "($1 && $2)" % [x.rdLoc, y.rdLoc]
  326. else:
  327. r.res = p.getTemp
  328. r.kind = resVal
  329. # while a and b:
  330. # -->
  331. # while true:
  332. # aa
  333. # if not a: tmp = false
  334. # else:
  335. # bb
  336. # tmp = b
  337. # tmp
  338. gen(p, a, x)
  339. lineF(p, "if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc])
  340. p.nested:
  341. gen(p, b, y)
  342. lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc])
  343. line(p, "}")
  344. proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
  345. assert r.kind == resNone
  346. var x, y: TCompRes = default(TCompRes)
  347. if p.isSimpleExpr(a) and p.isSimpleExpr(b):
  348. gen(p, a, x)
  349. gen(p, b, y)
  350. r.kind = resExpr
  351. r.res = "($1 || $2)" % [x.rdLoc, y.rdLoc]
  352. else:
  353. r.res = p.getTemp
  354. r.kind = resVal
  355. gen(p, a, x)
  356. lineF(p, "if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc])
  357. p.nested:
  358. gen(p, b, y)
  359. lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc])
  360. line(p, "}")
  361. type
  362. TMagicFrmt = array[0..1, string]
  363. TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
  364. const # magic checked op; magic unchecked op;
  365. jsMagics: TMagicOps = [
  366. mAddI: ["addInt", ""],
  367. mSubI: ["subInt", ""],
  368. mMulI: ["mulInt", ""],
  369. mDivI: ["divInt", ""],
  370. mModI: ["modInt", ""],
  371. mSucc: ["addInt", ""],
  372. mPred: ["subInt", ""],
  373. mAddF64: ["", ""],
  374. mSubF64: ["", ""],
  375. mMulF64: ["", ""],
  376. mDivF64: ["", ""],
  377. mShrI: ["", ""],
  378. mShlI: ["", ""],
  379. mAshrI: ["", ""],
  380. mBitandI: ["", ""],
  381. mBitorI: ["", ""],
  382. mBitxorI: ["", ""],
  383. mMinI: ["nimMin", "nimMin"],
  384. mMaxI: ["nimMax", "nimMax"],
  385. mAddU: ["", ""],
  386. mSubU: ["", ""],
  387. mMulU: ["", ""],
  388. mDivU: ["", ""],
  389. mModU: ["", ""],
  390. mEqI: ["", ""],
  391. mLeI: ["", ""],
  392. mLtI: ["", ""],
  393. mEqF64: ["", ""],
  394. mLeF64: ["", ""],
  395. mLtF64: ["", ""],
  396. mLeU: ["", ""],
  397. mLtU: ["", ""],
  398. mEqEnum: ["", ""],
  399. mLeEnum: ["", ""],
  400. mLtEnum: ["", ""],
  401. mEqCh: ["", ""],
  402. mLeCh: ["", ""],
  403. mLtCh: ["", ""],
  404. mEqB: ["", ""],
  405. mLeB: ["", ""],
  406. mLtB: ["", ""],
  407. mEqRef: ["", ""],
  408. mLePtr: ["", ""],
  409. mLtPtr: ["", ""],
  410. mXor: ["", ""],
  411. mEqCString: ["", ""],
  412. mEqProc: ["", ""],
  413. mUnaryMinusI: ["negInt", ""],
  414. mUnaryMinusI64: ["negInt64", ""],
  415. mAbsI: ["absInt", ""],
  416. mNot: ["", ""],
  417. mUnaryPlusI: ["", ""],
  418. mBitnotI: ["", ""],
  419. mUnaryPlusF64: ["", ""],
  420. mUnaryMinusF64: ["", ""],
  421. mCharToStr: ["nimCharToStr", "nimCharToStr"],
  422. mBoolToStr: ["nimBoolToStr", "nimBoolToStr"],
  423. mIntToStr: ["cstrToNimstr", "cstrToNimstr"],
  424. mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"],
  425. mFloatToStr: ["cstrToNimstr", "cstrToNimstr"],
  426. mCStrToStr: ["cstrToNimstr", "cstrToNimstr"],
  427. mStrToStr: ["", ""]]
  428. proc needsTemp(p: PProc; n: PNode): bool =
  429. # check if n contains a call to determine
  430. # if a temp should be made to prevent multiple evals
  431. result = false
  432. if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}:
  433. return true
  434. for c in n:
  435. if needsTemp(p, c):
  436. return true
  437. proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
  438. var
  439. a = x.rdLoc
  440. b = a
  441. if needsTemp(p, n):
  442. # if we have tmp just use it
  443. if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
  444. b = "$1[0][$1[1]]" % [x.tmpLoc]
  445. (a: a, tmp: b)
  446. else:
  447. let tmp = p.getTemp
  448. b = tmp
  449. a = "($1 = $2, $1)" % [tmp, a]
  450. (a: a, tmp: b)
  451. else:
  452. (a: a, tmp: b)
  453. proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
  454. var
  455. a = x.rdLoc
  456. b = a
  457. if needsTemp(p, n):
  458. # if we have tmp just use it
  459. if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
  460. b = "$1[0][$1[1]]" % [x.tmpLoc]
  461. result = (a: a, tmp: b)
  462. elif x.tmpLoc != "" and n.kind == nkBracketExpr:
  463. # genArrayAddr
  464. var
  465. address, index: TCompRes = default(TCompRes)
  466. first: Int128 = Zero
  467. gen(p, n[0], address)
  468. gen(p, n[1], index)
  469. let (m1, tmp1) = maybeMakeTemp(p, n[0], address)
  470. let typ = skipTypes(n[0].typ, abstractPtrs)
  471. if typ.kind == tyArray:
  472. first = firstOrd(p.config, typ.indexType)
  473. if optBoundsCheck in p.options:
  474. useMagic(p, "chckIndx")
  475. if first == 0: # save a couple chars
  476. index.res = "chckIndx($1, 0, ($2).length - 1)" % [index.res, tmp1]
  477. else:
  478. index.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [
  479. index.res, rope(first), tmp1]
  480. elif first != 0:
  481. index.res = "($1) - ($2)" % [index.res, rope(first)]
  482. else:
  483. discard # index.res = index.res
  484. let (n1, tmp2) = maybeMakeTemp(p, n[1], index)
  485. result = (a: "$1[$2]" % [m1, n1], tmp: "$1[$2]" % [tmp1, tmp2])
  486. # could also put here: nkDotExpr -> genFieldAccess, nkCheckedFieldExpr -> genCheckedFieldOp
  487. # but the uses of maybeMakeTempAssignable don't need them
  488. else:
  489. result = (a: a, tmp: b)
  490. else:
  491. result = (a: a, tmp: b)
  492. template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
  493. reassign = false) =
  494. # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
  495. # if $3 or $4 are present they will be substituted with temps for
  496. # lhs and rhs respectively
  497. var x, y: TCompRes = default(TCompRes)
  498. useMagic(p, magic)
  499. gen(p, n[1], x)
  500. gen(p, n[2], y)
  501. var
  502. a, tmp = x.rdLoc
  503. b, tmp2 = y.rdLoc
  504. when reassign:
  505. (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
  506. else:
  507. when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
  508. when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y)
  509. r.res = frmt % [a, b, tmp, tmp2]
  510. r.kind = resExpr
  511. proc unsignedTrimmer(size: BiggestInt): string =
  512. case size
  513. of 1: "& 0xff"
  514. of 2: "& 0xffff"
  515. of 4: ">>> 0"
  516. else: ""
  517. proc signedTrimmer(size: BiggestInt): string =
  518. # sign extension is done by shifting to the left and then back to the right
  519. "<< $1 >> $1" % [$(32 - size * 8)]
  520. proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
  521. reassign: static[bool] = false) =
  522. var x, y: TCompRes = default(TCompRes)
  523. gen(p, n[1], x)
  524. gen(p, n[2], y)
  525. let size = n[1].typ.skipTypes(abstractRange).size
  526. when reassign:
  527. let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
  528. if size == 8 and optJsBigInt64 in p.config.globalOptions:
  529. r.res = "$1 = BigInt.asUintN(64, ($4 $2 $3))" % [a, rope op, y.rdLoc, tmp]
  530. else:
  531. let trimmer = unsignedTrimmer(size)
  532. r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
  533. else:
  534. if size == 8 and optJsBigInt64 in p.config.globalOptions:
  535. r.res = "BigInt.asUintN(64, ($1 $2 $3))" % [x.rdLoc, rope op, y.rdLoc]
  536. else:
  537. let trimmer = unsignedTrimmer(size)
  538. r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
  539. r.kind = resExpr
  540. template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
  541. var x, y, z: TCompRes
  542. useMagic(p, magic)
  543. gen(p, n[1], x)
  544. gen(p, n[2], y)
  545. gen(p, n[3], z)
  546. r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
  547. r.kind = resExpr
  548. template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
  549. # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
  550. useMagic(p, magic)
  551. gen(p, n[1], r)
  552. var a, tmp = r.rdLoc
  553. if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r)
  554. r.res = frmt % [a, tmp]
  555. r.kind = resExpr
  556. proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
  557. var
  558. x, y: TCompRes = default(TCompRes)
  559. xLoc, yLoc: Rope = ""
  560. let i = ord(optOverflowCheck notin p.options)
  561. useMagic(p, jsMagics[op][i])
  562. if n.len > 2:
  563. gen(p, n[1], x)
  564. gen(p, n[2], y)
  565. xLoc = x.rdLoc
  566. yLoc = y.rdLoc
  567. else:
  568. gen(p, n[1], r)
  569. xLoc = r.rdLoc
  570. template applyFormat(frmt) =
  571. r.res = frmt % [xLoc, yLoc]
  572. template applyFormat(frmtA, frmtB) =
  573. if i == 0: applyFormat(frmtA) else: applyFormat(frmtB)
  574. template bitwiseExpr(op: string) =
  575. let typ = n[1].typ.skipTypes(abstractVarRange)
  576. if typ.kind in {tyUInt, tyUInt32}:
  577. r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc]
  578. else:
  579. r.res = "($1 $2 $3)" % [xLoc, op, yLoc]
  580. case op
  581. of mAddI:
  582. if i == 0:
  583. if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
  584. useMagic(p, "addInt64")
  585. applyFormat("addInt64($1, $2)")
  586. else:
  587. applyFormat("addInt($1, $2)")
  588. else:
  589. applyFormat("($1 + $2)")
  590. of mSubI:
  591. if i == 0:
  592. if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
  593. useMagic(p, "subInt64")
  594. applyFormat("subInt64($1, $2)")
  595. else:
  596. applyFormat("subInt($1, $2)")
  597. else:
  598. applyFormat("($1 - $2)")
  599. of mMulI:
  600. if i == 0:
  601. if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
  602. useMagic(p, "mulInt64")
  603. applyFormat("mulInt64($1, $2)")
  604. else:
  605. applyFormat("mulInt($1, $2)")
  606. else:
  607. applyFormat("($1 * $2)")
  608. of mDivI:
  609. if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
  610. useMagic(p, "divInt64")
  611. applyFormat("divInt64($1, $2)", "$1 / $2")
  612. else:
  613. applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
  614. of mModI:
  615. if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
  616. useMagic(p, "modInt64")
  617. applyFormat("modInt64($1, $2)", "$1 % $2")
  618. else:
  619. applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
  620. of mSucc:
  621. let typ = n[1].typ.skipTypes(abstractVarRange)
  622. case typ.kind
  623. of tyUInt..tyUInt32:
  624. binaryUintExpr(p, n, r, "+")
  625. of tyUInt64:
  626. if optJsBigInt64 in p.config.globalOptions:
  627. applyFormat("BigInt.asUintN(64, $1 + BigInt($2))")
  628. else: binaryUintExpr(p, n, r, "+")
  629. elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  630. if optOverflowCheck notin p.options:
  631. applyFormat("BigInt.asIntN(64, $1 + BigInt($2))")
  632. else: binaryExpr(p, n, r, "addInt64", "addInt64($1, BigInt($2))")
  633. else:
  634. if optOverflowCheck notin p.options: applyFormat("$1 + $2")
  635. else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)")
  636. of mPred:
  637. let typ = n[1].typ.skipTypes(abstractVarRange)
  638. case typ.kind
  639. of tyUInt..tyUInt32:
  640. binaryUintExpr(p, n, r, "-")
  641. of tyUInt64:
  642. if optJsBigInt64 in p.config.globalOptions:
  643. applyFormat("BigInt.asUintN(64, $1 - BigInt($2))")
  644. else: binaryUintExpr(p, n, r, "-")
  645. elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  646. if optOverflowCheck notin p.options:
  647. applyFormat("BigInt.asIntN(64, $1 - BigInt($2))")
  648. else: binaryExpr(p, n, r, "subInt64", "subInt64($1, BigInt($2))")
  649. else:
  650. if optOverflowCheck notin p.options: applyFormat("$1 - $2")
  651. else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)")
  652. of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
  653. of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
  654. of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
  655. of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
  656. of mShrI:
  657. let typ = n[1].typ.skipTypes(abstractVarRange)
  658. if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  659. applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
  660. elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
  661. applyFormat("($1 >> BigInt($2))")
  662. else:
  663. if typ.kind in {tyInt..tyInt32}:
  664. let trimmerU = unsignedTrimmer(typ.size)
  665. let trimmerS = signedTrimmer(typ.size)
  666. r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
  667. else:
  668. applyFormat("($1 >>> $2)")
  669. of mShlI:
  670. let typ = n[1].typ.skipTypes(abstractVarRange)
  671. if typ.size == 8:
  672. if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  673. applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
  674. elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
  675. applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
  676. else:
  677. applyFormat("($1 * Math.pow(2, $2))")
  678. else:
  679. if typ.kind in {tyUInt..tyUInt32}:
  680. let trimmer = unsignedTrimmer(typ.size)
  681. r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
  682. else:
  683. let trimmer = signedTrimmer(typ.size)
  684. r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
  685. of mAshrI:
  686. let typ = n[1].typ.skipTypes(abstractVarRange)
  687. if typ.size == 8:
  688. if optJsBigInt64 in p.config.globalOptions:
  689. applyFormat("($1 >> BigInt($2))")
  690. else:
  691. applyFormat("Math.floor($1 / Math.pow(2, $2))")
  692. else:
  693. if typ.kind in {tyUInt..tyUInt32}:
  694. applyFormat("($1 >>> $2)")
  695. else:
  696. applyFormat("($1 >> $2)")
  697. of mBitandI: bitwiseExpr("&")
  698. of mBitorI: bitwiseExpr("|")
  699. of mBitxorI: bitwiseExpr("^")
  700. of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
  701. of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
  702. of mAddU: applyFormat("", "")
  703. of mSubU: applyFormat("", "")
  704. of mMulU: applyFormat("", "")
  705. of mDivU: applyFormat("", "")
  706. of mModU: applyFormat("($1 % $2)", "($1 % $2)")
  707. of mEqI: applyFormat("($1 == $2)", "($1 == $2)")
  708. of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)")
  709. of mLtI: applyFormat("($1 < $2)", "($1 < $2)")
  710. of mEqF64: applyFormat("($1 == $2)", "($1 == $2)")
  711. of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)")
  712. of mLtF64: applyFormat("($1 < $2)", "($1 < $2)")
  713. of mLeU: applyFormat("($1 <= $2)", "($1 <= $2)")
  714. of mLtU: applyFormat("($1 < $2)", "($1 < $2)")
  715. of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)")
  716. of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)")
  717. of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)")
  718. of mEqCh: applyFormat("($1 == $2)", "($1 == $2)")
  719. of mLeCh: applyFormat("($1 <= $2)", "($1 <= $2)")
  720. of mLtCh: applyFormat("($1 < $2)", "($1 < $2)")
  721. of mEqB: applyFormat("($1 == $2)", "($1 == $2)")
  722. of mLeB: applyFormat("($1 <= $2)", "($1 <= $2)")
  723. of mLtB: applyFormat("($1 < $2)", "($1 < $2)")
  724. of mEqRef: applyFormat("($1 == $2)", "($1 == $2)")
  725. of mLePtr: applyFormat("($1 <= $2)", "($1 <= $2)")
  726. of mLtPtr: applyFormat("($1 < $2)", "($1 < $2)")
  727. of mXor: applyFormat("($1 != $2)", "($1 != $2)")
  728. of mEqCString: applyFormat("($1 == $2)", "($1 == $2)")
  729. of mEqProc: applyFormat("($1 == $2)", "($1 == $2)")
  730. of mUnaryMinusI: applyFormat("negInt($1)", "-($1)")
  731. of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)")
  732. of mAbsI:
  733. let typ = n[1].typ.skipTypes(abstractVarRange)
  734. if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  735. useMagic(p, "absInt64")
  736. applyFormat("absInt64($1)", "absInt64($1)")
  737. else:
  738. applyFormat("absInt($1)", "Math.abs($1)")
  739. of mNot: applyFormat("!($1)", "!($1)")
  740. of mUnaryPlusI: applyFormat("+($1)", "+($1)")
  741. of mBitnotI:
  742. let typ = n[1].typ.skipTypes(abstractVarRange)
  743. if typ.kind in {tyUInt..tyUInt64}:
  744. if typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
  745. applyFormat("BigInt.asUintN(64, ~($1))")
  746. else:
  747. let trimmer = unsignedTrimmer(typ.size)
  748. r.res = "(~($1) $2)" % [xLoc, trimmer]
  749. else:
  750. applyFormat("~($1)")
  751. of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
  752. of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
  753. of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)")
  754. of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)")
  755. of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")")
  756. of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")")
  757. of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
  758. of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1")
  759. else:
  760. assert false, $op
  761. proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
  762. case op
  763. of mAddU: binaryUintExpr(p, n, r, "+")
  764. of mSubU: binaryUintExpr(p, n, r, "-")
  765. of mMulU: binaryUintExpr(p, n, r, "*")
  766. of mDivU:
  767. binaryUintExpr(p, n, r, "/")
  768. if optJsBigInt64 notin p.config.globalOptions and
  769. n[1].typ.skipTypes(abstractRange).size == 8:
  770. # bigint / already truncates
  771. r.res = "Math.trunc($1)" % [r.res]
  772. of mDivI:
  773. arithAux(p, n, r, op)
  774. of mModI:
  775. arithAux(p, n, r, op)
  776. of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr:
  777. arithAux(p, n, r, op)
  778. of mEqRef:
  779. if mapType(n[1].typ) != etyBaseIndex:
  780. arithAux(p, n, r, op)
  781. else:
  782. var x, y: TCompRes = default(TCompRes)
  783. gen(p, n[1], x)
  784. gen(p, n[2], y)
  785. r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res]
  786. else:
  787. arithAux(p, n, r, op)
  788. r.kind = resExpr
  789. proc hasFrameInfo(p: PProc): bool =
  790. ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
  791. ((p.prc == nil) or not (sfPure in p.prc.flags))
  792. proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope =
  793. "/* line $2:$3 \"$1\" */$n" % [
  794. rope(toFullPath(config, info)), rope(line), rope(info.toColumn)
  795. ]
  796. proc genLineDir(p: PProc, n: PNode) =
  797. let line = toLinenumber(n.info)
  798. if line < 0:
  799. return
  800. if optEmbedOrigSrc in p.config.globalOptions:
  801. lineF(p, "//$1$n", [sourceLine(p.config, n.info)])
  802. if optLineDir in p.options or optLineDir in p.config.options:
  803. lineF(p, "$1", [lineDir(p.config, n.info, line)])
  804. if hasFrameInfo(p):
  805. lineF(p, "F.line = $1;$n", [rope(line)])
  806. let currentFileName = toFilename(p.config, n.info)
  807. if p.previousFileName != currentFileName:
  808. lineF(p, "F.filename = $1;$n", [makeJSString(currentFileName)])
  809. p.previousFileName = currentFileName
  810. proc genWhileStmt(p: PProc, n: PNode) =
  811. var cond: TCompRes = default(TCompRes)
  812. internalAssert p.config, isEmptyType(n.typ)
  813. genLineDir(p, n)
  814. inc(p.unique)
  815. setLen(p.blocks, p.blocks.len + 1)
  816. p.blocks[^1].id = -p.unique
  817. p.blocks[^1].isLoop = true
  818. let labl = p.unique.rope
  819. lineF(p, "Label$1: while (true) {$n", [labl])
  820. p.nested: gen(p, n[0], cond)
  821. lineF(p, "if (!$1) break Label$2;$n",
  822. [cond.res, labl])
  823. p.nested: genStmt(p, n[1])
  824. lineF(p, "}$n", [labl])
  825. setLen(p.blocks, p.blocks.len - 1)
  826. proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
  827. if src.kind != resNone:
  828. if dest.kind != resNone:
  829. lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
  830. else:
  831. lineF(p, "$1;$n", [src.rdLoc])
  832. src.kind = resNone
  833. src.res = ""
  834. proc genTry(p: PProc, n: PNode, r: var TCompRes) =
  835. # code to generate:
  836. #
  837. # ++excHandler;
  838. # var tmpFramePtr = framePtr;
  839. # try {
  840. # stmts;
  841. # --excHandler;
  842. # } catch (EXCEPTION) {
  843. # var prevJSError = lastJSError; lastJSError = EXCEPTION;
  844. # framePtr = tmpFramePtr;
  845. # --excHandler;
  846. # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
  847. # stmts;
  848. # } else if (e.typ && e.typ == NTI32342) {
  849. # stmts;
  850. # } else {
  851. # stmts;
  852. # }
  853. # lastJSError = prevJSError;
  854. # } finally {
  855. # framePtr = tmpFramePtr;
  856. # stmts;
  857. # }
  858. genLineDir(p, n)
  859. if not isEmptyType(n.typ):
  860. r.kind = resVal
  861. r.res = getTemp(p)
  862. inc(p.unique)
  863. var i = 1
  864. var catchBranchesExist = n.len > 1 and n[i].kind == nkExceptBranch
  865. if catchBranchesExist:
  866. p.body.add("++excHandler;\L")
  867. var tmpFramePtr = rope"F"
  868. lineF(p, "try {$n", [])
  869. var a: TCompRes
  870. gen(p, n[0], a)
  871. moveInto(p, a, r)
  872. var generalCatchBranchExists = false
  873. if catchBranchesExist:
  874. p.body.addf("--excHandler;$n} catch (EXCEPTION) {$n var prevJSError = lastJSError;$n" &
  875. " lastJSError = EXCEPTION;$n --excHandler;$n", [])
  876. if hasFrameInfo(p):
  877. line(p, "framePtr = $1;$n" % [tmpFramePtr])
  878. while i < n.len and n[i].kind == nkExceptBranch:
  879. if n[i].len == 1:
  880. # general except section:
  881. generalCatchBranchExists = true
  882. if i > 1: lineF(p, "else {$n", [])
  883. gen(p, n[i][0], a)
  884. moveInto(p, a, r)
  885. if i > 1: lineF(p, "}$n", [])
  886. else:
  887. var orExpr: Rope = ""
  888. var excAlias: PNode = nil
  889. useMagic(p, "isObj")
  890. for j in 0..<n[i].len - 1:
  891. var throwObj: PNode
  892. let it = n[i][j]
  893. if it.isInfixAs():
  894. throwObj = it[1]
  895. excAlias = it[2]
  896. # If this is a ``except exc as sym`` branch there must be no following
  897. # nodes
  898. doAssert orExpr == ""
  899. elif it.kind == nkType:
  900. throwObj = it
  901. else:
  902. throwObj = nil
  903. internalError(p.config, n.info, "genTryStmt")
  904. if orExpr != "": orExpr.add("||")
  905. # Generate the correct type checking code depending on whether this is a
  906. # NIM-native or a JS-native exception
  907. # if isJsObject(throwObj.typ):
  908. if isImportedException(throwObj.typ, p.config):
  909. orExpr.addf("lastJSError instanceof $1",
  910. [throwObj.typ.sym.loc.r])
  911. else:
  912. orExpr.addf("isObj(lastJSError.m_type, $1)",
  913. [genTypeInfo(p, throwObj.typ)])
  914. if i > 1: line(p, "else ")
  915. lineF(p, "if (lastJSError && ($1)) {$n", [orExpr])
  916. # If some branch requires a local alias introduce it here. This is needed
  917. # since JS cannot do ``catch x as y``.
  918. if excAlias != nil:
  919. excAlias.sym.loc.r = mangleName(p.module, excAlias.sym)
  920. lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r)
  921. gen(p, n[i][^1], a)
  922. moveInto(p, a, r)
  923. lineF(p, "}$n", [])
  924. inc(i)
  925. if catchBranchesExist:
  926. if not generalCatchBranchExists:
  927. useMagic(p, "reraiseException")
  928. line(p, "else {\L")
  929. line(p, "\treraiseException();\L")
  930. line(p, "}\L")
  931. lineF(p, "lastJSError = prevJSError;$n")
  932. line(p, "} finally {\L")
  933. if hasFrameInfo(p):
  934. line(p, "framePtr = $1;$n" % [tmpFramePtr])
  935. if i < n.len and n[i].kind == nkFinally:
  936. genStmt(p, n[i][0])
  937. line(p, "}\L")
  938. proc genRaiseStmt(p: PProc, n: PNode) =
  939. if n[0].kind != nkEmpty:
  940. var a: TCompRes = default(TCompRes)
  941. gen(p, n[0], a)
  942. let typ = skipTypes(n[0].typ, abstractPtrs)
  943. genLineDir(p, n)
  944. useMagic(p, "raiseException")
  945. lineF(p, "raiseException($1, $2);$n",
  946. [a.rdLoc, makeJSString(typ.sym.name.s)])
  947. else:
  948. genLineDir(p, n)
  949. useMagic(p, "reraiseException")
  950. line(p, "reraiseException();\L")
  951. proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
  952. var
  953. a, b, cond, stmt: TCompRes = default(TCompRes)
  954. genLineDir(p, n)
  955. gen(p, n[0], cond)
  956. let typeKind = skipTypes(n[0].typ, abstractVar).kind
  957. var transferRange = false
  958. let anyString = typeKind in {tyString, tyCstring}
  959. case typeKind
  960. of tyString:
  961. useMagic(p, "toJSStr")
  962. lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
  963. of tyFloat..tyFloat128, tyInt..tyInt64, tyUInt..tyUInt64:
  964. transferRange = true
  965. else:
  966. lineF(p, "switch ($1) {$n", [cond.rdLoc])
  967. if not isEmptyType(n.typ):
  968. r.kind = resVal
  969. r.res = getTemp(p)
  970. for i in 1..<n.len:
  971. let it = n[i]
  972. let itLen = it.len
  973. case it.kind
  974. of nkOfBranch:
  975. if transferRange:
  976. if i == 1:
  977. lineF(p, "if (", [])
  978. else:
  979. lineF(p, "else if (", [])
  980. for j in 0..<itLen - 1:
  981. let e = it[j]
  982. if e.kind == nkRange:
  983. if transferRange:
  984. gen(p, e[0], a)
  985. gen(p, e[1], b)
  986. if j != itLen - 2:
  987. lineF(p, "$1 >= $2 && $1 <= $3 || $n", [cond.rdLoc, a.rdLoc, b.rdLoc])
  988. else:
  989. lineF(p, "$1 >= $2 && $1 <= $3", [cond.rdLoc, a.rdLoc, b.rdLoc])
  990. else:
  991. var v = copyNode(e[0])
  992. while v.intVal <= e[1].intVal:
  993. gen(p, v, cond)
  994. lineF(p, "case $1:$n", [cond.rdLoc])
  995. inc(v.intVal)
  996. else:
  997. if anyString:
  998. case e.kind
  999. of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n",
  1000. [makeJSString(e.strVal, false)])
  1001. of nkNilLit: lineF(p, "case null:$n", [])
  1002. else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2")
  1003. else:
  1004. if transferRange:
  1005. gen(p, e, a)
  1006. if j != itLen - 2:
  1007. lineF(p, "$1 == $2 || $n", [cond.rdLoc, a.rdLoc])
  1008. else:
  1009. lineF(p, "$1 == $2", [cond.rdLoc, a.rdLoc])
  1010. else:
  1011. gen(p, e, a)
  1012. lineF(p, "case $1:$n", [a.rdLoc])
  1013. if transferRange:
  1014. lineF(p, "){", [])
  1015. p.nested:
  1016. gen(p, lastSon(it), stmt)
  1017. moveInto(p, stmt, r)
  1018. if transferRange:
  1019. lineF(p, "}$n", [])
  1020. else:
  1021. lineF(p, "break;$n", [])
  1022. of nkElse:
  1023. if transferRange:
  1024. lineF(p, "else{$n", [])
  1025. else:
  1026. lineF(p, "default: $n", [])
  1027. p.nested:
  1028. gen(p, it[0], stmt)
  1029. moveInto(p, stmt, r)
  1030. if transferRange:
  1031. lineF(p, "}$n", [])
  1032. else:
  1033. lineF(p, "break;$n", [])
  1034. else: internalError(p.config, it.info, "jsgen.genCaseStmt")
  1035. if not transferRange:
  1036. lineF(p, "}$n", [])
  1037. proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
  1038. inc(p.unique)
  1039. let idx = p.blocks.len
  1040. if n[0].kind != nkEmpty:
  1041. # named block?
  1042. if (n[0].kind != nkSym): internalError(p.config, n.info, "genBlock")
  1043. var sym = n[0].sym
  1044. sym.loc.k = locOther
  1045. sym.position = idx+1
  1046. let labl = p.unique
  1047. lineF(p, "Label$1: {$n", [labl.rope])
  1048. setLen(p.blocks, idx + 1)
  1049. p.blocks[idx].id = - p.unique # negative because it isn't used yet
  1050. gen(p, n[1], r)
  1051. setLen(p.blocks, idx)
  1052. lineF(p, "};$n", [labl.rope])
  1053. proc genBreakStmt(p: PProc, n: PNode) =
  1054. var idx: int
  1055. genLineDir(p, n)
  1056. if n[0].kind != nkEmpty:
  1057. # named break?
  1058. assert(n[0].kind == nkSym)
  1059. let sym = n[0].sym
  1060. assert(sym.loc.k == locOther)
  1061. idx = sym.position-1
  1062. else:
  1063. # an unnamed 'break' can only break a loop after 'transf' pass:
  1064. idx = p.blocks.len - 1
  1065. while idx >= 0 and not p.blocks[idx].isLoop: dec idx
  1066. if idx < 0 or not p.blocks[idx].isLoop:
  1067. internalError(p.config, n.info, "no loop to break")
  1068. p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
  1069. lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)])
  1070. proc genAsmOrEmitStmt(p: PProc, n: PNode; isAsmStmt = false) =
  1071. genLineDir(p, n)
  1072. p.body.add p.indentLine("")
  1073. let offset =
  1074. if isAsmStmt: 1 # first son is pragmas
  1075. else: 0
  1076. for i in offset..<n.len:
  1077. let it = n[i]
  1078. case it.kind
  1079. of nkStrLit..nkTripleStrLit:
  1080. p.body.add(it.strVal)
  1081. of nkSym:
  1082. let v = it.sym
  1083. # for backwards compatibility we don't deref syms here :-(
  1084. if false:
  1085. discard
  1086. else:
  1087. var r = default(TCompRes)
  1088. gen(p, it, r)
  1089. if it.typ.kind == tyPointer:
  1090. # A fat pointer is disguised as an array
  1091. r.res = r.address
  1092. r.address = ""
  1093. r.typ = etyNone
  1094. elif r.typ == etyBaseIndex:
  1095. # Deference first
  1096. r.res = "$1[$2]" % [r.address, r.res]
  1097. r.address = ""
  1098. r.typ = etyNone
  1099. p.body.add(r.rdLoc)
  1100. else:
  1101. var r: TCompRes = default(TCompRes)
  1102. gen(p, it, r)
  1103. p.body.add(r.rdLoc)
  1104. p.body.add "\L"
  1105. proc genIf(p: PProc, n: PNode, r: var TCompRes) =
  1106. var cond, stmt: TCompRes = default(TCompRes)
  1107. var toClose = 0
  1108. if not isEmptyType(n.typ):
  1109. r.kind = resVal
  1110. r.res = getTemp(p)
  1111. for i in 0..<n.len:
  1112. let it = n[i]
  1113. if it.len != 1:
  1114. if i > 0:
  1115. lineF(p, "else {$n", [])
  1116. inc(toClose)
  1117. p.nested: gen(p, it[0], cond)
  1118. lineF(p, "if ($1) {$n", [cond.rdLoc])
  1119. gen(p, it[1], stmt)
  1120. else:
  1121. # else part:
  1122. lineF(p, "else {$n", [])
  1123. p.nested: gen(p, it[0], stmt)
  1124. moveInto(p, stmt, r)
  1125. lineF(p, "}$n", [])
  1126. line(p, repeat('}', toClose) & "\L")
  1127. proc generateHeader(p: PProc, typ: PType): Rope =
  1128. result = ""
  1129. for i in 1..<typ.n.len:
  1130. assert(typ.n[i].kind == nkSym)
  1131. var param = typ.n[i].sym
  1132. if isCompileTimeOnly(param.typ): continue
  1133. if result != "": result.add(", ")
  1134. var name = mangleName(p.module, param)
  1135. result.add(name)
  1136. if mapType(param.typ) == etyBaseIndex:
  1137. result.add(", ")
  1138. result.add(name)
  1139. result.add("_Idx")
  1140. proc countJsParams(typ: PType): int =
  1141. result = 0
  1142. for i in 1..<typ.n.len:
  1143. assert(typ.n[i].kind == nkSym)
  1144. var param = typ.n[i].sym
  1145. if isCompileTimeOnly(param.typ): continue
  1146. if mapType(param.typ) == etyBaseIndex:
  1147. inc result, 2
  1148. else:
  1149. inc result
  1150. const
  1151. nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
  1152. nkFloatLit..nkFloat64Lit, nkPar, nkStringToCString,
  1153. nkObjConstr, nkTupleConstr, nkBracket,
  1154. nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
  1155. nkCommand, nkHiddenCallConv, nkCallStrLit}
  1156. proc needsNoCopy(p: PProc; y: PNode): bool =
  1157. return y.kind in nodeKindsNeedNoCopy or
  1158. ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
  1159. (skipTypes(y.typ, abstractInst).kind in
  1160. {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes))
  1161. proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
  1162. var a, b: TCompRes = default(TCompRes)
  1163. var xtyp = mapType(p, x.typ)
  1164. # disable `[]=` for cstring
  1165. if x.kind == nkBracketExpr and x.len >= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCstring:
  1166. localError(p.config, x.info, "cstring doesn't support `[]=` operator")
  1167. gen(p, x, a)
  1168. genLineDir(p, y)
  1169. gen(p, y, b)
  1170. # we don't care if it's an etyBaseIndex (global) of a string, it's
  1171. # still a string that needs to be copied properly:
  1172. if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}:
  1173. xtyp = etySeq
  1174. case xtyp
  1175. of etySeq:
  1176. if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
  1177. lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
  1178. else:
  1179. useMagic(p, "nimCopy")
  1180. lineF(p, "$1 = nimCopy(null, $2, $3);$n",
  1181. [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
  1182. of etyObject:
  1183. if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
  1184. lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
  1185. else:
  1186. useMagic(p, "nimCopy")
  1187. # supports proc getF(): var T
  1188. if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds:
  1189. lineF(p, "nimCopy($1, $2, $3);$n",
  1190. [a.res, b.res, genTypeInfo(p, x.typ)])
  1191. else:
  1192. lineF(p, "$1 = nimCopy($1, $2, $3);$n",
  1193. [a.res, b.res, genTypeInfo(p, x.typ)])
  1194. of etyBaseIndex:
  1195. if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
  1196. if y.kind == nkCall:
  1197. let tmp = p.getTemp(false)
  1198. lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
  1199. elif b.typ == etyBaseIndex:
  1200. lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
  1201. elif b.typ == etyNone:
  1202. internalAssert p.config, b.address == ""
  1203. lineF(p, "$# = [$#, 0];$n", [a.address, b.res])
  1204. elif x.typ.kind == tyVar and y.typ.kind == tyPtr:
  1205. lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
  1206. lineF(p, "$1 = $2;$n", [a.address, b.res])
  1207. lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
  1208. elif a.typ == etyBaseIndex:
  1209. # array indexing may not map to var type
  1210. if b.address != "":
  1211. lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
  1212. else:
  1213. lineF(p, "$1 = $2;$n", [a.address, b.res])
  1214. else:
  1215. internalError(p.config, x.info, $("genAsgn", b.typ, a.typ))
  1216. elif b.address != "":
  1217. lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
  1218. else:
  1219. lineF(p, "$1 = $2;$n", [a.address, b.res])
  1220. else:
  1221. lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
  1222. proc genAsgn(p: PProc, n: PNode) =
  1223. genAsgnAux(p, n[0], n[1], noCopyNeeded=false)
  1224. proc genFastAsgn(p: PProc, n: PNode) =
  1225. # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong
  1226. # for code like
  1227. # while j >= pos:
  1228. # dest[i].shallowCopy(dest[j])
  1229. # See bug #5933. So we try to be more compatible with the C backend semantics
  1230. # here for 'shallowCopy'. This is an educated guess and might require further
  1231. # changes later:
  1232. let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString}
  1233. genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy)
  1234. proc genSwap(p: PProc, n: PNode) =
  1235. var a, b: TCompRes = default(TCompRes)
  1236. gen(p, n[1], a)
  1237. gen(p, n[2], b)
  1238. var tmp = p.getTemp(false)
  1239. if mapType(p, skipTypes(n[1].typ, abstractVar)) == etyBaseIndex:
  1240. let tmp2 = p.getTemp(false)
  1241. if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
  1242. internalError(p.config, n.info, "genSwap")
  1243. lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n",
  1244. [tmp, a.address, b.address])
  1245. tmp = tmp2
  1246. lineF(p, "var $1 = $2; $2 = $3; $3 = $1;",
  1247. [tmp, a.res, b.res])
  1248. proc getFieldPosition(p: PProc; f: PNode): int =
  1249. case f.kind
  1250. of nkIntLit..nkUInt64Lit: result = int(f.intVal)
  1251. of nkSym: result = f.sym.position
  1252. else:
  1253. result = 0
  1254. internalError(p.config, f.info, "genFieldPosition")
  1255. proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
  1256. var a: TCompRes = default(TCompRes)
  1257. r.typ = etyBaseIndex
  1258. let b = if n.kind == nkHiddenAddr: n[0] else: n
  1259. gen(p, b[0], a)
  1260. if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
  1261. r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
  1262. else:
  1263. if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
  1264. var f = b[1].sym
  1265. if f.loc.r == "": f.loc.r = mangleName(p.module, f)
  1266. r.res = makeJSString($f.loc.r)
  1267. internalAssert p.config, a.typ != etyBaseIndex
  1268. r.address = a.res
  1269. r.kind = resExpr
  1270. proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
  1271. gen(p, n[0], r)
  1272. r.typ = mapType(n.typ)
  1273. let otyp = skipTypes(n[0].typ, abstractVarRange)
  1274. template mkTemp(i: int) =
  1275. if r.typ == etyBaseIndex:
  1276. if needsTemp(p, n[i]):
  1277. let tmp = p.getTemp
  1278. r.address = "($1 = $2, $1)[0]" % [tmp, r.res]
  1279. r.res = "$1[1]" % [tmp]
  1280. r.tmpLoc = tmp
  1281. else:
  1282. r.address = "$1[0]" % [r.res]
  1283. r.res = "$1[1]" % [r.res]
  1284. if otyp.kind == tyTuple:
  1285. r.res = ("$1.Field$2") %
  1286. [r.res, getFieldPosition(p, n[1]).rope]
  1287. mkTemp(0)
  1288. else:
  1289. if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
  1290. var f = n[1].sym
  1291. if f.loc.r == "": f.loc.r = mangleName(p.module, f)
  1292. r.res = "$1.$2" % [r.res, f.loc.r]
  1293. mkTemp(1)
  1294. r.kind = resExpr
  1295. proc genAddr(p: PProc, n: PNode, r: var TCompRes)
  1296. proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
  1297. internalAssert p.config, n.kind == nkCheckedFieldExpr
  1298. # nkDotExpr to access the requested field
  1299. let accessExpr = n[0]
  1300. # nkCall to check if the discriminant is valid
  1301. var checkExpr = n[1]
  1302. let negCheck = checkExpr[0].sym.magic == mNot
  1303. if negCheck:
  1304. checkExpr = checkExpr[^1]
  1305. # Field symbol
  1306. var field = accessExpr[1].sym
  1307. internalAssert p.config, field.kind == skField
  1308. if field.loc.r == "": field.loc.r = mangleName(p.module, field)
  1309. # Discriminant symbol
  1310. let disc = checkExpr[2].sym
  1311. internalAssert p.config, disc.kind == skField
  1312. if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc)
  1313. var setx: TCompRes = default(TCompRes)
  1314. gen(p, checkExpr[1], setx)
  1315. var obj: TCompRes = default(TCompRes)
  1316. gen(p, accessExpr[0], obj)
  1317. # Avoid evaluating the LHS twice (one to read the discriminant and one to read
  1318. # the field)
  1319. let tmp = p.getTemp()
  1320. lineF(p, "var $1 = $2;$n", tmp, obj.res)
  1321. useMagic(p, "raiseFieldError2")
  1322. useMagic(p, "makeNimstrLit")
  1323. useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
  1324. let msg = genFieldDefect(p.config, field.name.s, disc)
  1325. lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
  1326. setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===",
  1327. makeJSString(msg), genTypeInfo(p, disc.typ))
  1328. if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
  1329. r.typ = etyBaseIndex
  1330. r.res = makeJSString($field.loc.r)
  1331. r.address = tmp
  1332. else:
  1333. r.typ = etyNone
  1334. r.res = "$1.$2" % [tmp, field.loc.r]
  1335. r.kind = resExpr
  1336. proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
  1337. var
  1338. a, b: TCompRes = default(TCompRes)
  1339. first: Int128 = Zero
  1340. r.typ = etyBaseIndex
  1341. let m = if n.kind == nkHiddenAddr: n[0] else: n
  1342. gen(p, m[0], a)
  1343. gen(p, m[1], b)
  1344. #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
  1345. let (x, tmp) = maybeMakeTemp(p, m[0], a)
  1346. r.address = x
  1347. var typ = skipTypes(m[0].typ, abstractPtrs)
  1348. if typ.kind == tyArray:
  1349. first = firstOrd(p.config, typ.indexType)
  1350. if optBoundsCheck in p.options:
  1351. useMagic(p, "chckIndx")
  1352. if first == 0: # save a couple chars
  1353. r.res = "chckIndx($1, 0, ($2).length - 1)" % [b.res, tmp]
  1354. else:
  1355. r.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [
  1356. b.res, rope(first), tmp]
  1357. elif first != 0:
  1358. r.res = "($1) - ($2)" % [b.res, rope(first)]
  1359. else:
  1360. r.res = b.res
  1361. r.kind = resExpr
  1362. proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
  1363. var ty = skipTypes(n[0].typ, abstractVarRange)
  1364. if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange)
  1365. case ty.kind
  1366. of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
  1367. genArrayAddr(p, n, r)
  1368. of tyTuple:
  1369. genFieldAddr(p, n, r)
  1370. else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
  1371. r.typ = mapType(n.typ)
  1372. if r.res == "": internalError(p.config, n.info, "genArrayAccess")
  1373. if ty.kind == tyCstring:
  1374. r.res = "$1.charCodeAt($2)" % [r.address, r.res]
  1375. elif r.typ == etyBaseIndex:
  1376. if needsTemp(p, n[0]):
  1377. let tmp = p.getTemp
  1378. r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
  1379. r.res = "$1[1]" % [tmp]
  1380. r.tmpLoc = tmp
  1381. else:
  1382. let x = r.rdLoc
  1383. r.address = "$1[0]" % [x]
  1384. r.res = "$1[1]" % [x]
  1385. else:
  1386. r.res = "$1[$2]" % [r.address, r.res]
  1387. r.kind = resExpr
  1388. template isIndirect(x: PSym): bool =
  1389. let v = x
  1390. ({sfAddrTaken, sfGlobal} * v.flags != {} and
  1391. #(mapType(v.typ) != etyObject) and
  1392. {sfImportc, sfExportc} * v.flags == {} and
  1393. v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator,
  1394. skConst, skTemp, skLet})
  1395. proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
  1396. let s = n.sym
  1397. if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3")
  1398. case s.kind
  1399. of skParam:
  1400. r.res = s.loc.r
  1401. r.address = ""
  1402. r.typ = etyNone
  1403. of skVar, skLet, skResult:
  1404. r.kind = resExpr
  1405. let jsType = mapType(p):
  1406. if typ.isNil:
  1407. n.typ
  1408. else:
  1409. typ
  1410. if jsType == etyObject:
  1411. # make addr() a no-op:
  1412. r.typ = etyNone
  1413. if isIndirect(s):
  1414. r.res = s.loc.r & "[0]"
  1415. else:
  1416. r.res = s.loc.r
  1417. r.address = ""
  1418. elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
  1419. # for ease of code generation, we do not distinguish between
  1420. # sfAddrTaken and sfGlobal.
  1421. r.typ = etyBaseIndex
  1422. r.address = s.loc.r
  1423. r.res = rope("0")
  1424. else:
  1425. # 'var openArray' for instance produces an 'addr' but this is harmless:
  1426. gen(p, n, r)
  1427. #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n))
  1428. else: internalError(p.config, n.info, $("genAddr: 2", s.kind))
  1429. proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
  1430. if n.kind == nkSym:
  1431. genSymAddr(p, n, nil, r)
  1432. else:
  1433. case n[0].kind
  1434. of nkSym:
  1435. genSymAddr(p, n[0], n.typ, r)
  1436. of nkCheckedFieldExpr:
  1437. genCheckedFieldOp(p, n[0], n.typ, r)
  1438. of nkDotExpr:
  1439. if mapType(p, n.typ) == etyBaseIndex:
  1440. genFieldAddr(p, n[0], r)
  1441. else:
  1442. genFieldAccess(p, n[0], r)
  1443. of nkBracketExpr:
  1444. var ty = skipTypes(n[0].typ, abstractVarRange)
  1445. if ty.kind in MappedToObject:
  1446. gen(p, n[0], r)
  1447. else:
  1448. let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind
  1449. case kindOfIndexedExpr
  1450. of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
  1451. genArrayAddr(p, n[0], r)
  1452. of tyTuple:
  1453. genFieldAddr(p, n[0], r)
  1454. of tyGenericBody:
  1455. genAddr(p, n[^1], r)
  1456. else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
  1457. of nkObjDownConv:
  1458. gen(p, n[0], r)
  1459. of nkHiddenDeref:
  1460. gen(p, n[0], r)
  1461. of nkDerefExpr:
  1462. var x = n[0]
  1463. if n.kind == nkHiddenAddr:
  1464. x = n[0][0]
  1465. if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
  1466. x.typ = n.typ
  1467. gen(p, x, r)
  1468. of nkHiddenAddr:
  1469. gen(p, n[0], r)
  1470. of nkConv:
  1471. genAddr(p, n[0], r)
  1472. of nkStmtListExpr:
  1473. if n.len == 1: gen(p, n[0], r)
  1474. else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr")
  1475. of nkCallKinds:
  1476. if n[0].typ.kind == tyOpenArray:
  1477. # 'var openArray' for instance produces an 'addr' but this is harmless:
  1478. # namely toOpenArray(a, 1, 3)
  1479. gen(p, n[0], r)
  1480. else:
  1481. internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
  1482. else:
  1483. internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
  1484. proc attachProc(p: PProc; content: Rope; s: PSym) =
  1485. p.g.code.add(content)
  1486. proc attachProc(p: PProc; s: PSym) =
  1487. let newp = genProc(p, s)
  1488. attachProc(p, newp, s)
  1489. proc genProcForSymIfNeeded(p: PProc, s: PSym) =
  1490. if not p.g.generatedSyms.containsOrIncl(s.id):
  1491. let newp = genProc(p, s)
  1492. var owner = p
  1493. while owner != nil and owner.prc != s.owner:
  1494. owner = owner.up
  1495. if owner != nil: owner.locals.add(newp)
  1496. else: attachProc(p, newp, s)
  1497. proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
  1498. let s = n.sym
  1499. if p.prc == s.owner or needsNoCopy(p, n):
  1500. return
  1501. var owner = p.up
  1502. while true:
  1503. if owner == nil:
  1504. internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
  1505. if owner.prc == s.owner:
  1506. if not owner.generatedParamCopies.containsOrIncl(s.id):
  1507. let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
  1508. owner.locals.add(owner.indentLine(copy))
  1509. return
  1510. owner = owner.up
  1511. proc genVarInit(p: PProc, v: PSym, n: PNode)
  1512. proc genSym(p: PProc, n: PNode, r: var TCompRes) =
  1513. var s = n.sym
  1514. case s.kind
  1515. of skVar, skLet, skParam, skTemp, skResult, skForVar:
  1516. if s.loc.r == "":
  1517. internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
  1518. if sfCompileTime in s.flags:
  1519. genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
  1520. if s.kind == skParam:
  1521. genCopyForParamIfNeeded(p, n)
  1522. let k = mapType(p, s.typ)
  1523. if k == etyBaseIndex:
  1524. r.typ = etyBaseIndex
  1525. if {sfAddrTaken, sfGlobal} * s.flags != {}:
  1526. if isIndirect(s):
  1527. r.address = "$1[0][0]" % [s.loc.r]
  1528. r.res = "$1[0][1]" % [s.loc.r]
  1529. else:
  1530. r.address = "$1[0]" % [s.loc.r]
  1531. r.res = "$1[1]" % [s.loc.r]
  1532. else:
  1533. r.address = s.loc.r
  1534. r.res = s.loc.r & "_Idx"
  1535. elif isIndirect(s):
  1536. r.res = "$1[0]" % [s.loc.r]
  1537. else:
  1538. r.res = s.loc.r
  1539. of skConst:
  1540. genConstant(p, s)
  1541. if s.loc.r == "":
  1542. internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
  1543. r.res = s.loc.r
  1544. of skProc, skFunc, skConverter, skMethod:
  1545. if sfCompileTime in s.flags:
  1546. localError(p.config, n.info, "request to generate code for .compileTime proc: " &
  1547. s.name.s)
  1548. discard mangleName(p.module, s)
  1549. r.res = s.loc.r
  1550. if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
  1551. {sfImportc, sfInfixCall} * s.flags != {}:
  1552. discard
  1553. elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty:
  1554. # we cannot produce code for the dispatcher yet:
  1555. discard
  1556. elif sfForward in s.flags:
  1557. p.g.forwarded.add(s)
  1558. else:
  1559. genProcForSymIfNeeded(p, s)
  1560. else:
  1561. if s.loc.r == "":
  1562. internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
  1563. if mapType(p, s.typ) == etyBaseIndex:
  1564. r.address = s.loc.r
  1565. r.res = s.loc.r & "_Idx"
  1566. else:
  1567. r.res = s.loc.r
  1568. r.kind = resVal
  1569. proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
  1570. let it = n[0]
  1571. let t = mapType(p, it.typ)
  1572. if t == etyObject or it.typ.kind == tyLent:
  1573. gen(p, it, r)
  1574. else:
  1575. var a: TCompRes = default(TCompRes)
  1576. gen(p, it, a)
  1577. r.kind = a.kind
  1578. r.typ = mapType(p, n.typ)
  1579. if r.typ == etyBaseIndex:
  1580. let tmp = p.getTemp
  1581. r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc]
  1582. r.res = "$1[1]" % [tmp]
  1583. r.tmpLoc = tmp
  1584. elif a.typ == etyBaseIndex:
  1585. if a.tmpLoc != "":
  1586. r.tmpLoc = a.tmpLoc
  1587. r.res = a.rdLoc
  1588. else:
  1589. internalError(p.config, n.info, "genDeref")
  1590. proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
  1591. var a: TCompRes = default(TCompRes)
  1592. gen(p, n, a)
  1593. if a.typ == etyBaseIndex:
  1594. r.res.add(a.address)
  1595. r.res.add(", ")
  1596. r.res.add(a.res)
  1597. else:
  1598. r.res.add(a.res)
  1599. proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) =
  1600. var a: TCompRes = default(TCompRes)
  1601. gen(p, n, a)
  1602. if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
  1603. a.typ == etyBaseIndex:
  1604. r.res.add("$1[$2]" % [a.address, a.res])
  1605. elif a.typ == etyBaseIndex:
  1606. r.res.add(a.address)
  1607. r.res.add(", ")
  1608. r.res.add(a.res)
  1609. if emitted != nil: inc emitted[]
  1610. elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent, tyOwned} and
  1611. n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
  1612. # this fixes bug #5608:
  1613. let tmp = getTemp(p)
  1614. r.res.add("($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
  1615. if emitted != nil: inc emitted[]
  1616. else:
  1617. r.res.add(a.res)
  1618. proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
  1619. r.res.add("(")
  1620. var hasArgs = false
  1621. var typ = skipTypes(n[0].typ, abstractInst)
  1622. assert(typ.kind == tyProc)
  1623. assert(typ.len == typ.n.len)
  1624. var emitted = start-1
  1625. for i in start..<n.len:
  1626. let it = n[i]
  1627. var paramType: PNode = nil
  1628. if i < typ.len:
  1629. assert(typ.n[i].kind == nkSym)
  1630. paramType = typ.n[i]
  1631. if paramType.typ.isCompileTimeOnly: continue
  1632. if hasArgs: r.res.add(", ")
  1633. if paramType.isNil:
  1634. genArgNoParam(p, it, r)
  1635. else:
  1636. genArg(p, it, paramType.sym, r, addr emitted)
  1637. inc emitted
  1638. hasArgs = true
  1639. r.res.add(")")
  1640. when false:
  1641. # XXX look into this:
  1642. let jsp = countJsParams(typ)
  1643. if emitted != jsp and tfVarargs notin typ.flags:
  1644. localError(p.config, n.info, "wrong number of parameters emitted; expected: " & $jsp &
  1645. " but got: " & $emitted)
  1646. r.kind = resExpr
  1647. proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
  1648. generated: var int; r: var TCompRes) =
  1649. if i >= n.len:
  1650. globalError(p.config, n.info, "wrong importcpp pattern; expected parameter at position " & $i &
  1651. " but got only: " & $(n.len-1))
  1652. let it = n[i]
  1653. var paramType: PNode = nil
  1654. if i < typ.len:
  1655. assert(typ.n[i].kind == nkSym)
  1656. paramType = typ.n[i]
  1657. if paramType.typ.isCompileTimeOnly: return
  1658. if paramType.isNil:
  1659. genArgNoParam(p, it, r)
  1660. else:
  1661. genArg(p, it, paramType.sym, r)
  1662. inc generated
  1663. proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
  1664. r: var TCompRes) =
  1665. var i = 0
  1666. var j = 1
  1667. r.kind = resExpr
  1668. while i < pat.len:
  1669. case pat[i]
  1670. of '@':
  1671. var generated = 0
  1672. for k in j..<n.len:
  1673. if generated > 0: r.res.add(", ")
  1674. genOtherArg(p, n, k, typ, generated, r)
  1675. inc i
  1676. of '#':
  1677. var generated = 0
  1678. genOtherArg(p, n, j, typ, generated, r)
  1679. inc j
  1680. inc i
  1681. of '\31':
  1682. # unit separator
  1683. r.res.add("#")
  1684. inc i
  1685. of '\29':
  1686. # group separator
  1687. r.res.add("@")
  1688. inc i
  1689. else:
  1690. let start = i
  1691. while i < pat.len:
  1692. if pat[i] notin {'@', '#', '\31', '\29'}: inc(i)
  1693. else: break
  1694. if i - 1 >= start:
  1695. r.res.add(substr(pat, start, i - 1))
  1696. proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
  1697. # don't call '$' here for efficiency:
  1698. let f = n[0].sym
  1699. if f.loc.r == "": f.loc.r = mangleName(p.module, f)
  1700. if sfInfixCall in f.flags:
  1701. let pat = $n[0].sym.loc.r
  1702. internalAssert p.config, pat.len > 0
  1703. if pat.contains({'#', '(', '@'}):
  1704. var typ = skipTypes(n[0].typ, abstractInst)
  1705. assert(typ.kind == tyProc)
  1706. genPatternCall(p, n, pat, typ, r)
  1707. return
  1708. if n.len != 1:
  1709. gen(p, n[1], r)
  1710. if r.typ == etyBaseIndex:
  1711. if r.address == "":
  1712. globalError(p.config, n.info, "cannot invoke with infix syntax")
  1713. r.res = "$1[$2]" % [r.address, r.res]
  1714. r.address = ""
  1715. r.typ = etyNone
  1716. r.res.add(".")
  1717. var op: TCompRes = default(TCompRes)
  1718. gen(p, n[0], op)
  1719. r.res.add(op.res)
  1720. genArgs(p, n, r, 2)
  1721. proc genCall(p: PProc, n: PNode, r: var TCompRes) =
  1722. gen(p, n[0], r)
  1723. genArgs(p, n, r)
  1724. if n.typ != nil:
  1725. let t = mapType(n.typ)
  1726. if t == etyBaseIndex:
  1727. let tmp = p.getTemp
  1728. r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
  1729. r.res = "$1[1]" % [tmp]
  1730. r.tmpLoc = tmp
  1731. r.typ = t
  1732. proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
  1733. let n = n[1].skipConv
  1734. internalAssert p.config, n.kind == nkBracket
  1735. useMagic(p, "toJSStr") # Used in rawEcho
  1736. useMagic(p, "rawEcho")
  1737. r.res.add("rawEcho(")
  1738. for i in 0..<n.len:
  1739. let it = n[i]
  1740. if it.typ.isCompileTimeOnly: continue
  1741. if i > 0: r.res.add(", ")
  1742. genArgNoParam(p, it, r)
  1743. r.res.add(")")
  1744. r.kind = resExpr
  1745. proc putToSeq(s: string, indirect: bool): Rope =
  1746. result = rope(s)
  1747. if indirect: result = "[$1]" % [result]
  1748. proc createVar(p: PProc, typ: PType, indirect: bool): Rope
  1749. proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) =
  1750. case rec.kind
  1751. of nkRecList:
  1752. for i in 0..<rec.len:
  1753. createRecordVarAux(p, rec[i], excludedFieldIDs, output)
  1754. of nkRecCase:
  1755. createRecordVarAux(p, rec[0], excludedFieldIDs, output)
  1756. for i in 1..<rec.len:
  1757. createRecordVarAux(p, lastSon(rec[i]), excludedFieldIDs, output)
  1758. of nkSym:
  1759. # Do not produce code for void types
  1760. if isEmptyType(rec.sym.typ): return
  1761. if rec.sym.id notin excludedFieldIDs:
  1762. if output.len > 0: output.add(", ")
  1763. output.addf("$#: ", [mangleName(p.module, rec.sym)])
  1764. output.add(createVar(p, rec.sym.typ, false))
  1765. else: internalError(p.config, rec.info, "createRecordVarAux")
  1766. proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
  1767. var t = typ
  1768. if objHasTypeField(t):
  1769. if output.len > 0: output.add(", ")
  1770. output.addf("m_type: $1", [genTypeInfo(p, t)])
  1771. while t != nil:
  1772. t = t.skipTypes(skipPtrs)
  1773. createRecordVarAux(p, t.n, excludedFieldIDs, output)
  1774. t = t.baseClass
  1775. proc arrayTypeForElemType(conf: ConfigRef; typ: PType): string =
  1776. let typ = typ.skipTypes(abstractRange)
  1777. case typ.kind
  1778. of tyInt, tyInt32: "Int32Array"
  1779. of tyInt16: "Int16Array"
  1780. of tyInt8: "Int8Array"
  1781. of tyInt64:
  1782. if optJsBigInt64 in conf.globalOptions:
  1783. "BigInt64Array"
  1784. else:
  1785. ""
  1786. of tyUInt, tyUInt32: "Uint32Array"
  1787. of tyUInt16: "Uint16Array"
  1788. of tyUInt8, tyChar, tyBool: "Uint8Array"
  1789. of tyUInt64:
  1790. if optJsBigInt64 in conf.globalOptions:
  1791. "BigUint64Array"
  1792. else:
  1793. ""
  1794. of tyFloat32: "Float32Array"
  1795. of tyFloat64, tyFloat: "Float64Array"
  1796. of tyEnum:
  1797. case typ.size
  1798. of 1: "Uint8Array"
  1799. of 2: "Uint16Array"
  1800. of 4: "Uint32Array"
  1801. else: ""
  1802. else: ""
  1803. proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
  1804. var t = skipTypes(typ, abstractInst)
  1805. case t.kind
  1806. of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
  1807. result = putToSeq("0", indirect)
  1808. of tyInt, tyUInt:
  1809. if $t.sym.loc.r == "bigint":
  1810. result = putToSeq("0n", indirect)
  1811. else:
  1812. result = putToSeq("0", indirect)
  1813. of tyInt64, tyUInt64:
  1814. if optJsBigInt64 in p.config.globalOptions:
  1815. result = putToSeq("0n", indirect)
  1816. else:
  1817. result = putToSeq("0", indirect)
  1818. of tyFloat..tyFloat128:
  1819. result = putToSeq("0.0", indirect)
  1820. of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent:
  1821. result = createVar(p, skipModifier(typ), indirect)
  1822. of tySet:
  1823. result = putToSeq("{}", indirect)
  1824. of tyBool:
  1825. result = putToSeq("false", indirect)
  1826. of tyNil:
  1827. result = putToSeq("null", indirect)
  1828. of tyArray:
  1829. let length = toInt(lengthOrd(p.config, t))
  1830. let e = elemType(t)
  1831. let jsTyp = arrayTypeForElemType(p.config, e)
  1832. if jsTyp.len > 0:
  1833. result = "new $1($2)" % [rope(jsTyp), rope(length)]
  1834. elif length > 32:
  1835. useMagic(p, "arrayConstr")
  1836. # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary.
  1837. useMagic(p, "nimCopy")
  1838. result = "arrayConstr($1, $2, $3)" % [rope(length),
  1839. createVar(p, e, false), genTypeInfo(p, e)]
  1840. else:
  1841. result = rope("[")
  1842. var i = 0
  1843. while i < length:
  1844. if i > 0: result.add(", ")
  1845. result.add(createVar(p, e, false))
  1846. inc(i)
  1847. result.add("]")
  1848. if indirect: result = "[$1]" % [result]
  1849. of tyTuple:
  1850. result = rope("{")
  1851. for i in 0..<t.len:
  1852. if i > 0: result.add(", ")
  1853. result.addf("Field$1: $2", [i.rope,
  1854. createVar(p, t[i], false)])
  1855. result.add("}")
  1856. if indirect: result = "[$1]" % [result]
  1857. of tyObject:
  1858. var initList: Rope = ""
  1859. createObjInitList(p, t, initIntSet(), initList)
  1860. result = ("({$1})") % [initList]
  1861. if indirect: result = "[$1]" % [result]
  1862. of tyVar, tyPtr, tyRef, tyPointer:
  1863. if mapType(p, t) == etyBaseIndex:
  1864. result = putToSeq("[null, 0]", indirect)
  1865. else:
  1866. result = putToSeq("null", indirect)
  1867. of tySequence, tyString:
  1868. result = putToSeq("[]", indirect)
  1869. of tyCstring, tyProc:
  1870. result = putToSeq("null", indirect)
  1871. of tyStatic:
  1872. if t.n != nil:
  1873. result = createVar(p, skipModifier t, indirect)
  1874. else:
  1875. internalError(p.config, "createVar: " & $t.kind)
  1876. result = ""
  1877. else:
  1878. internalError(p.config, "createVar: " & $t.kind)
  1879. result = ""
  1880. template returnType: untyped = ""
  1881. proc genVarInit(p: PProc, v: PSym, n: PNode) =
  1882. var
  1883. a: TCompRes = default(TCompRes)
  1884. s: Rope
  1885. varCode: string
  1886. varName = mangleName(p.module, v)
  1887. useReloadingGuard = sfGlobal in v.flags and p.config.hcrOn
  1888. useGlobalPragmas = sfGlobal in v.flags and ({sfPure, sfThread} * v.flags != {})
  1889. if v.constraint.isNil:
  1890. if useReloadingGuard:
  1891. lineF(p, "var $1;$n", varName)
  1892. lineF(p, "if ($1 === undefined) {$n", varName)
  1893. varCode = $varName
  1894. inc p.extraIndent
  1895. elif useGlobalPragmas:
  1896. lineF(p, "if (globalThis.$1 === undefined) {$n", varName)
  1897. varCode = "globalThis." & $varName
  1898. inc p.extraIndent
  1899. else:
  1900. varCode = "var $2"
  1901. else:
  1902. # Is this really a thought through feature? this basically unused
  1903. # feature makes it impossible for almost all format strings in
  1904. # this function to be checked at compile time.
  1905. varCode = v.constraint.strVal
  1906. if n.kind == nkEmpty:
  1907. if not isIndirect(v) and
  1908. v.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, v.typ) == etyBaseIndex:
  1909. lineF(p, "var $1 = null;$n", [varName])
  1910. lineF(p, "var $1_Idx = 0;$n", [varName])
  1911. else:
  1912. line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]))
  1913. else:
  1914. gen(p, n, a)
  1915. case mapType(p, v.typ)
  1916. of etyObject, etySeq:
  1917. if needsNoCopy(p, n):
  1918. s = a.res
  1919. else:
  1920. useMagic(p, "nimCopy")
  1921. s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
  1922. of etyBaseIndex:
  1923. let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
  1924. if a.typ == etyBaseIndex:
  1925. if targetBaseIndex:
  1926. line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
  1927. [returnType, v.loc.r, a.address, a.res]))
  1928. else:
  1929. if isIndirect(v):
  1930. line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
  1931. [returnType, v.loc.r, a.address, a.res]))
  1932. else:
  1933. line(p, runtimeFormat(varCode & " = [$3, $4];$n",
  1934. [returnType, v.loc.r, a.address, a.res]))
  1935. else:
  1936. if targetBaseIndex:
  1937. let tmp = p.getTemp
  1938. lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
  1939. [tmp, a.res, v.loc.r])
  1940. else:
  1941. line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
  1942. return
  1943. else:
  1944. s = a.res
  1945. if isIndirect(v):
  1946. line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
  1947. else:
  1948. line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
  1949. if useReloadingGuard or useGlobalPragmas:
  1950. dec p.extraIndent
  1951. lineF(p, "}$n")
  1952. proc genVarStmt(p: PProc, n: PNode) =
  1953. for i in 0..<n.len:
  1954. var a = n[i]
  1955. if a.kind != nkCommentStmt:
  1956. if a.kind == nkVarTuple:
  1957. let unpacked = lowerTupleUnpacking(p.module.graph, a, p.module.idgen, p.prc)
  1958. genStmt(p, unpacked)
  1959. else:
  1960. assert(a.kind == nkIdentDefs)
  1961. assert(a[0].kind == nkSym)
  1962. var v = a[0].sym
  1963. if lfNoDecl notin v.loc.flags and sfImportc notin v.flags:
  1964. genLineDir(p, a)
  1965. if sfCompileTime notin v.flags:
  1966. genVarInit(p, v, a[2])
  1967. else:
  1968. # lazy emit, done when it's actually used.
  1969. if v.ast == nil: v.ast = a[2]
  1970. proc genConstant(p: PProc, c: PSym) =
  1971. if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
  1972. let oldBody = move p.body
  1973. #genLineDir(p, c.astdef)
  1974. genVarInit(p, c, c.astdef)
  1975. p.g.constants.add(p.body)
  1976. p.body = oldBody
  1977. proc genNew(p: PProc, n: PNode) =
  1978. var a: TCompRes = default(TCompRes)
  1979. gen(p, n[1], a)
  1980. var t = skipTypes(n[1].typ, abstractVar)[0]
  1981. if mapType(t) == etyObject:
  1982. lineF(p, "$1 = $2;$n", [a.rdLoc, createVar(p, t, false)])
  1983. elif a.typ == etyBaseIndex:
  1984. lineF(p, "$1 = [$3]; $2 = 0;$n", [a.address, a.res, createVar(p, t, false)])
  1985. else:
  1986. lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)])
  1987. proc genNewSeq(p: PProc, n: PNode) =
  1988. var x, y: TCompRes = default(TCompRes)
  1989. gen(p, n[1], x)
  1990. gen(p, n[2], y)
  1991. let t = skipTypes(n[1].typ, abstractVar)[0]
  1992. lineF(p, "$1 = new Array($2); for (var i = 0 ; i < $2 ; ++i) { $1[i] = $3; }", [
  1993. x.rdLoc, y.rdLoc, createVar(p, t, false)])
  1994. proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
  1995. case skipTypes(n[1].typ, abstractVar + abstractRange).kind
  1996. of tyEnum, tyInt..tyInt32, tyUInt..tyUInt32, tyChar: gen(p, n[1], r)
  1997. of tyInt64, tyUInt64:
  1998. if optJsBigInt64 in p.config.globalOptions:
  1999. unaryExpr(p, n, r, "", "Number($1)")
  2000. else: gen(p, n[1], r)
  2001. of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)")
  2002. else: internalError(p.config, n.info, "genOrd")
  2003. proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
  2004. var a: TCompRes = default(TCompRes)
  2005. gen(p, n[1], a)
  2006. r.kind = resExpr
  2007. if skipTypes(n[1].typ, abstractVarRange).kind == tyChar:
  2008. r.res.add("[$1].concat(" % [a.res])
  2009. else:
  2010. r.res.add("($1 || []).concat(" % [a.res])
  2011. for i in 2..<n.len - 1:
  2012. gen(p, n[i], a)
  2013. if skipTypes(n[i].typ, abstractVarRange).kind == tyChar:
  2014. r.res.add("[$1]," % [a.res])
  2015. else:
  2016. r.res.add("$1 || []," % [a.res])
  2017. gen(p, n[^1], a)
  2018. if skipTypes(n[^1].typ, abstractVarRange).kind == tyChar:
  2019. r.res.add("[$1])" % [a.res])
  2020. else:
  2021. r.res.add("$1 || [])" % [a.res])
  2022. proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") =
  2023. useMagic(p, magic)
  2024. r.res.add(magic & "(")
  2025. var a: TCompRes = default(TCompRes)
  2026. gen(p, n[1], a)
  2027. if magic == "reprAny":
  2028. # the pointer argument in reprAny is expandend to
  2029. # (pointedto, pointer), so we need to fill it
  2030. if a.address.len == 0:
  2031. r.res.add(a.res)
  2032. r.res.add(", null")
  2033. else:
  2034. r.res.add("$1, $2" % [a.address, a.res])
  2035. else:
  2036. r.res.add(a.res)
  2037. if typ != "":
  2038. r.res.add(", ")
  2039. r.res.add(typ)
  2040. r.res.add(")")
  2041. proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
  2042. let t = skipTypes(n[1].typ, abstractVarRange)
  2043. case t.kind
  2044. of tyInt..tyInt64, tyUInt..tyUInt64:
  2045. genReprAux(p, n, r, "reprInt")
  2046. of tyChar:
  2047. genReprAux(p, n, r, "reprChar")
  2048. of tyBool:
  2049. genReprAux(p, n, r, "reprBool")
  2050. of tyFloat..tyFloat128:
  2051. genReprAux(p, n, r, "reprFloat")
  2052. of tyString:
  2053. genReprAux(p, n, r, "reprStr")
  2054. of tyEnum, tyOrdinal:
  2055. genReprAux(p, n, r, "reprEnum", genTypeInfo(p, t))
  2056. of tySet:
  2057. genReprAux(p, n, r, "reprSet", genTypeInfo(p, t))
  2058. of tyEmpty, tyVoid:
  2059. localError(p.config, n.info, "'repr' doesn't support 'void' type")
  2060. of tyPointer:
  2061. genReprAux(p, n, r, "reprPointer")
  2062. of tyOpenArray, tyVarargs:
  2063. genReprAux(p, n, r, "reprJSONStringify")
  2064. else:
  2065. genReprAux(p, n, r, "reprAny", genTypeInfo(p, t))
  2066. r.kind = resExpr
  2067. proc genOf(p: PProc, n: PNode, r: var TCompRes) =
  2068. var x: TCompRes = default(TCompRes)
  2069. let t = skipTypes(n[2].typ,
  2070. abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned})
  2071. gen(p, n[1], x)
  2072. if tfFinal in t.flags:
  2073. r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)]
  2074. else:
  2075. useMagic(p, "isObj")
  2076. r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)]
  2077. r.kind = resExpr
  2078. proc genDefault(p: PProc, n: PNode; r: var TCompRes) =
  2079. r.res = createVar(p, n.typ, indirect = false)
  2080. r.kind = resExpr
  2081. proc genReset(p: PProc, n: PNode) =
  2082. var x: TCompRes = default(TCompRes)
  2083. useMagic(p, "genericReset")
  2084. gen(p, n[1], x)
  2085. if x.typ == etyBaseIndex:
  2086. lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
  2087. else:
  2088. let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
  2089. lineF(p, "$1 = genericReset($3, $2);$n", [a,
  2090. genTypeInfo(p, n[1].typ), tmp])
  2091. proc genMove(p: PProc; n: PNode; r: var TCompRes) =
  2092. var a: TCompRes = default(TCompRes)
  2093. r.kind = resVal
  2094. r.res = p.getTemp()
  2095. gen(p, n[1], a)
  2096. lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
  2097. genReset(p, n)
  2098. #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
  2099. proc genDup(p: PProc; n: PNode; r: var TCompRes) =
  2100. var a: TCompRes = default(TCompRes)
  2101. r.kind = resVal
  2102. r.res = p.getTemp()
  2103. gen(p, n[1], a)
  2104. lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
  2105. proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
  2106. var a: TCompRes = default(TCompRes)
  2107. r.res = rope("[")
  2108. r.kind = resExpr
  2109. for i in 0 ..< n.len:
  2110. if i > 0: r.res.add(", ")
  2111. gen(p, n[i], a)
  2112. if a.typ == etyBaseIndex:
  2113. r.res.addf("[$1, $2]", [a.address, a.res])
  2114. else:
  2115. if not needsNoCopy(p, n[i]):
  2116. let typ = n[i].typ.skipTypes(abstractInst)
  2117. useMagic(p, "nimCopy")
  2118. a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
  2119. r.res.add(a.res)
  2120. r.res.add("]")
  2121. proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
  2122. var
  2123. a: TCompRes
  2124. line, filen: Rope
  2125. var op = n[0].sym.magic
  2126. case op
  2127. of mOr: genOr(p, n[1], n[2], r)
  2128. of mAnd: genAnd(p, n[1], n[2], r)
  2129. of mAddI..mStrToStr: arith(p, n, r, op)
  2130. of mRepr: genRepr(p, n, r)
  2131. of mSwap: genSwap(p, n)
  2132. of mAppendStrCh:
  2133. binaryExpr(p, n, r, "addChar",
  2134. "addChar($1, $2);")
  2135. of mAppendStrStr:
  2136. var lhs, rhs: TCompRes = default(TCompRes)
  2137. gen(p, n[1], lhs)
  2138. gen(p, n[2], rhs)
  2139. if skipTypes(n[1].typ, abstractVarRange).kind == tyCstring:
  2140. let (b, tmp) = maybeMakeTemp(p, n[2], rhs)
  2141. r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" %
  2142. [b, lhs.rdLoc, tmp]
  2143. else:
  2144. let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
  2145. r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
  2146. r.kind = resExpr
  2147. of mAppendSeqElem:
  2148. var x, y: TCompRes = default(TCompRes)
  2149. gen(p, n[1], x)
  2150. gen(p, n[2], y)
  2151. if mapType(n[2].typ) == etyBaseIndex:
  2152. let c = "[$1, $2]" % [y.address, y.res]
  2153. r.res = "$1.push($2);" % [x.rdLoc, c]
  2154. elif needsNoCopy(p, n[2]):
  2155. r.res = "$1.push($2);" % [x.rdLoc, y.rdLoc]
  2156. else:
  2157. useMagic(p, "nimCopy")
  2158. let c = getTemp(p, defineInLocals=false)
  2159. lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
  2160. [c, y.rdLoc, genTypeInfo(p, n[2].typ)])
  2161. r.res = "$1.push($2);" % [x.rdLoc, c]
  2162. r.kind = resExpr
  2163. of mConStrStr:
  2164. genConStrStr(p, n, r)
  2165. of mEqStr:
  2166. binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
  2167. of mLeStr:
  2168. binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
  2169. of mLtStr:
  2170. binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
  2171. of mIsNil:
  2172. # we want to accept undefined, so we ==
  2173. if mapType(n[1].typ) != etyBaseIndex:
  2174. unaryExpr(p, n, r, "", "($1 == null)")
  2175. else:
  2176. var x: TCompRes = default(TCompRes)
  2177. gen(p, n[1], x)
  2178. r.res = "($# == null && $# === 0)" % [x.address, x.res]
  2179. of mEnumToStr: genRepr(p, n, r)
  2180. of mNew, mNewFinalize: genNew(p, n)
  2181. of mChr: gen(p, n[1], r)
  2182. of mArrToSeq:
  2183. # only array literals doesn't need copy
  2184. if n[1].kind == nkBracket:
  2185. genJSArrayConstr(p, n[1], r)
  2186. else:
  2187. var x: TCompRes = default(TCompRes)
  2188. gen(p, n[1], x)
  2189. useMagic(p, "nimCopy")
  2190. r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)]
  2191. of mOpenArrayToSeq:
  2192. genCall(p, n, r)
  2193. of mDestroy, mTrace: discard "ignore calls to the default destructor"
  2194. of mOrd: genOrd(p, n, r)
  2195. of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
  2196. var x: TCompRes = default(TCompRes)
  2197. gen(p, n[1], x)
  2198. if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
  2199. let (a, tmp) = maybeMakeTemp(p, n[1], x)
  2200. r.res = "(($1) == null ? 0 : ($2).length)" % [a, tmp]
  2201. else:
  2202. r.res = "($1).length" % [x.rdLoc]
  2203. r.kind = resExpr
  2204. of mHigh:
  2205. var x: TCompRes = default(TCompRes)
  2206. gen(p, n[1], x)
  2207. if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
  2208. let (a, tmp) = maybeMakeTemp(p, n[1], x)
  2209. r.res = "(($1) == null ? -1 : ($2).length - 1)" % [a, tmp]
  2210. else:
  2211. r.res = "($1).length - 1" % [x.rdLoc]
  2212. r.kind = resExpr
  2213. of mInc:
  2214. let typ = n[1].typ.skipTypes(abstractVarRange)
  2215. case typ.kind
  2216. of tyUInt..tyUInt32:
  2217. binaryUintExpr(p, n, r, "+", true)
  2218. of tyUInt64:
  2219. if optJsBigInt64 in p.config.globalOptions:
  2220. binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 + BigInt($2))", true)
  2221. else: binaryUintExpr(p, n, r, "+", true)
  2222. elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  2223. if optOverflowCheck notin p.options:
  2224. binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 + BigInt($2))", true)
  2225. else: binaryExpr(p, n, r, "addInt64", "$1 = addInt64($3, BigInt($2))", true)
  2226. else:
  2227. if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
  2228. else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true)
  2229. of ast.mDec:
  2230. let typ = n[1].typ.skipTypes(abstractVarRange)
  2231. case typ.kind
  2232. of tyUInt..tyUInt32:
  2233. binaryUintExpr(p, n, r, "-", true)
  2234. of tyUInt64:
  2235. if optJsBigInt64 in p.config.globalOptions:
  2236. binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 - BigInt($2))", true)
  2237. else: binaryUintExpr(p, n, r, "-", true)
  2238. elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  2239. if optOverflowCheck notin p.options:
  2240. binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 - BigInt($2))", true)
  2241. else: binaryExpr(p, n, r, "subInt64", "$1 = subInt64($3, BigInt($2))", true)
  2242. else:
  2243. if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
  2244. else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true)
  2245. of mSetLengthStr:
  2246. binaryExpr(p, n, r, "mnewString",
  2247. """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
  2248. else {$3.length = $4; }""")
  2249. of mSetLengthSeq:
  2250. var x, y: TCompRes = default(TCompRes)
  2251. gen(p, n[1], x)
  2252. gen(p, n[2], y)
  2253. let t = skipTypes(n[1].typ, abstractVar)[0]
  2254. let (a, tmp) = maybeMakeTemp(p, n[1], x)
  2255. let (b, tmp2) = maybeMakeTemp(p, n[2], y)
  2256. r.res = """if ($1.length < $2) { for (var i = $4.length ; i < $5 ; ++i) $4.push($3); }
  2257. else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2]
  2258. r.kind = resExpr
  2259. of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
  2260. of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
  2261. of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
  2262. of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)")
  2263. of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
  2264. of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
  2265. of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
  2266. of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
  2267. of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
  2268. of mInSet:
  2269. binaryExpr(p, n, r, "", "($1[$2] != undefined)")
  2270. of mNewSeq: genNewSeq(p, n)
  2271. of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
  2272. of mOf: genOf(p, n, r)
  2273. of mDefault, mZeroDefault: genDefault(p, n, r)
  2274. of mReset, mWasMoved: genReset(p, n)
  2275. of mEcho: genEcho(p, n, r)
  2276. of mNLen..mNError, mSlurp, mStaticExec:
  2277. localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s)
  2278. of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
  2279. of mNewStringOfCap:
  2280. unaryExpr(p, n, r, "mnewString", "mnewString(0)")
  2281. of mDotDot:
  2282. genProcForSymIfNeeded(p, n[0].sym)
  2283. genCall(p, n, r)
  2284. of mParseBiggestFloat:
  2285. useMagic(p, "nimParseBiggestFloat")
  2286. genCall(p, n, r)
  2287. of mSlice:
  2288. # arr.slice([begin[, end]]): 'end' is exclusive
  2289. var x, y, z: TCompRes = default(TCompRes)
  2290. gen(p, n[1], x)
  2291. gen(p, n[2], y)
  2292. gen(p, n[3], z)
  2293. r.res = "($1.slice($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc]
  2294. r.kind = resExpr
  2295. of mMove:
  2296. genMove(p, n, r)
  2297. of mDup:
  2298. genDup(p, n, r)
  2299. of mEnsureMove:
  2300. gen(p, n[1], r)
  2301. else:
  2302. genCall(p, n, r)
  2303. #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
  2304. proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
  2305. var
  2306. a, b: TCompRes = default(TCompRes)
  2307. useMagic(p, "setConstr")
  2308. r.res = rope("setConstr(")
  2309. r.kind = resExpr
  2310. for i in 0..<n.len:
  2311. if i > 0: r.res.add(", ")
  2312. var it = n[i]
  2313. if it.kind == nkRange:
  2314. gen(p, it[0], a)
  2315. gen(p, it[1], b)
  2316. if it[0].typ.kind == tyBool:
  2317. r.res.addf("$1, $2", [a.res, b.res])
  2318. else:
  2319. r.res.addf("[$1, $2]", [a.res, b.res])
  2320. else:
  2321. gen(p, it, a)
  2322. r.res.add(a.res)
  2323. r.res.add(")")
  2324. # emit better code for constant sets:
  2325. if isDeepConstExpr(n):
  2326. inc(p.g.unique)
  2327. let tmp = rope("ConstSet") & rope(p.g.unique)
  2328. p.g.constants.addf("var $1 = $2;$n", [tmp, r.res])
  2329. r.res = tmp
  2330. proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
  2331. ## Constructs array or sequence.
  2332. ## Nim array of uint8..uint32, int8..int32 maps to JS typed arrays.
  2333. ## Nim sequence maps to JS array.
  2334. var t = skipTypes(n.typ, abstractInst)
  2335. let e = elemType(t)
  2336. let jsTyp = arrayTypeForElemType(p.config, e)
  2337. if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0:
  2338. # generate typed array
  2339. # for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]`
  2340. # TODO use `set` or loop to initialize typed array which improves performances in some situations
  2341. var a: TCompRes = default(TCompRes)
  2342. r.res = "new $1([" % [rope(jsTyp)]
  2343. r.kind = resExpr
  2344. for i in 0 ..< n.len:
  2345. if i > 0: r.res.add(", ")
  2346. gen(p, n[i], a)
  2347. r.res.add(a.res)
  2348. r.res.add("])")
  2349. else:
  2350. genJSArrayConstr(p, n, r)
  2351. proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
  2352. var a: TCompRes = default(TCompRes)
  2353. r.res = rope("{")
  2354. r.kind = resExpr
  2355. for i in 0..<n.len:
  2356. if i > 0: r.res.add(", ")
  2357. var it = n[i]
  2358. if it.kind == nkExprColonExpr: it = it[1]
  2359. gen(p, it, a)
  2360. let typ = it.typ.skipTypes(abstractInst)
  2361. if a.typ == etyBaseIndex:
  2362. r.res.addf("Field$#: [$#, $#]", [i.rope, a.address, a.res])
  2363. else:
  2364. if not needsNoCopy(p, it):
  2365. useMagic(p, "nimCopy")
  2366. a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
  2367. r.res.addf("Field$#: $#", [i.rope, a.res])
  2368. r.res.add("}")
  2369. proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
  2370. var a: TCompRes = default(TCompRes)
  2371. r.kind = resExpr
  2372. var initList : Rope = ""
  2373. var fieldIDs = initIntSet()
  2374. let nTyp = n.typ.skipTypes(abstractInst)
  2375. for i in 1..<n.len:
  2376. if i > 1: initList.add(", ")
  2377. var it = n[i]
  2378. internalAssert p.config, it.kind == nkExprColonExpr
  2379. let val = it[1]
  2380. gen(p, val, a)
  2381. var f = it[0].sym
  2382. if f.loc.r == "": f.loc.r = mangleName(p.module, f)
  2383. fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
  2384. let typ = val.typ.skipTypes(abstractInst)
  2385. if a.typ == etyBaseIndex:
  2386. initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res])
  2387. else:
  2388. if not needsNoCopy(p, val):
  2389. useMagic(p, "nimCopy")
  2390. a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
  2391. initList.addf("$#: $#", [f.loc.r, a.res])
  2392. let t = skipTypes(n.typ, abstractInst + skipPtrs)
  2393. createObjInitList(p, t, fieldIDs, initList)
  2394. r.res = ("{$1}") % [initList]
  2395. proc genConv(p: PProc, n: PNode, r: var TCompRes) =
  2396. var dest = skipTypes(n.typ, abstractVarRange)
  2397. var src = skipTypes(n[1].typ, abstractVarRange)
  2398. gen(p, n[1], r)
  2399. if dest.kind == src.kind:
  2400. # no-op conversion
  2401. return
  2402. let toInt = (dest.kind in tyInt..tyInt32)
  2403. let fromInt = (src.kind in tyInt..tyInt32)
  2404. let toUint = (dest.kind in tyUInt..tyUInt32)
  2405. let fromUint = (src.kind in tyUInt..tyUInt32)
  2406. if toUint and (fromInt or fromUint):
  2407. let trimmer = unsignedTrimmer(dest.size)
  2408. r.res = "($1 $2)" % [r.res, trimmer]
  2409. elif dest.kind == tyBool:
  2410. r.res = "(!!($1))" % [r.res]
  2411. r.kind = resExpr
  2412. elif toInt:
  2413. if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2414. r.res = "Number($1)" % [r.res]
  2415. else:
  2416. r.res = "(($1) | 0)" % [r.res]
  2417. elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  2418. if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
  2419. r.res = "BigInt($1)" % [r.res]
  2420. elif src.kind in {tyFloat..tyFloat64}:
  2421. r.res = "BigInt(Math.trunc($1))" % [r.res]
  2422. elif src.kind == tyUInt64:
  2423. r.res = "BigInt.asIntN(64, $1)" % [r.res]
  2424. elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
  2425. if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
  2426. r.res = "BigInt($1)" % [r.res]
  2427. elif fromInt: # could be negative
  2428. r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
  2429. elif src.kind in {tyFloat..tyFloat64}:
  2430. r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
  2431. elif src.kind == tyInt64:
  2432. r.res = "BigInt.asUintN(64, $1)" % [r.res]
  2433. elif toUint or dest.kind in tyFloat..tyFloat64:
  2434. if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2435. r.res = "Number($1)" % [r.res]
  2436. else:
  2437. # TODO: What types must we handle here?
  2438. discard
  2439. proc upConv(p: PProc, n: PNode, r: var TCompRes) =
  2440. gen(p, n[0], r) # XXX
  2441. proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
  2442. var a, b: TCompRes = default(TCompRes)
  2443. gen(p, n[0], r)
  2444. let src = skipTypes(n[0].typ, abstractVarRange)
  2445. let dest = skipTypes(n.typ, abstractVarRange)
  2446. if optRangeCheck notin p.options:
  2447. return
  2448. elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
  2449. if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2450. r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
  2451. else:
  2452. r.res = "BigInt.asUintN($1, BigInt($2))" % [$(dest.size * 8), r.res]
  2453. if not (dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions):
  2454. r.res = "Number($1)" % [r.res]
  2455. else:
  2456. if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2457. # we do a range check anyway, so it's ok if the number gets rounded
  2458. r.res = "Number($1)" % [r.res]
  2459. gen(p, n[1], a)
  2460. gen(p, n[2], b)
  2461. useMagic(p, "chckRange")
  2462. r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res]
  2463. r.kind = resExpr
  2464. proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
  2465. # we do an optimization here as this is likely to slow down
  2466. # much of the code otherwise:
  2467. if n[0].kind == nkCStringToString:
  2468. gen(p, n[0][0], r)
  2469. else:
  2470. gen(p, n[0], r)
  2471. if r.res == "": internalError(p.config, n.info, "convStrToCStr")
  2472. useMagic(p, "toJSStr")
  2473. r.res = "toJSStr($1)" % [r.res]
  2474. r.kind = resExpr
  2475. proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
  2476. # we do an optimization here as this is likely to slow down
  2477. # much of the code otherwise:
  2478. if n[0].kind == nkStringToCString:
  2479. gen(p, n[0][0], r)
  2480. else:
  2481. gen(p, n[0], r)
  2482. if r.res == "": internalError(p.config, n.info, "convCStrToStr")
  2483. useMagic(p, "cstrToNimstr")
  2484. r.res = "cstrToNimstr($1)" % [r.res]
  2485. r.kind = resExpr
  2486. proc genReturnStmt(p: PProc, n: PNode) =
  2487. if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt")
  2488. p.beforeRetNeeded = true
  2489. if n[0].kind != nkEmpty:
  2490. genStmt(p, n[0])
  2491. else:
  2492. genLineDir(p, n)
  2493. lineF(p, "break BeforeRet;$n", [])
  2494. proc frameCreate(p: PProc; procname, filename: Rope): Rope =
  2495. const frameFmt =
  2496. "var F = {procname: $1, prev: framePtr, filename: $2, line: 0};$n"
  2497. result = p.indentLine(frameFmt % [procname, filename])
  2498. result.add p.indentLine(ropes.`%`("framePtr = F;$n", []))
  2499. proc frameDestroy(p: PProc): Rope =
  2500. result = p.indentLine rope(("framePtr = F.prev;") & "\L")
  2501. proc genProcBody(p: PProc, prc: PSym): Rope =
  2502. if hasFrameInfo(p):
  2503. result = frameCreate(p,
  2504. makeJSString(prc.owner.name.s & '.' & prc.name.s),
  2505. makeJSString(toFilenameOption(p.config, prc.info.fileIndex, foStacktrace)))
  2506. else:
  2507. result = ""
  2508. if p.beforeRetNeeded:
  2509. result.add p.indentLine("BeforeRet: {\n")
  2510. result.add p.body
  2511. result.add p.indentLine("};\n")
  2512. else:
  2513. result.add(p.body)
  2514. if prc.typ.callConv == ccSysCall:
  2515. result = ("try {$n$1} catch (e) {$n" &
  2516. " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
  2517. if hasFrameInfo(p):
  2518. result.add(frameDestroy(p))
  2519. proc optionalLine(p: Rope): Rope =
  2520. if p == "":
  2521. return ""
  2522. else:
  2523. return p & "\L"
  2524. proc genProc(oldProc: PProc, prc: PSym): Rope =
  2525. ## Generate a JS procedure ('function').
  2526. result = ""
  2527. var
  2528. resultSym: PSym
  2529. a: TCompRes = default(TCompRes)
  2530. #if gVerbosity >= 3:
  2531. # echo "BEGIN generating code for: " & prc.name.s
  2532. var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
  2533. p.up = oldProc
  2534. var returnStmt: Rope = ""
  2535. var resultAsgn: Rope = ""
  2536. var name = mangleName(p.module, prc)
  2537. let header = generateHeader(p, prc.typ)
  2538. if prc.typ.returnType != nil and sfPure notin prc.flags:
  2539. resultSym = prc.ast[resultPos].sym
  2540. let mname = mangleName(p.module, resultSym)
  2541. # otherwise uses "fat pointers"
  2542. let useRawPointer = not isIndirect(resultSym) and
  2543. resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and
  2544. mapType(p, resultSym.typ) == etyBaseIndex
  2545. if useRawPointer:
  2546. resultAsgn = p.indentLine(("var $# = null;$n") % [mname])
  2547. resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
  2548. else:
  2549. let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
  2550. resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
  2551. gen(p, prc.ast[resultPos], a)
  2552. if mapType(p, resultSym.typ) == etyBaseIndex:
  2553. returnStmt = "return [$#, $#];$n" % [a.address, a.res]
  2554. else:
  2555. returnStmt = "return $#;$n" % [a.res]
  2556. var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {})
  2557. if sfInjectDestructors in prc.flags:
  2558. transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
  2559. p.nested: genStmt(p, transformedBody)
  2560. if optLineDir in p.config.options:
  2561. result = lineDir(p.config, prc.info, toLinenumber(prc.info))
  2562. var def: Rope
  2563. if not prc.constraint.isNil:
  2564. def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#",
  2565. [ returnType,
  2566. name,
  2567. header,
  2568. optionalLine(p.globals),
  2569. optionalLine(p.locals),
  2570. optionalLine(resultAsgn),
  2571. optionalLine(genProcBody(p, prc)),
  2572. optionalLine(p.indentLine(returnStmt))])
  2573. else:
  2574. # if optLineDir in p.config.options:
  2575. # result.add("\L")
  2576. if p.config.hcrOn:
  2577. # Here, we introduce thunks that create the equivalent of a jump table
  2578. # for all global functions, because references to them may be stored
  2579. # in JavaScript variables. The added indirection ensures that such
  2580. # references will end up calling the reloaded code.
  2581. var thunkName = name
  2582. name = name & "IMLP"
  2583. result.add("\Lfunction $#() { return $#.apply(this, arguments); }$n" %
  2584. [thunkName, name])
  2585. def = "\Lfunction $#($#) {$n$#$#$#$#$#" %
  2586. [ name,
  2587. header,
  2588. optionalLine(p.globals),
  2589. optionalLine(p.locals),
  2590. optionalLine(resultAsgn),
  2591. optionalLine(genProcBody(p, prc)),
  2592. optionalLine(p.indentLine(returnStmt))]
  2593. dec p.extraIndent
  2594. result.add p.indentLine(def)
  2595. result.add p.indentLine("}\n")
  2596. #if gVerbosity >= 3:
  2597. # echo "END generated code for: " & prc.name.s
  2598. proc genStmt(p: PProc, n: PNode) =
  2599. var r: TCompRes = default(TCompRes)
  2600. gen(p, n, r)
  2601. if r.res != "": lineF(p, "$#;$n", [r.res])
  2602. proc genPragma(p: PProc, n: PNode) =
  2603. for i in 0..<n.len:
  2604. let it = n[i]
  2605. case whichPragma(it)
  2606. of wEmit: genAsmOrEmitStmt(p, it[1])
  2607. of wPush:
  2608. processPushBackendOption(p.optionsStack, p.options, n, i+1)
  2609. of wPop:
  2610. processPopBackendOption(p.optionsStack, p.options)
  2611. else: discard
  2612. proc genCast(p: PProc, n: PNode, r: var TCompRes) =
  2613. var dest = skipTypes(n.typ, abstractVarRange)
  2614. var src = skipTypes(n[1].typ, abstractVarRange)
  2615. gen(p, n[1], r)
  2616. if dest.kind == src.kind:
  2617. # no-op conversion
  2618. return
  2619. let toInt = (dest.kind in tyInt..tyInt32)
  2620. let toUint = (dest.kind in tyUInt..tyUInt32)
  2621. let fromInt = (src.kind in tyInt..tyInt32)
  2622. let fromUint = (src.kind in tyUInt..tyUInt32)
  2623. if toUint:
  2624. if fromInt or fromUint:
  2625. r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
  2626. elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2627. r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res]
  2628. elif toInt:
  2629. if fromInt or fromUint:
  2630. r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
  2631. elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2632. r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res]
  2633. elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
  2634. if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
  2635. r.res = "BigInt($1)" % [r.res]
  2636. elif src.kind in {tyFloat..tyFloat64}:
  2637. r.res = "BigInt(Math.trunc($1))" % [r.res]
  2638. elif src.kind == tyUInt64:
  2639. r.res = "BigInt.asIntN(64, $1)" % [r.res]
  2640. elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
  2641. if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
  2642. r.res = "BigInt($1)" % [r.res]
  2643. elif fromInt: # could be negative
  2644. r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
  2645. elif src.kind in {tyFloat..tyFloat64}:
  2646. r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
  2647. elif src.kind == tyInt64:
  2648. r.res = "BigInt.asUintN(64, $1)" % [r.res]
  2649. elif dest.kind in tyFloat..tyFloat64:
  2650. if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
  2651. r.res = "Number($1)" % [r.res]
  2652. elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
  2653. r.address = r.res
  2654. r.res = "null"
  2655. r.typ = etyBaseIndex
  2656. elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer:
  2657. r.res = r.address
  2658. r.typ = etyObject
  2659. proc gen(p: PProc, n: PNode, r: var TCompRes) =
  2660. r.typ = etyNone
  2661. if r.kind != resCallee: r.kind = resNone
  2662. #r.address = ""
  2663. r.res = ""
  2664. case n.kind
  2665. of nkSym:
  2666. genSym(p, n, r)
  2667. of nkCharLit..nkUInt64Lit:
  2668. case n.typ.skipTypes(abstractVarRange).kind
  2669. of tyBool:
  2670. r.res = if n.intVal == 0: rope"false" else: rope"true"
  2671. of tyUInt64:
  2672. r.res = rope($cast[BiggestUInt](n.intVal))
  2673. if optJsBigInt64 in p.config.globalOptions:
  2674. r.res.add('n')
  2675. of tyInt64:
  2676. let wrap = n.intVal < 0 # wrap negative integers with parens
  2677. if wrap: r.res.add '('
  2678. r.res.addInt n.intVal
  2679. if optJsBigInt64 in p.config.globalOptions:
  2680. r.res.add('n')
  2681. if wrap: r.res.add ')'
  2682. else:
  2683. let wrap = n.intVal < 0 # wrap negative integers with parens
  2684. if wrap: r.res.add '('
  2685. r.res.addInt n.intVal
  2686. if wrap: r.res.add ')'
  2687. r.kind = resExpr
  2688. of nkNilLit:
  2689. if isEmptyType(n.typ):
  2690. discard
  2691. elif mapType(p, n.typ) == etyBaseIndex:
  2692. r.typ = etyBaseIndex
  2693. r.address = rope"null"
  2694. r.res = rope"0"
  2695. r.kind = resExpr
  2696. else:
  2697. r.res = rope"null"
  2698. r.kind = resExpr
  2699. of nkStrLit..nkTripleStrLit:
  2700. if skipTypes(n.typ, abstractVarRange).kind == tyString:
  2701. if n.strVal.len <= 64:
  2702. r.res = makeJsNimStrLit(n.strVal)
  2703. else:
  2704. useMagic(p, "makeNimstrLit")
  2705. r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
  2706. else:
  2707. r.res = makeJSString(n.strVal, false)
  2708. r.kind = resExpr
  2709. of nkFloatLit..nkFloat64Lit:
  2710. let f = n.floatVal
  2711. case classify(f)
  2712. of fcNan:
  2713. if signbit(f):
  2714. r.res = rope"-NaN"
  2715. else:
  2716. r.res = rope"NaN"
  2717. of fcNegZero:
  2718. r.res = rope"-0.0"
  2719. of fcZero:
  2720. r.res = rope"0.0"
  2721. of fcInf:
  2722. r.res = rope"Infinity"
  2723. of fcNegInf:
  2724. r.res = rope"-Infinity"
  2725. else:
  2726. if n.typ.skipTypes(abstractVarRange).kind == tyFloat32:
  2727. r.res.addFloatRoundtrip(f.float32)
  2728. else:
  2729. r.res.addFloatRoundtrip(f)
  2730. r.kind = resExpr
  2731. of nkCallKinds:
  2732. if isEmptyType(n.typ):
  2733. genLineDir(p, n)
  2734. if (n[0].kind == nkSym) and (n[0].sym.magic != mNone):
  2735. genMagic(p, n, r)
  2736. elif n[0].kind == nkSym and sfInfixCall in n[0].sym.flags and
  2737. n.len >= 1:
  2738. genInfixCall(p, n, r)
  2739. else:
  2740. genCall(p, n, r)
  2741. of nkClosure: gen(p, n[0], r)
  2742. of nkCurly: genSetConstr(p, n, r)
  2743. of nkBracket: genArrayConstr(p, n, r)
  2744. of nkPar, nkTupleConstr: genTupleConstr(p, n, r)
  2745. of nkObjConstr: genObjConstr(p, n, r)
  2746. of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
  2747. of nkAddr, nkHiddenAddr:
  2748. if n.typ.kind in {tyLent}:
  2749. gen(p, n[0], r)
  2750. else:
  2751. genAddr(p, n, r)
  2752. of nkDerefExpr, nkHiddenDeref:
  2753. if n.typ.kind in {tyLent}:
  2754. gen(p, n[0], r)
  2755. else:
  2756. genDeref(p, n, r)
  2757. of nkBracketExpr: genArrayAccess(p, n, r)
  2758. of nkDotExpr: genFieldAccess(p, n, r)
  2759. of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r)
  2760. of nkObjDownConv: gen(p, n[0], r)
  2761. of nkObjUpConv: upConv(p, n, r)
  2762. of nkCast: genCast(p, n, r)
  2763. of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
  2764. of nkChckRange64: genRangeChck(p, n, r, "chckRange64")
  2765. of nkChckRange: genRangeChck(p, n, r, "chckRange")
  2766. of nkStringToCString: convStrToCStr(p, n, r)
  2767. of nkCStringToString: convCStrToStr(p, n, r)
  2768. of nkEmpty: discard
  2769. of nkLambdaKinds:
  2770. let s = n[namePos].sym
  2771. discard mangleName(p.module, s)
  2772. r.res = s.loc.r
  2773. if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure:
  2774. globalError(p.config, n.info, "Closure iterators are not supported by JS backend!")
  2775. elif lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
  2776. elif not p.g.generatedSyms.containsOrIncl(s.id):
  2777. p.locals.add(genProc(p, s))
  2778. of nkType: r.res = genTypeInfo(p, n.typ)
  2779. of nkStmtList, nkStmtListExpr:
  2780. # this shows the distinction is nice for backends and should be kept
  2781. # in the frontend
  2782. let isExpr = not isEmptyType(n.typ)
  2783. for i in 0..<n.len - isExpr.ord:
  2784. genStmt(p, n[i])
  2785. if isExpr:
  2786. gen(p, lastSon(n), r)
  2787. of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
  2788. of nkIfStmt, nkIfExpr: genIf(p, n, r)
  2789. of nkWhen:
  2790. # This is "when nimvm" node
  2791. gen(p, n[1][0], r)
  2792. of nkWhileStmt: genWhileStmt(p, n)
  2793. of nkVarSection, nkLetSection: genVarStmt(p, n)
  2794. of nkConstSection: discard
  2795. of nkForStmt, nkParForStmt:
  2796. internalError(p.config, n.info, "for statement not eliminated")
  2797. of nkCaseStmt: genCaseJS(p, n, r)
  2798. of nkReturnStmt: genReturnStmt(p, n)
  2799. of nkBreakStmt: genBreakStmt(p, n)
  2800. of nkAsgn: genAsgn(p, n)
  2801. of nkFastAsgn, nkSinkAsgn: genFastAsgn(p, n)
  2802. of nkDiscardStmt:
  2803. if n[0].kind != nkEmpty:
  2804. genLineDir(p, n)
  2805. gen(p, n[0], r)
  2806. r.res = "var _ = " & r.res
  2807. of nkAsmStmt:
  2808. warningDeprecated(p.config, n.info, "'asm' for the JS target is deprecated, use the 'emit' pragma")
  2809. genAsmOrEmitStmt(p, n, true)
  2810. of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r)
  2811. of nkRaiseStmt: genRaiseStmt(p, n)
  2812. of nkTypeSection, nkCommentStmt, nkIncludeStmt,
  2813. nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
  2814. nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt,
  2815. nkMixinStmt, nkBindStmt: discard
  2816. of nkIteratorDef:
  2817. if n[0].sym.typ.callConv == TCallingConvention.ccClosure:
  2818. globalError(p.config, n.info, "Closure iterators are not supported by JS backend!")
  2819. of nkPragma: genPragma(p, n)
  2820. of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
  2821. var s = n[namePos].sym
  2822. if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
  2823. genSym(p, n[namePos], r)
  2824. r.res = ""
  2825. of nkGotoState, nkState:
  2826. globalError(p.config, n.info, "First class iterators not implemented")
  2827. of nkPragmaBlock: gen(p, n.lastSon, r)
  2828. of nkComesFrom:
  2829. discard "XXX to implement for better stack traces"
  2830. else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
  2831. proc newModule(g: ModuleGraph; module: PSym): BModule =
  2832. ## Create a new JS backend module node.
  2833. if g.backend == nil:
  2834. g.backend = newGlobals()
  2835. result = BModule(module: module, sigConflicts: initCountTable[SigHash](),
  2836. graph: g, config: g.config
  2837. )
  2838. if sfSystemModule in module.flags:
  2839. PGlobals(g.backend).inSystem = true
  2840. proc genHeader(): Rope =
  2841. ## Generate the JS header.
  2842. result = rope("""/* Generated by the Nim Compiler v$1 */
  2843. var framePtr = null;
  2844. var excHandler = 0;
  2845. var lastJSError = null;
  2846. """.unindent.format(VersionAsString))
  2847. proc addHcrInitGuards(p: PProc, n: PNode,
  2848. moduleLoadedVar: Rope, inInitGuard: var bool) =
  2849. if n.kind == nkStmtList:
  2850. for child in n:
  2851. addHcrInitGuards(p, child, moduleLoadedVar, inInitGuard)
  2852. else:
  2853. let stmtShouldExecute = n.kind in {
  2854. nkProcDef, nkFuncDef, nkMethodDef,nkConverterDef,
  2855. nkVarSection, nkLetSection} or nfExecuteOnReload in n.flags
  2856. if inInitGuard:
  2857. if stmtShouldExecute:
  2858. dec p.extraIndent
  2859. line(p, "}\L")
  2860. inInitGuard = false
  2861. else:
  2862. if not stmtShouldExecute:
  2863. lineF(p, "if ($1 == undefined) {$n", [moduleLoadedVar])
  2864. inc p.extraIndent
  2865. inInitGuard = true
  2866. genStmt(p, n)
  2867. proc genModule(p: PProc, n: PNode) =
  2868. ## Generate the JS module code.
  2869. ## Called for each top level node in a Nim module.
  2870. if optStackTrace in p.options:
  2871. p.body.add(frameCreate(p,
  2872. makeJSString("module " & p.module.module.name.s),
  2873. makeJSString(toFilenameOption(p.config, p.module.module.info.fileIndex, foStacktrace))))
  2874. var transformedN = transformStmt(p.module.graph, p.module.idgen, p.module.module, n)
  2875. if sfInjectDestructors in p.module.module.flags:
  2876. transformedN = injectDestructorCalls(p.module.graph, p.module.idgen, p.module.module, transformedN)
  2877. if p.config.hcrOn and n.kind == nkStmtList:
  2878. let moduleSym = p.module.module
  2879. var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" &
  2880. idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts, p.config)
  2881. lineF(p, "var $1;$n", [moduleLoadedVar])
  2882. var inGuardedBlock = false
  2883. addHcrInitGuards(p, transformedN, moduleLoadedVar, inGuardedBlock)
  2884. if inGuardedBlock:
  2885. dec p.extraIndent
  2886. line(p, "}\L")
  2887. lineF(p, "$1 = true;$n", [moduleLoadedVar])
  2888. else:
  2889. genStmt(p, transformedN)
  2890. if optStackTrace in p.options:
  2891. p.body.add(frameDestroy(p))
  2892. proc processJSCodeGen*(b: PPassContext, n: PNode): PNode =
  2893. ## Generate JS code for a node.
  2894. result = n
  2895. let m = BModule(b)
  2896. if pipelineutils.skipCodegen(m.config, n): return n
  2897. if m.module == nil: internalError(m.config, n.info, "myProcess")
  2898. let globals = PGlobals(m.graph.backend)
  2899. var p = newInitProc(globals, m)
  2900. m.initProc = p
  2901. p.unique = globals.unique
  2902. genModule(p, n)
  2903. p.g.code.add(p.locals)
  2904. p.g.code.add(p.body)
  2905. proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
  2906. ## Combine source code from all nodes.
  2907. let globals = PGlobals(graph.backend)
  2908. for prc in globals.forwarded:
  2909. if not globals.generatedSyms.containsOrIncl(prc.id):
  2910. var p = newInitProc(globals, m)
  2911. attachProc(p, prc)
  2912. generateIfMethodDispatchers(graph, m.idgen)
  2913. for prc in getDispatchers(graph):
  2914. if not globals.generatedSyms.containsOrIncl(prc.id):
  2915. var p = newInitProc(globals, m)
  2916. attachProc(p, prc)
  2917. result = globals.typeInfo & globals.constants & globals.code
  2918. proc getClassName(t: PType): Rope =
  2919. var s = t.sym
  2920. if s.isNil or sfAnon in s.flags:
  2921. s = skipTypes(t, abstractPtrs).sym
  2922. if s.isNil or sfAnon in s.flags:
  2923. doAssert(false, "cannot retrieve class name")
  2924. if s.loc.r != "": result = s.loc.r
  2925. else: result = rope(s.name.s)
  2926. proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
  2927. ## Finalize JS code generation of a Nim module.
  2928. ## Param `n` may contain nodes returned from the last module close call.
  2929. var m = BModule(b)
  2930. if sfMainModule in m.module.flags:
  2931. # Add global destructors to the module.
  2932. # This must come before the last call to `myProcess`.
  2933. for i in countdown(high(graph.globalDestructors), 0):
  2934. n.add graph.globalDestructors[i]
  2935. # Process any nodes left over from the last call to `myClose`.
  2936. result = processJSCodeGen(b, n)
  2937. # Some codegen is different (such as no stacktraces; see `initProcOptions`)
  2938. # when `std/system` is being processed.
  2939. if sfSystemModule in m.module.flags:
  2940. PGlobals(graph.backend).inSystem = false
  2941. # Check if codegen should continue before any files are generated.
  2942. # It may bail early is if too many errors have been raised.
  2943. if pipelineutils.skipCodegen(m.config, n): return n
  2944. # Nim modules are compiled into a single JS file.
  2945. # If this is the main module, then this is the final call to `myClose`.
  2946. if sfMainModule in m.module.flags:
  2947. var code = genHeader() & wholeCode(graph, m)
  2948. let outFile = m.config.prepareToWriteOutput()
  2949. # Generate an optional source map.
  2950. if optSourcemap in m.config.globalOptions:
  2951. var map: SourceMap
  2952. map = genSourceMap($code, outFile.string)
  2953. code &= "\n//# sourceMappingURL=$#.map" % [outFile.string]
  2954. writeFile(outFile.string & ".map", $(%map))
  2955. # Check if the generated JS code matches the output file, or else
  2956. # write it to the file.
  2957. if not equalsFile(code, outFile):
  2958. if not writeRope(code, outFile):
  2959. rawMessage(m.config, errCannotOpenFile, outFile.string)
  2960. proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
  2961. result = newModule(graph, s)
  2962. result.idgen = idgen