jsgen.nim 93 KB

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