jsgen.nim 93 KB

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