summary refs log blame commit diff stats
path: root/compiler/jsgen.nim
blob: f01d4d877d675da89db871ca8870cd70d7de2867 (plain) (tree)
1
2
3
4
5
6
7
8
9

 
                            
                                         




                                                   
                                        
                                     
 


















                                                                                
      
                                                                               
                                                                         
                                                                              
                            
 

                
                       
                                 
                
                   
 

                                                 



                                         



                                                                   
                                                   






                                                            
                                                              
                                                     
                                                                  

                 

                                                                               
                                                                

                   
                        
                        

                             
                               

                         

                   

                  
                      


                     
                         

                                                            
                       
                                                                   
                           
 
                                                     
                                      
 
                             
             

                                     
                                         
                      
 
                                   
                 
             


                  
                                          




                             
                                            
 
                                                                
                                        




                     

                          
                                                           

                                         
 



                                                               

                                                                           
                               
 
                                       
                                      
             

                                                                 
                        
         
                           
               
                                                                 
                         
                                                                         

                                                                      



                                                                               
                                                                      
               
                      
                            
                                                                       
                                                             
                                                         
                    

                                  
 



                                                 
                                                 
                  
                   






                                              

                    


                                                   
                                   
                 

                                         

                      

                           

                    
                                        















                                              







                                                           
 
               
 
                                             
                                
                                             
                                   
 
                                       




                                                            

                              



                                                                         
                                                                  

                                          
                                             
                                                        

                                                                             
               
                                            



                 
                                                           
               





                                           
 

                                                     
                    
                                             


                    
                                             












                             
                                                                  
                
                                                 



                                                    
                                             


                    
                                             



                     
                                                                
                
                                                 

    
                                  
                                                 
 
                                                                      
                      
                                                         



                                                                   

                                                         



                                                
                            





                                                                    

                                                                      




                                              


                                               


                                                 



                                                 









                                                  




                                                         
                                                  


                                                              


                                            








                                                                              


                                                         



                                                                
                                                                             
                                                                               





                                                                               

                         
                                                                           
                    
                    

                      
                                   
                  
 
                                                





                        



                                                 
                            




                                                  



                                                                                              
                                                                       
              
                                                                         
       
                                                                    
 





                                                                            
                                            

                  
                                                                          
                    
                      
                          
                  
 
                                                                                
     
                  
                                               
                         
                    

                        
                                               
       
                        
                                      
 
                                                             




                                        
           


                        
                                                                         
                             
                                     
                                                                                                                                          
         
                                                              






                                                                      



                                     



                                  


                                
 

                                     
                             
                                       
                                                
                                                                         
                                                   
                       
                                             
                                                    
                                        
                                                      
                                                                        
 

                                       
                  

                                   
               
                            
                              
                                 
                                
                          
                                                                    
                         
                                                                
                        
                       
                                          
                          
 

                                                            
                            
                                                        
         
                                       


                      
                                                  

                     
                  

             

                                                        
                    






                                                          
                                 

                
      
                  


                            
               


                                                                          
                                                 
                                      
                                            
                                                                   
                             
                 
                      
                   
                                      
                                                          
                                                 

                                                                       

                                                                                                               
                                                        
                                 
                 
                               
                                     
                                            
                                  
                       
                                       
         
                            
                          

                                            
                                             


                                                             
                                    

                                                    
                                         
                       
                             
          




                                                                           
                          
                                    
                                                
                                 
                          
                          

                                                     
                                                  
                                   
 



                                       
                        
                                                    
                                 
                                             

                                                 
                                   
                                            
 
                                                     
     
                        
                  
                         
                                                                           
                                           
                          
                                                          
       
                                                 


                            
                                      
                      
                

                                           
                          
                             
                                     
                                             
                           
                                                   
                         
             
                          
                       
                                                                  
                                                
                                                               
               
                           
                                                   
                               
                          
                                    
              
                                     
                              
                          
                                  
                                                     
                         
 
                                                    
               
                         
                               
                  
                                                                   
                           
                        
                        
                           
                                                                    
                     
                                               
                      
                                                              
                       
 
                                       

                  
                               

                                   
                           
                                 
                        




                                                                   
                                               
                                                          
                                                                        
 
                                           
                  
                                      
                       
                                                              



                                                                                                          
                                          
                                                                    

                                                 




                            
                                      
                      
                        
               
                                    
                    
                              
                                               
                              
         
                  
                                  
                              
                        

                                         
 
                                                 
              
                                          
                                       
                                 
                                             
                                       
                                          





                                            
                                                 


                                                            
                       
 



                                                                             
                                              
 
                                            
                                             

                                                                       
 
                                                            
                    
 









                                                                    


                                                                                


                                                  


                
              

           
               
                                                                 
                                                    
         
                            
                                            
                                                 

                                                      
                          

                                                                                                



                                                                               
       
                                              
 
                                  
                  
                                                                         
 
                                      

                                                        
 
                                  


                      
                            
                                                                       
                               
                                                      
                                      
                                                     
                                                  

                                            
                                                 
                                                                 
 



                                                  
                                                 
 
                                                        
                 

                                                      
                      
                                                                


                                                                    
                                              
       
                                                                             
                         
                                                        
                                  
                                      

                   
 
                                                          
                 
                      

                                                       

                                                 
       
                                                                               
                         
                                                        
                                                     
                  
 





                                                                 
                                       


                                                        
                  
                     
                      


                                                      
                                                                
                   
                                                  
                                                                        
                 
                                                                
                           
                             



                                                                               

                                                                            
                  
                                            



                  
                                                          
                                                     
                                                                            
              

                                                                          
                         
             
                         
                                                                      
                 
                                                          



                                                                

                  
 


                                              
                                      
                                                          
                                                            
                                                                   
 
                                                   
                     

                         
                                                          
               

                              
                                    
                             
                              
                       

                                 

                                   

                         
                       
                                                                             

                                                                    

                            
                         
           


                                                                               
                                             
                        
                                
               
                                         


                                     

                                                       


                                 
                                                                                     






                                                                                            


                                                                    
 

























                                                      






                                                
                                
 
                                                  
               
             
                                                       
                      
                                                                        











                                                   
           
                       
         
                           

                                    
             
                     
                      
                                                                        



                            
                                  
                                   
                                   
                   
                                                     
                                                
             

                                                          
             

                              

                                 

                      
                                                                        
                   
                 

                                                    
                                            
                        

                   
                        


                                           

                                                      

                                       
 
                                                         

                 
                           


                         
       
                     
 












                                                                           
                                                            
                 
                     




                                                  
                                          
                      
                              

                                         
                               

                                                  
                                



                                     
                  
                 
                  
 




































                                                                
 
                                                        







                                                    
                      

                           
                                                            
                                         

                   
                        

                       
                    
                     
 





                                                   
 
                                                   

                                    




                                            
                                 
          
                                   

                                      
                                         
                                    
                           
                                
                  
 


                                                
 
                                                          
                                                                                           
               

                                          
                                                                  
               
                                                                
                                          
                                                                           
           
                                         
                                         
                              
                                                 

                        
                                                                
                                                  
                                                     
 
                                                                                          

                                               
                                       
                                                                      
                 
                                                        

                 











                                               
                                                            

                                      
                                                      
                                    
                         
                                      
                            
                                                 
           
                                                 
            
                                        
                            


                                       
                                                

                                                         
                                
                                                                              
                                                     

                                                         
         
                                   
               
                       

                                           
              
                            
                                           
             













                                                   
              
                      
                                                   
                                                
                                           
                         

                                              
         
                                         
                                                        
                                       
       


                                          

                                              
               
           
                       
                                                   
                                                                       
       
                                   
                
                          
                 
                           
                 
           
                              
                                                                    
                    
                                                                     
                                                 
                                            

                                        
                                                         


                                        
               
                     
                                                        
         
                                                                   


                                      
                     










                                                    
 
                                     
                                                                               

                        
                         
                           
                         


                                 


                                                       



                                                                                     
 
                                    


                      
                                                       

                                                                         
                                              
 
                                                  

                                                         
                                                 
                                       
 
                                                        

                 
                      
                  
                                                               
                                       
       
                                                   
 

                                      
                                                                 
                                  
         
                                            

                                   
                                                                            
                                   
       
                              
 














                                                                 



















                                                                 
                                                   
                           
                                                              
          
                                                    
             

                                          
                       

                               
                    
                                                                              



                                                     
                                                 
                 
                                                                               

                        
                                                            

                        
                                                               
                  
 
                                   


                             
                                                      
                                               
 
                                                    
     
               
                     



                                             
                                         
                            
                         
              
                          
                                                                            
                                                       





                                                                        
                          
                   






                                                                                    
         
                             
                      





                                                                 
                      



















                                                                    
                                                    
                                 
                                     
                                                  
                                                               
                          



                                                                   
                                                

                                                           
              
                                                     
           
                                                              

                                                                  
         
                                                                  
                                             
          




                                                                              
              




                                                                              

                                                                                         






                                                                   
                                                    

                                                                               
                             
                        
                           
                            
                                         
                                                                   






                                                                  
                                                                   



                                                       
                                  


                                           


                                       


                                                 

                    
                                                               

                                                        
     

                          
                            
                  
                                      
                              
                      
                          

                           
                                                              
         
                   

                       
 
                                                          
                 
                              
                  
                                      
                              
                        
                     
                       
 
                                                          
                 
                              
                  
                                      
                              


                                                  

                                                      
 
                                                        
                 
                  
                     
                             
                                      
                                 
                      
                                             
                         
                          
                                                        
                       
                                                              
                                                   
                                             
                                             
 
                                                   


                                                      




                           
                                   
                    
           
                              


                                           

                                                  
                                   

                                                                       

                      
                                

                        
                            
                                                           
                    
 
                                                         

                                                             
                                         
                                
       
                        
                                                           
                          
                                   
                    
 
                                                         

                                                             
                                         
                                
       
                        
                                                           
                               
                                        
                    
 
                                        
                                                             
                          
                               

                         
                    


                                                              

                                                                                                                                    



                                                                                       
 
                                             
                                  
                           
                                                                
                                                 
       
                
                       
                                                           
                                              
       
                       
                                                            

                                                                               
                                  
                                
 
                                               
     
                   
               
                      
                                                    
                                                                  
                



                                         
                                                       
                                           
                                                        
                                        
                                                           
                                      
                                                 


                                                            
                         
                                             

                                                

                                                   


                                  
              
                                                 
 





                                             




















                                                      


                                                                  








                                                                 



                                               

             
           
                   
                            



                                                            
                    

                          
             
                                           
                          
                            
                     
                      
         
                        
                      
                              

                                                              

                                                            
         
                                           
                    
                              
                      

                                    
                      


                                           
                    
                 
                                                                    
                       
                                                                           
                   
                           
         


                                       
                                   
                                       
                                                               




                            





                                                       
                             




                                                        
                     
                   
                               
                                   
                   
                                                           
                                                    
                                  
                                          



                                                                        
                                                     
                           
              
                           

                                                


                                

                                                 
                            
                             
                                                         
                                   







                                      
                                      

                                    

                                                                        

                                                   






                                                                  
                                        
                                                                   
 

                     
                                       
             
                        

                          
 
























                                                                           
 
                                    
                                
                              
                                                         
                                                        
               
                                
                                
 
                                                  
                                    

                    
                                                        

                                                    

                         
 
                                   


                                                        
                        
 
                                        
                              


                                                        
                        
 
                                          
 






















                                                           
                                                
                                    

                          
                                    
                                                       
                           




                                                      
                                                                
                                                                    

                                               
 
                                                          
                                                                           

              
                                    


                                                                 
 
                                                                     
#
#
#           The Nim Compiler
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# This is the JavaScript code generator.
# Soon also a PHP code generator. ;-)

discard """
The JS code generator contains only 2 tricks:

Trick 1
-------
Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``)
which are pairs (array, index). The derefence operation is then 'array[index]'.
Check ``mapType`` for the details.

Trick 2
-------
It is preferable to generate '||' and '&&' if possible since that is more
idiomatic and hence should be friendlier for the JS JIT implementation. However
code like ``foo and (let bar = baz())`` cannot be translated this way. Instead
the expressions need to be transformed into statements. ``isSimpleExpr``
implements the required case distinction.
"""


import
  ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
  nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os,
  times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
  intsets, cgmeth, lowerings

type
  TTarget = enum
    targetJS, targetPHP
  TJSGen = object of TPassContext
    module: PSym
    target: TTarget

  BModule = ref TJSGen
  TJSTypeKind = enum       # necessary JS "types"
    etyNone,                  # no type
    etyNull,                  # null type
    etyProc,                  # proc type
    etyBool,                  # bool type
    etyInt,                   # JavaScript's int
    etyFloat,                 # JavaScript's float
    etyString,                # JavaScript's string
    etyObject,                # JavaScript's reference to an object
    etyBaseIndex              # base + index needed
  TResKind = enum
    resNone,                  # not set
    resExpr,                  # is some complex expression
    resVal                    # is a temporary/value/l-value
  TCompRes = object
    kind: TResKind
    typ: TJSTypeKind
    res: Rope               # result part; index if this is an
                             # (address, index)-tuple
    address: Rope           # address of an (address, index)-tuple

  TBlock = object
    id: int                  # the ID of the label; positive means that it
                             # has been used (i.e. the label should be emitted)
    isLoop: bool             # whether it's a 'block' or 'while'

  TGlobals = object
    typeInfo, code: Rope
    forwarded: seq[PSym]
    generatedSyms: IntSet
    typeInfoGenerated: IntSet
    classes: seq[(PType, Rope)]

  PGlobals = ref TGlobals
  PProc = ref TProc
  TProc = object
    procDef: PNode
    prc: PSym
    locals, body: Rope
    options: TOptions
    module: BModule
    g: PGlobals
    beforeRetNeeded: bool
    target: TTarget # duplicated here for faster dispatching
    unique: int    # for temp identifier generation
    blocks: seq[TBlock]
    up: PProc     # up the call chain; required for closure support
    declaredGlobals: IntSet

template `|`(a, b: expr): expr {.immediate, dirty.} =
  (if p.target == targetJS: a else: b)

proc newGlobals(): PGlobals =
  new(result)
  result.forwarded = @[]
  result.generatedSyms = initIntSet()
  result.typeInfoGenerated = initIntSet()
  result.classes = @[]

proc initCompRes(r: var TCompRes) =
  r.address = nil
  r.res = nil
  r.typ = etyNone
  r.kind = resNone

proc rdLoc(a: TCompRes): Rope {.inline.} =
  result = a.res
  when false:
    if a.typ != etyBaseIndex:
      result = a.res
    else:
      result = "$1[$2]" % [a.address, a.res]

proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
             options: TOptions): PProc =
  result = PProc(
    blocks: @[],
    options: options,
    module: module,
    procDef: procDef,
    g: globals,
    target: module.target)
  if procDef != nil: result.prc = procDef.sons[namePos].sym
  if result.target == targetPHP:
    result.declaredGlobals = initIntSet()

proc declareGlobal(p: PProc; id: int; r: Rope) =
  if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
    p.locals.addf("global $1;$n", [r])

const
  MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
    tySet, tyBigNum, tyVarargs}

proc mapType(typ: PType): TJSTypeKind =
  let t = skipTypes(typ, abstractInst)
  case t.kind
  of tyVar, tyRef, tyPtr:
    if skipTypes(t.lastSon, abstractInst).kind in MappedToObject:
      result = etyObject
    else:
      result = etyBaseIndex
  of tyPointer:
    # treat a tyPointer like a typed pointer to an array of bytes
    result = etyBaseIndex
  of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy:
    result = mapType(t.sons[0])
  of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
  of tyBool: result = etyBool
  of tyFloat..tyFloat128: result = etyFloat
  of tySet: result = etyObject # map a set to a table
  of tyString, tySequence: result = etyInt # little hack to get right semantics
  of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum,
     tyVarargs:
    result = etyObject
  of tyNil: result = etyNull
  of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
     tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
     tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
    result = etyNone
  of tyProc: result = etyProc
  of tyCString: result = etyString

proc mapType(p: PProc; typ: PType): TJSTypeKind =
  if p.target == targetPHP: result = etyObject
  else: result = mapType(typ)

proc mangleName(s: PSym; target: TTarget): Rope =
  result = s.loc.r
  if result == nil:
    if target == targetJS or s.kind == skTemp:
      result = rope(mangle(s.name.s))
    else:
      var x = newStringOfCap(s.name.s.len)
      var i = 0
      while i < s.name.s.len:
        let c = s.name.s[i]
        case c
        of 'A'..'Z':
          if i > 0 and s.name.s[i-1] in {'a'..'z'}:
            x.add '_'
          x.add(chr(c.ord - 'A'.ord + 'a'.ord))
        of 'a'..'z', '_', '0'..'9':
          x.add c
        else:
          x.add("HEX" & toHex(ord(c), 2))
        inc i
      result = rope(x)
    add(result, "_")
    add(result, rope(s.id))
    s.loc.r = result

proc escapeJSString(s: string): string =
  result = newStringOfCap(s.len + s.len shr 2)
  result.add("\"")
  for c in items(s):
    case c
    of '\l': result.add("\\n")
    of '\r': result.add("\\r")
    of '\t': result.add("\\t")
    of '\b': result.add("\\b")
    of '\a': result.add("\\a")
    of '\e': result.add("\\e")
    of '\v': result.add("\\v")
    of '\\': result.add("\\\\")
    of '\'': result.add("\\'")
    of '\"': result.add("\\\"")
    else: add(result, c)
  result.add("\"")

proc makeJSString(s: string, escapeNonAscii = true): Rope =
  if s.isNil:
    result = "null".rope
  elif escapeNonAscii:
    result = strutils.escape(s).rope
  else:
    result = escapeJSString(s).rope

include jstypes

proc gen(p: PProc, n: PNode, r: var TCompRes)
proc genStmt(p: PProc, n: PNode)
proc genProc(oldProc: PProc, prc: PSym): Rope
proc genConstant(p: PProc, c: PSym)

proc useMagic(p: PProc, name: string) =
  if name.len == 0: return
  var s = magicsys.getCompilerProc(name)
  if s != nil:
    internalAssert s.kind in {skProc, skMethod, skConverter}
    if not p.g.generatedSyms.containsOrIncl(s.id):
      let code = genProc(p, s)
      add(p.g.code, code)
  else:
    # we used to exclude the system module from this check, but for DLL
    # generation support this sloppyness leads to hard to detect bugs, so
    # we're picky here for the system module too:
    if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name)
    else: rawMessage(errSystemNeeds, name)

proc isSimpleExpr(p: PProc; n: PNode): bool =
  # calls all the way down --> can stay expression based
  if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar} or
      (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}):
    for c in n:
      if not p.isSimpleExpr(c): return false
    result = true
  elif n.isAtom:
    result = true

proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
  inc(p.unique)
  if p.target == targetJS:
    result = "Tmp$1" % [rope(p.unique)]
    if defineInLocals:
      addf(p.locals, "var $1;$n", [result])
  else:
    result = "$$Tmp$1" % [rope(p.unique)]

proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
  assert r.kind == resNone
  var x, y: TCompRes
  if p.isSimpleExpr(a) and p.isSimpleExpr(b):
    gen(p, a, x)
    gen(p, b, y)
    r.kind = resExpr
    r.res = "($1 && $2)" % [x.rdLoc, y.rdLoc]
  else:
    r.res = p.getTemp
    r.kind = resVal
    # while a and b:
    # -->
    # while true:
    #   aa
    #   if not a: tmp = false
    #   else:
    #     bb
    #     tmp = b
    # tmp
    gen(p, a, x)
    p.body.addf("if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc])
    gen(p, b, y)
    p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc])

proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
  assert r.kind == resNone
  var x, y: TCompRes
  if p.isSimpleExpr(a) and p.isSimpleExpr(b):
    gen(p, a, x)
    gen(p, b, y)
    r.kind = resExpr
    r.res = "($1 || $2)" % [x.rdLoc, y.rdLoc]
  else:
    r.res = p.getTemp
    r.kind = resVal
    gen(p, a, x)
    p.body.addf("if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc])
    gen(p, b, y)
    p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc])

type
  TMagicFrmt = array[0..3, string]
  TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]

const # magic checked op; magic unchecked op; checked op; unchecked op
  jsOps: TMagicOps = [
    ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
    ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
    ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
    ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
    ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
    ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
    ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
    ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
    ["", "", "($1 - $2)", "($1 - $2)"], # SubF64
    ["", "", "($1 * $2)", "($1 * $2)"], # MulF64
    ["", "", "($1 / $2)", "($1 / $2)"], # DivF64
    ["", "", "", ""], # ShrI
    ["", "", "($1 << $2)", "($1 << $2)"], # ShlI
    ["", "", "($1 & $2)", "($1 & $2)"], # BitandI
    ["", "", "($1 | $2)", "($1 | $2)"], # BitorI
    ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
    ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
    ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
    ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
    ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
    ["", "", "", ""], # addU
    ["", "", "", ""], # subU
    ["", "", "", ""], # mulU
    ["", "", "", ""], # divU
    ["", "", "($1 % $2)", "($1 % $2)"], # modU
    ["", "", "($1 == $2)", "($1 == $2)"], # EqI
    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
    ["", "", "($1 < $2)", "($1 < $2)"], # LtI
    ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
    ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
    ["", "", "($1 <= $2)", "($1 <= $2)"], # leU
    ["", "", "($1 < $2)", "($1 < $2)"], # ltU
    ["", "", "($1 <= $2)", "($1 <= $2)"], # leU64
    ["", "", "($1 < $2)", "($1 < $2)"], # ltU64
    ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
    ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
    ["", "", "($1 == $2)", "($1 == $2)"], # EqCh
    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
    ["", "", "($1 < $2)", "($1 < $2)"], # LtCh
    ["", "", "($1 == $2)", "($1 == $2)"], # EqB
    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
    ["", "", "($1 < $2)", "($1 < $2)"], # LtB
    ["", "", "($1 == $2)", "($1 == $2)"], # EqRef
    ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
    ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
    ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
    ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
    ["", "", "($1 != $2)", "($1 != $2)"], # Xor
    ["", "", "($1 == $2)", "($1 == $2)"], # EqProc
    ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
    ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
    ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
    ["", "", "!($1)", "!($1)"], # Not
    ["", "", "+($1)", "+($1)"], # UnaryPlusI
    ["", "", "~($1)", "~($1)"], # BitnotI
    ["", "", "+($1)", "+($1)"], # UnaryPlusF64
    ["", "", "-($1)", "-($1)"], # UnaryMinusF64
    ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
    ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
    ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
    ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
    ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
    ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
    ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
    ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
    ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
    ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
    ["", "", "$1", "$1"],     # ToFloat
    ["", "", "$1", "$1"],     # ToBiggestFloat
    ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
    ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt
    ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
    ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [
      "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")",
      "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr",
                                   "cstrToNimstr(($1)+\"\")",
                                   "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr",
      "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
    ["", "", "$1", "$1"]]

proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
  var x, y: TCompRes
  useMagic(p, magic)
  gen(p, n.sons[1], x)
  gen(p, n.sons[2], y)
  r.res = frmt % [x.rdLoc, y.rdLoc]
  r.kind = resExpr

proc unsignedTrimmerJS(size: BiggestInt): Rope =
  case size
    of 1: rope"& 0xff"
    of 2: rope"& 0xffff"
    of 4: rope">>> 0"
    else: rope""

proc unsignedTrimmerPHP(size: BiggestInt): Rope =
  case size
    of 1: rope"& 0xff"
    of 2: rope"& 0xffff"
    of 4: rope"& 0xffffffff"
    else: rope""

template unsignedTrimmer(size: BiggestInt): Rope =
  size.unsignedTrimmerJS | size.unsignedTrimmerPHP

proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: bool = false) =
  var x, y: TCompRes
  gen(p, n.sons[1], x)
  gen(p, n.sons[2], y)
  let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
  if reassign:
    r.res = "$1 = (($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
  else:
    r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]

proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
  var x, y, z: TCompRes
  useMagic(p, magic)
  gen(p, n.sons[1], x)
  gen(p, n.sons[2], y)
  gen(p, n.sons[3], z)
  r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
  r.kind = resExpr

proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
  useMagic(p, magic)
  gen(p, n.sons[1], r)
  r.res = frmt % [r.rdLoc]
  r.kind = resExpr

proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) =
  var
    x, y: TCompRes
  let i = ord(optOverflowCheck notin p.options)
  useMagic(p, ops[op][i])
  if sonsLen(n) > 2:
    gen(p, n.sons[1], x)
    gen(p, n.sons[2], y)
    r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc]
  else:
    gen(p, n.sons[1], r)
    r.res = ops[op][i + 2] % [r.rdLoc]

proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
  case op
  of mAddU: binaryUintExpr(p, n, r, "+")
  of mSubU: binaryUintExpr(p, n, r, "-")
  of mMulU: binaryUintExpr(p, n, r, "*")
  of mDivU: binaryUintExpr(p, n, r, "/")
  of mShrI:
    var x, y: TCompRes
    gen(p, n.sons[1], x)
    gen(p, n.sons[2], y)
    let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
    if p.target == targetPHP:
      # XXX prevent multi evaluations
      r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc]
    else:
      r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc]
  of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
      mCStrToStr, mStrToStr, mEnumToStr:
    if p.target == targetPHP:
      if op == mEnumToStr:
        var x: TCompRes
        gen(p, n.sons[1], x)
        r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc]
      elif op == mCharToStr:
        var x: TCompRes
        gen(p, n.sons[1], x)
        r.res = "chr($#)" % [x.rdLoc]
      else:
        gen(p, n.sons[1], r)
    else:
      arithAux(p, n, r, op, jsOps)
  else:
    arithAux(p, n, r, op, jsOps)
  r.kind = resExpr

proc genLineDir(p: PProc, n: PNode) =
  let line = toLinenumber(n.info)
  if optLineDir in p.options:
    addf(p.body, "// line $2 \"$1\"$n",
         [rope(toFilename(n.info)), rope(line)])
  if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
      ((p.prc == nil) or sfPure notin p.prc.flags):
    useMagic(p, "endb")
    addf(p.body, "endb($1);$n", [rope(line)])
  elif ({optLineTrace, optStackTrace} * p.options ==
      {optLineTrace, optStackTrace}) and
      ((p.prc == nil) or not (sfPure in p.prc.flags)):
    addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])

proc genWhileStmt(p: PProc, n: PNode) =
  var
    cond: TCompRes
  internalAssert isEmptyType(n.typ)
  genLineDir(p, n)
  inc(p.unique)
  var length = len(p.blocks)
  setLen(p.blocks, length + 1)
  p.blocks[length].id = -p.unique
  p.blocks[length].isLoop = true
  let labl = p.unique.rope
  addf(p.body, "L$1: while (true) {$n" | "while (true) {$n", [labl])
  gen(p, n.sons[0], cond)
  addf(p.body, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n",
       [cond.res, labl])
  genStmt(p, n.sons[1])
  addf(p.body, "}$n" | "}L$#:;$n", [labl])
  setLen(p.blocks, length)

proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
  if src.kind != resNone:
    if dest.kind != resNone:
      p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc])
    else:
      p.body.addf("$1;$n", [src.rdLoc])
    src.kind = resNone
    src.res = nil

proc genTry(p: PProc, n: PNode, r: var TCompRes) =
  # code to generate:
  #
  #  ++excHandler;
  #  try {
  #    stmts;
  #  } catch (EXC) {
  #    var prevJSError = lastJSError; lastJSError = EXC;
  #    --excHandler;
  #    if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
  #      stmts;
  #    } else if (e.typ && e.typ == NTI32342) {
  #      stmts;
  #    } else {
  #      stmts;
  #    }
  #    lastJSError = prevJSError;
  #  } finally {
  #    stmts;
  #  }
  genLineDir(p, n)
  if not isEmptyType(n.typ):
    r.kind = resVal
    r.res = getTemp(p)
  inc(p.unique)
  var i = 1
  var length = sonsLen(n)
  var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
  if catchBranchesExist and p.target == targetJS:
    add(p.body, "++excHandler;" & tnl)
  var safePoint = "Tmp$1" % [rope(p.unique)]
  if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
  addf(p.body, "try {$n", [])
  var a: TCompRes
  gen(p, n.sons[0], a)
  moveInto(p, a, r)
  var generalCatchBranchExists = false
  let dollar = rope(if p.target == targetJS: "" else: "$")
  if p.target == targetJS and catchBranchesExist:
    addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" &
        " lastJSError = EXC;$n --excHandler;$n", [])
  elif p.target == targetPHP:
    addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
  while i < length and n.sons[i].kind == nkExceptBranch:
    let blen = sonsLen(n.sons[i])
    if blen == 1:
      # general except section:
      generalCatchBranchExists = true
      if i > 1: addf(p.body, "else {$n", [])
      gen(p, n.sons[i].sons[0], a)
      moveInto(p, a, r)
      if i > 1: addf(p.body, "}$n", [])
    else:
      var orExpr: Rope = nil
      useMagic(p, "isObj")
      for j in countup(0, blen - 2):
        if n.sons[i].sons[j].kind != nkType:
          internalError(n.info, "genTryStmt")
        if orExpr != nil: add(orExpr, "||")
        addf(orExpr, "isObj($2lastJSError.m_type, $1)",
             [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
      if i > 1: add(p.body, "else ")
      addf(p.body, "if ($3lastJSError && ($2)) {$n",
        [safePoint, orExpr, dollar])
      gen(p, n.sons[i].sons[blen - 1], a)
      moveInto(p, a, r)
      addf(p.body, "}$n", [])
    inc(i)
  if catchBranchesExist:
    if not generalCatchBranchExists:
      useMagic(p, "reraiseException")
      add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
    addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
  if p.target == targetJS:
    add(p.body, "} finally {" & tnl)
  if i < length and n.sons[i].kind == nkFinally:
    genStmt(p, n.sons[i].sons[0])
  if p.target == targetJS:
    add(p.body, "}" & tnl)
  if p.target == targetPHP:
    # we need to repeat the finally block for PHP ...
    if i < length and n.sons[i].kind == nkFinally:
      genStmt(p, n.sons[i].sons[0])

proc genRaiseStmt(p: PProc, n: PNode) =
  genLineDir(p, n)
  if n.sons[0].kind != nkEmpty:
    var a: TCompRes
    gen(p, n.sons[0], a)
    let typ = skipTypes(n.sons[0].typ, abstractPtrs)
    useMagic(p, "raiseException")
    addf(p.body, "raiseException($1, $2);$n",
         [a.rdLoc, makeJSString(typ.sym.name.s)])
  else:
    useMagic(p, "reraiseException")
    add(p.body, "reraiseException();" & tnl)

proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
  var
    cond, stmt: TCompRes
  genLineDir(p, n)
  gen(p, n.sons[0], cond)
  let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
  if stringSwitch and p.target == targetJS:
    useMagic(p, "toJSStr")
    addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc])
  else:
    addf(p.body, "switch ($1) {$n", [cond.rdLoc])
  if not isEmptyType(n.typ):
    r.kind = resVal
    r.res = getTemp(p)
  for i in countup(1, sonsLen(n) - 1):
    let it = n.sons[i]
    case it.kind
    of nkOfBranch:
      for j in countup(0, sonsLen(it) - 2):
        let e = it.sons[j]
        if e.kind == nkRange:
          var v = copyNode(e.sons[0])
          while v.intVal <= e.sons[1].intVal:
            gen(p, v, cond)
            addf(p.body, "case $1: ", [cond.rdLoc])
            inc(v.intVal)
        else:
          if stringSwitch:
            case e.kind
            of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ",
                [makeJSString(e.strVal, false)])
            else: internalError(e.info, "jsgen.genCaseStmt: 2")
          else:
            gen(p, e, cond)
            addf(p.body, "case $1: ", [cond.rdLoc])
      gen(p, lastSon(it), stmt)
      moveInto(p, stmt, r)
      addf(p.body, "$nbreak;$n", [])
    of nkElse:
      addf(p.body, "default: $n", [])
      gen(p, it.sons[0], stmt)
      moveInto(p, stmt, r)
      addf(p.body, "break;$n", [])
    else: internalError(it.info, "jsgen.genCaseStmt")
  addf(p.body, "}$n", [])

proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
  inc(p.unique)
  let idx = len(p.blocks)
  if n.sons[0].kind != nkEmpty:
    # named block?
    if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
    var sym = n.sons[0].sym
    sym.loc.k = locOther
    sym.position = idx+1
  setLen(p.blocks, idx + 1)
  p.blocks[idx].id = - p.unique # negative because it isn't used yet
  let labl = p.unique
  addf(p.body, "L$1: do {$n" | "", [labl.rope])
  gen(p, n.sons[1], r)
  addf(p.body, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
  setLen(p.blocks, idx)

proc genBreakStmt(p: PProc, n: PNode) =
  var idx: int
  genLineDir(p, n)
  if n.sons[0].kind != nkEmpty:
    # named break?
    assert(n.sons[0].kind == nkSym)
    let sym = n.sons[0].sym
    assert(sym.loc.k == locOther)
    idx = sym.position-1
  else:
    # an unnamed 'break' can only break a loop after 'transf' pass:
    idx = len(p.blocks) - 1
    while idx >= 0 and not p.blocks[idx].isLoop: dec idx
    if idx < 0 or not p.blocks[idx].isLoop:
      internalError(n.info, "no loop to break")
  p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
  addf(p.body, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)])

proc genAsmOrEmitStmt(p: PProc, n: PNode) =
  genLineDir(p, n)
  for i in countup(0, sonsLen(n) - 1):
    case n.sons[i].kind
    of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal)
    of nkSym:
      let v = n.sons[i].sym
      if p.target == targetPHP and v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}:
        add(p.body, "$")
      add(p.body, mangleName(v, p.target))
    else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()")

proc genIf(p: PProc, n: PNode, r: var TCompRes) =
  var cond, stmt: TCompRes
  var toClose = 0
  if not isEmptyType(n.typ):
    r.kind = resVal
    r.res = getTemp(p)
  for i in countup(0, sonsLen(n) - 1):
    let it = n.sons[i]
    if sonsLen(it) != 1:
      if i > 0:
        addf(p.body, "else {$n", [])
        inc(toClose)
      gen(p, it.sons[0], cond)
      addf(p.body, "if ($1) {$n", [cond.rdLoc])
      gen(p, it.sons[1], stmt)
    else:
      # else part:
      addf(p.body, "else {$n", [])
      gen(p, it.sons[0], stmt)
    moveInto(p, stmt, r)
    addf(p.body, "}$n", [])
  add(p.body, repeat('}', toClose) & tnl)

proc generateHeader(p: PProc, typ: PType): Rope =
  result = nil
  for i in countup(1, sonsLen(typ.n) - 1):
    assert(typ.n.sons[i].kind == nkSym)
    var param = typ.n.sons[i].sym
    if isCompileTimeOnly(param.typ): continue
    if result != nil: add(result, ", ")
    var name = mangleName(param, p.target)
    if p.target == targetJS:
      add(result, name)
      if mapType(param.typ) == etyBaseIndex:
        add(result, ", ")
        add(result, name)
        add(result, "_Idx")
    elif not (i == 1 and param.name.s == "this"):
      if param.typ.skipTypes({tyGenericInst}).kind == tyVar:
        add(result, "&")
      add(result, "$")
      add(result, name)

const
  nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
    nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString,
    nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
    nkCommand, nkHiddenCallConv, nkCallStrLit}

proc needsNoCopy(p: PProc; y: PNode): bool =
  result = (y.kind in nodeKindsNeedNoCopy) or
      (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) or
      p.target == targetPHP

proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
  var a, b: TCompRes

  if p.target == targetPHP and x.kind == nkBracketExpr and
      x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}:
    var c: TCompRes
    gen(p, x[0], a)
    gen(p, x[1], b)
    gen(p, y, c)
    addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
    return

  let xtyp = mapType(p, x.typ)

  if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
    gen(p, x.sons[0], a)
    let tmp = p.getTemp(false)
    addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc])
    a.res = "$1[0][$1[1]]" % [tmp]
  else:
    gen(p, x, a)

  gen(p, y, b)

  case xtyp
  of etyObject:
    if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
      addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
    else:
      useMagic(p, "nimCopy")
      addf(p.body, "nimCopy($1, $2, $3);$n",
           [a.res, b.res, genTypeInfo(p, y.typ)])
  of etyBaseIndex:
    if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
      if y.kind == nkCall:
        let tmp = p.getTemp(false)
        addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
      else:
        internalError(x.info, "genAsgn")
    else:
      addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
  else:
    addf(p.body, "$1 = $2;$n", [a.res, b.res])

proc genAsgn(p: PProc, n: PNode) =
  genLineDir(p, n)
  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP)

proc genFastAsgn(p: PProc, n: PNode) =
  genLineDir(p, n)
  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true)

proc genSwap(p: PProc, n: PNode) =
  var a, b: TCompRes
  gen(p, n.sons[1], a)
  gen(p, n.sons[2], b)
  var tmp = p.getTemp(false)
  if mapType(p, skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
    let tmp2 = p.getTemp(false)
    if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
      internalError(n.info, "genSwap")
    addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" |
                 "$1 = $2; $2 = $3; $3 = $1;$n", [
                 tmp, a.address, b.address])
    tmp = tmp2
  addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" |
               "$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res])

proc getFieldPosition(f: PNode): int =
  case f.kind
  of nkIntLit..nkUInt64Lit: result = int(f.intVal)
  of nkSym: result = f.sym.position
  else: internalError(f.info, "genFieldPosition")

proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  r.typ = etyBaseIndex
  let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
  gen(p, b.sons[0], a)
  if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
    if p.target == targetJS:
      r.res = makeJSString( "Field" & $getFieldPosition(b.sons[1]) )
    else:
      r.res = getFieldPosition(b.sons[1]).rope
  else:
    if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
    var f = b.sons[1].sym
    if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
    r.res = makeJSString($f.loc.r)
  internalAssert a.typ != etyBaseIndex
  r.address = a.res
  r.kind = resExpr

proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
  r.typ = etyNone
  gen(p, n.sons[0], r)
  let otyp = skipTypes(n.sons[0].typ, abstractVarRange)
  if otyp.kind == tyTuple:
    r.res = ("$1.Field$2" | "$1[$2]") %
        [r.res, getFieldPosition(n.sons[1]).rope]
  else:
    if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
    var f = n.sons[1].sym
    if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
    r.res = ("$1.$2" | "$1['$2']") % [r.res, f.loc.r]
  r.kind = resExpr

proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
  let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
  internalAssert m.kind == nkCheckedFieldExpr
  genFieldAddr(p, m.sons[0], r) # XXX

proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
  genFieldAccess(p, n.sons[0], r) # XXX

proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
  var
    a, b: TCompRes
    first: BiggestInt
  r.typ = etyBaseIndex
  let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
  gen(p, m.sons[0], a)
  gen(p, m.sons[1], b)
  internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
  r.address = a.res
  var typ = skipTypes(m.sons[0].typ, abstractPtrs)
  if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0])
  else: first = 0
  if optBoundsCheck in p.options and not isConstExpr(m.sons[1]):
    useMagic(p, "chckIndx")
    if p.target == targetPHP:
      if typ.kind != tyString:
        r.res = "chckIndx($1, $2, count($3))-$2" % [b.res, rope(first), a.res]
      else:
        r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
    else:
      r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res]
  elif first != 0:
    r.res = "($1)-$2" % [b.res, rope(first)]
  else:
    r.res = b.res
  r.kind = resExpr

proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
  var ty = skipTypes(n.sons[0].typ, abstractVarRange)
  if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
  case ty.kind
  of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
     tyVarargs:
    genArrayAddr(p, n, r)
  of tyTuple:
    genFieldAddr(p, n, r)
  else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
  r.typ = etyNone
  if r.res == nil: internalError(n.info, "genArrayAccess")
  if p.target == targetPHP and ty.kind in {tyString, tyCString}:
    r.res = "ord($1[$2])" % [r.address, r.res]
  else:
    r.res = "$1[$2]" % [r.address, r.res]
  r.address = nil
  r.kind = resExpr

template isIndirect(x: PSym): bool =
  let v = x
  ({sfAddrTaken, sfGlobal} * v.flags != {} and
    #(mapType(v.typ) != etyObject) and
    {sfImportc, sfVolatile, sfExportc} * v.flags == {} and
    v.kind notin {skProc, skConverter, skMethod, skIterator,
                  skConst, skTemp, skLet} and p.target == targetJS)

proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
  case n.sons[0].kind
  of nkSym:
    let s = n.sons[0].sym
    if s.loc.r == nil: internalError(n.info, "genAddr: 3")
    case s.kind
    of skVar, skLet, skResult:
      r.kind = resExpr
      let jsType = mapType(p, n.typ)
      if jsType == etyObject:
        # make addr() a no-op:
        r.typ = etyNone
        if isIndirect(s):
          r.res = s.loc.r & "[0]"
        elif p.target == targetPHP:
          r.res = "&" & s.loc.r
        else:
          r.res = s.loc.r
        r.address = nil
      elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
        # for ease of code generation, we do not distinguish between
        # sfAddrTaken and sfGlobal.
        r.typ = etyBaseIndex
        r.address = s.loc.r
        r.res = rope("0")
      else:
        # 'var openArray' for instance produces an 'addr' but this is harmless:
        gen(p, n.sons[0], r)
        #internalError(n.info, "genAddr: 4 " & renderTree(n))
    else: internalError(n.info, "genAddr: 2")
  of nkCheckedFieldExpr:
    genCheckedFieldAddr(p, n, r)
  of nkDotExpr:
    if mapType(p, n.typ) == etyBaseIndex:
      genFieldAddr(p, n.sons[0], r)
    else:
      genFieldAccess(p, n.sons[0], r)
  of nkBracketExpr:
    var ty = skipTypes(n.sons[0].typ, abstractVarRange)
    if ty.kind in MappedToObject:
      gen(p, n.sons[0], r)
    else:
      let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind
      case kindOfIndexedExpr
      of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
          tyVarargs:
        genArrayAddr(p, n.sons[0], r)
      of tyTuple:
        genFieldAddr(p, n.sons[0], r)
      else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
  of nkObjDownConv:
    gen(p, n.sons[0], r)
  else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)

proc thisParam(p: PProc; typ: PType): PType =
  if p.target == targetPHP:
    # XXX Might be very useful for the JS backend too?
    let typ = skipTypes(typ, abstractInst)
    assert(typ.kind == tyProc)
    if 1 < sonsLen(typ.n):
      assert(typ.n.sons[1].kind == nkSym)
      let param = typ.n.sons[1].sym
      if param.name.s == "this":
        result = param.typ.skipTypes(abstractVar)

proc attachProc(p: PProc; content: Rope; s: PSym) =
  let otyp = thisParam(p, s.typ)
  if otyp != nil:
    for i, cls in p.g.classes:
      if sameType(cls[0], otyp):
        add(p.g.classes[i][1], content)
        return
    p.g.classes.add((otyp, content))
  else:
    add(p.g.code, content)

proc attachProc(p: PProc; s: PSym) =
  let newp = genProc(p, s)
  attachProc(p, newp, s)

proc genProcForSymIfNeeded(p: PProc, s: PSym) =
  if not p.g.generatedSyms.containsOrIncl(s.id):
    let newp = genProc(p, s)
    var owner = p
    while owner != nil and owner.prc != s.owner:
      owner = owner.up
    if owner != nil: add(owner.locals, newp)
    else: attachProc(p, newp, s)

proc genSym(p: PProc, n: PNode, r: var TCompRes) =
  var s = n.sym
  case s.kind
  of skVar, skLet, skParam, skTemp, skResult, skForVar:
    if s.loc.r == nil:
      internalError(n.info, "symbol has no generated name: " & s.name.s)
    if p.target == targetJS:
      let k = mapType(p, s.typ)
      if k == etyBaseIndex:
        r.typ = etyBaseIndex
        if {sfAddrTaken, sfGlobal} * s.flags != {}:
          r.address = "$1[0]" % [s.loc.r]
          r.res = "$1[1]" % [s.loc.r]
        else:
          r.address = s.loc.r
          r.res = s.loc.r & "_Idx"
      elif isIndirect(s):
        r.res = "$1[0]" % [s.loc.r]
      else:
        r.res = s.loc.r
    else:
      r.res = "$" & s.loc.r
      if sfGlobal in s.flags:
        p.declareGlobal(s.id, r.res)
  of skConst:
    genConstant(p, s)
    if s.loc.r == nil:
      internalError(n.info, "symbol has no generated name: " & s.name.s)
    if p.target == targetJS:
      r.res = s.loc.r
    else:
      r.res = "$" & s.loc.r
      p.declareGlobal(s.id, r.res)
  of skProc, skConverter, skMethod:
    discard mangleName(s, p.target)
    r.res = s.loc.r
    if lfNoDecl in s.loc.flags or s.magic != mNone or
       {sfImportc, sfInfixCall} * s.flags != {}:
      discard
    elif s.kind == skMethod and s.getBody.kind == nkEmpty:
      # we cannot produce code for the dispatcher yet:
      discard
    elif sfForward in s.flags:
      p.g.forwarded.add(s)
    else:
      genProcForSymIfNeeded(p, s)
  else:
    if s.loc.r == nil:
      internalError(n.info, "symbol has no generated name: " & s.name.s)
    r.res = s.loc.r
  r.kind = resVal

proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
  if mapType(p, n.sons[0].typ) == etyObject:
    gen(p, n.sons[0], r)
  else:
    var a: TCompRes
    gen(p, n.sons[0], a)
    if a.typ == etyBaseIndex:
      r.res = "$1[$2]" % [a.address, a.res]
    elif n.sons[0].kind == nkCall:
      let tmp = p.getTemp
      r.res = "($1 = $2, $1[0][$1[1]])" % [tmp, a.res]
    else:
      internalError(n.info, "genDeref")

proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  gen(p, n, a)
  if a.typ == etyBaseIndex:
    add(r.res, a.address)
    add(r.res, ", ")
    add(r.res, a.res)
  else:
    add(r.res, a.res)

proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) =
  var a: TCompRes
  gen(p, n, a)
  if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
      a.typ == etyBaseIndex:
    add(r.res, "$1[$2]" % [a.address, a.res])
  elif a.typ == etyBaseIndex:
    add(r.res, a.address)
    add(r.res, ", ")
    add(r.res, a.res)
  else:
    add(r.res, a.res)

proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
  add(r.res, "(")
  var hasArgs = false

  var typ = skipTypes(n.sons[0].typ, abstractInst)
  assert(typ.kind == tyProc)
  assert(sonsLen(typ) == sonsLen(typ.n))

  for i in countup(start, sonsLen(n) - 1):
    let it = n.sons[i]
    var paramType: PNode = nil
    if i < sonsLen(typ):
      assert(typ.n.sons[i].kind == nkSym)
      paramType = typ.n.sons[i]
      if paramType.typ.isCompileTimeOnly: continue

    if hasArgs: add(r.res, ", ")
    if paramType.isNil:
      genArgNoParam(p, it, r)
    else:
      genArg(p, it, paramType.sym, r)
    hasArgs = true
  add(r.res, ")")
  r.kind = resExpr

proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
                 generated: var int; r: var TCompRes) =
  let it = n[i]
  var paramType: PNode = nil
  if i < sonsLen(typ):
    assert(typ.n.sons[i].kind == nkSym)
    paramType = typ.n.sons[i]
    if paramType.typ.isCompileTimeOnly: return
  if paramType.isNil:
    genArgNoParam(p, it, r)
  else:
    genArg(p, it, paramType.sym, r)

proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
                    r: var TCompRes) =
  var i = 0
  var j = 1
  while i < pat.len:
    case pat[i]
    of '@':
      var generated = 0
      for k in j .. < n.len:
        if generated > 0: add(r.res, ", ")
        genOtherArg(p, n, k, typ, generated, r)
      inc i
    of '#':
      var generated = 0
      genOtherArg(p, n, j, typ, generated, r)
      inc j
      inc i
    else:
      let start = i
      while i < pat.len:
        if pat[i] notin {'@', '#'}: inc(i)
        else: break
      if i - 1 >= start:
        add(r.res, substr(pat, start, i - 1))

proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
  # don't call '$' here for efficiency:
  let pat = n.sons[0].sym.loc.r.data
  internalAssert pat != nil
  if pat.contains({'#', '(', '@'}):
    var typ = skipTypes(n.sons[0].typ, abstractInst)
    assert(typ.kind == tyProc)
    genPatternCall(p, n, pat, typ, r)
    return
  gen(p, n.sons[1], r)
  if r.typ == etyBaseIndex:
    if r.address == nil:
      globalError(n.info, "cannot invoke with infix syntax")
    r.res = "$1[$2]" % [r.address, r.res]
    r.address = nil
    r.typ = etyNone
  add(r.res, "." | "->")
  var op: TCompRes
  gen(p, n.sons[0], op)
  add(r.res, op.res)
  genArgs(p, n, r, 2)

proc genCall(p: PProc, n: PNode, r: var TCompRes) =
  if thisParam(p, n.sons[0].typ) != nil:
    genInfixCall(p, n, r)
    return
  gen(p, n.sons[0], r)
  genArgs(p, n, r)

proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
  let n = n[1].skipConv
  internalAssert n.kind == nkBracket
  if p.target == targetJS:
    useMagic(p, "toJSStr") # Used in rawEcho
    useMagic(p, "rawEcho")
  elif n.len == 0:
    r.kind = resExpr
    add(r.res, """print("\n")""")
    return
  add(r.res, "rawEcho(" | "print(")
  for i in countup(0, sonsLen(n) - 1):
    let it = n.sons[i]
    if it.typ.isCompileTimeOnly: continue
    if i > 0: add(r.res, ", " | ".")
    genArgNoParam(p, it, r)
  add(r.res, ")" | """."\n")""")
  r.kind = resExpr

proc putToSeq(s: string, indirect: bool): Rope =
  result = rope(s)
  if indirect: result = "[$1]" % [result]

proc createVar(p: PProc, typ: PType, indirect: bool): Rope
proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) =
  case rec.kind
  of nkRecList:
    for i in countup(0, sonsLen(rec) - 1):
      createRecordVarAux(p, rec.sons[i], excludedFieldIDs, output)
  of nkRecCase:
    createRecordVarAux(p, rec.sons[0], excludedFieldIDs, output)
    for i in countup(1, sonsLen(rec) - 1):
      createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
  of nkSym:
    if rec.sym.id notin excludedFieldIDs:
      if output.len > 0: output.add(", ")
      if p.target == targetJS:
        output.add(mangleName(rec.sym, p.target))
        output.add(": ")
      else:
        output.addf("'$#' => ", [mangleName(rec.sym, p.target)])
      output.add(createVar(p, rec.sym.typ, false))
  else: internalError(rec.info, "createRecordVarAux")

proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
  var t = typ
  if tfFinal notin t.flags or t.sons[0] != nil:
    if output.len > 0: output.add(", ")
    addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)])
  while t != nil:
    createRecordVarAux(p, t.n, excludedFieldIDs, output)
    t = t.sons[0]

proc arrayTypeForElemType(typ: PType): string =
  case typ.kind
  of tyInt, tyInt32: "Int32Array"
  of tyInt16: "Int16Array"
  of tyInt8: "Int8Array"
  of tyUint, tyUint32: "Uint32Array"
  of tyUint16: "Uint16Array"
  of tyUint8: "Uint8Array"
  of tyFloat32: "Float32Array"
  of tyFloat64, tyFloat: "Float64Array"
  else: nil

proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
  var t = skipTypes(typ, abstractInst)
  case t.kind
  of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
    result = putToSeq("0", indirect)
  of tyFloat..tyFloat128:
    result = putToSeq("0.0", indirect)
  of tyRange, tyGenericInst:
    result = createVar(p, lastSon(typ), indirect)
  of tySet:
    result = putToSeq("{}" | "array()", indirect)
  of tyBool:
    result = putToSeq("false", indirect)
  of tyArray, tyArrayConstr:
    let length = int(lengthOrd(t))
    let e = elemType(t)
    let jsTyp = arrayTypeForElemType(e)
    if not jsTyp.isNil and p.target == targetJS:
      result = "new $1($2)" % [rope(jsTyp), rope(length)]
    elif length > 32:
      useMagic(p, "arrayConstr")
      # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary.
      if p.target == targetJS: useMagic(p, "nimCopy")
      result = "arrayConstr($1, $2, $3)" % [rope(length),
          createVar(p, e, false), genTypeInfo(p, e)]
    else:
      result = rope("[" | "array(")
      var i = 0
      while i < length:
        if i > 0: add(result, ", ")
        add(result, createVar(p, e, false))
        inc(i)
      add(result, "]" | ")")
    if indirect: result = "[$1]" % [result]
  of tyTuple:
    if p.target == targetJS:
      result = rope("{")
      for i in 0.. <t.sonsLen:
        if i > 0: add(result, ", ")
        addf(result, "Field$1: $2", [i.rope,
             createVar(p, t.sons[i], false)])
      add(result, "}")
      if indirect: result = "[$1]" % [result]
    else:
      result = rope("array(")
      for i in 0.. <t.sonsLen:
        if i > 0: add(result, ", ")
        add(result, createVar(p, t.sons[i], false))
      add(result, ")")
  of tyObject:
    var initList: Rope
    createObjInitList(p, t, initIntSet(), initList)
    result = ("{$1}" | "array($#)") % [initList]
    if indirect: result = "[$1]" % [result]
  of tyVar, tyPtr, tyRef:
    if mapType(p, t) == etyBaseIndex:
      result = putToSeq("[null, 0]", indirect)
    else:
      result = putToSeq("null", indirect)
  of tySequence, tyString, tyCString, tyPointer, tyProc:
    result = putToSeq("null", indirect)
  else:
    internalError("createVar: " & $t.kind)
    result = nil

proc genVarInit(p: PProc, v: PSym, n: PNode) =
  var
    a: TCompRes
    s: Rope
  if n.kind == nkEmpty:
    addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n",
         [mangleName(v, p.target), createVar(p, v.typ, isIndirect(v))])
  else:
    discard mangleName(v, p.target)
    gen(p, n, a)
    case mapType(p, v.typ)
    of etyObject:
      if needsNoCopy(p, n):
        s = a.res
      else:
        useMagic(p, "nimCopy")
        s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
    of etyBaseIndex:
      if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
      if {sfAddrTaken, sfGlobal} * v.flags != {}:
        addf(p.body, "var $1 = [$2, $3];$n",
            [v.loc.r, a.address, a.res])
      else:
        addf(p.body, "var $1 = $2; var $1_Idx = $3;$n", [
             v.loc.r, a.address, a.res])
      return
    else:
      s = a.res
    if isIndirect(v):
      addf(p.body, "var $1 = /**/[$2];$n", [v.loc.r, s])
    else:
      addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s])

proc genVarStmt(p: PProc, n: PNode) =
  for i in countup(0, sonsLen(n) - 1):
    var a = n.sons[i]
    if a.kind != nkCommentStmt:
      if a.kind == nkVarTuple:
        let unpacked = lowerTupleUnpacking(a, p.prc)
        genStmt(p, unpacked)
      else:
        assert(a.kind == nkIdentDefs)
        assert(a.sons[0].kind == nkSym)
        var v = a.sons[0].sym
        if lfNoDecl notin v.loc.flags:
          genLineDir(p, a)
          genVarInit(p, v, a.sons[2])

proc genConstant(p: PProc, c: PSym) =
  if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
    let oldBody = p.body
    p.body = nil
    #genLineDir(p, c.ast)
    genVarInit(p, c, c.ast)
    add(p.g.code, p.body)
    p.body = oldBody

proc genNew(p: PProc, n: PNode) =
  var a: TCompRes
  gen(p, n.sons[1], a)
  var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
  if p.target == targetJS:
    addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)])
  else:
    addf(p.body, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)])

proc genNewSeq(p: PProc, n: PNode) =
  var x, y: TCompRes
  gen(p, n.sons[1], x)
  gen(p, n.sons[2], y)
  let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
  addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" |
               "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [
    x.rdLoc, y.rdLoc, createVar(p, t, false)])

proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
  case skipTypes(n.sons[1].typ, abstractVar).kind
  of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r)
  of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)")
  else: internalError(n.info, "genOrd")

proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes

  gen(p, n.sons[1], a)
  r.kind = resExpr
  if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
    r.res.add("[$1].concat(" % [a.res])
  else:
    r.res.add("($1.slice(0,-1)).concat(" % [a.res])

  for i in countup(2, sonsLen(n) - 2):
    gen(p, n.sons[i], a)
    if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
      r.res.add("[$1]," % [a.res])
    else:
      r.res.add("$1.slice(0,-1)," % [a.res])

  gen(p, n.sons[sonsLen(n) - 1], a)
  if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar:
    r.res.add("[$1, 0])" % [a.res])
  else:
    r.res.add("$1)" % [a.res])

proc genConStrStrPHP(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  gen(p, n.sons[1], a)
  r.kind = resExpr
  if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
    r.res.add("chr($1)" % [a.res])
  else:
    r.res.add(a.res)
  for i in countup(2, sonsLen(n) - 1):
    gen(p, n.sons[i], a)
    if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
      r.res.add(".chr($1)" % [a.res])
    else:
      r.res.add(".$1" % [a.res])

proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
  # we map mArray to PHP's array constructor, a mild hack:
  var a, b: TCompRes
  r.kind = resExpr
  r.res = rope("array(")
  let x = skipConv(n[1])
  if x.kind == nkBracket:
    for i in countup(0, x.len - 1):
      let it = x[i]
      if it.kind == nkPar and it.len == 2:
        if i > 0: r.res.add(", ")
        gen(p, it[0], a)
        gen(p, it[1], b)
        r.res.add("$# => $#" % [a.rdLoc, b.rdLoc])
      else:
        localError(it.info, "'toArray' needs tuple constructors")
  else:
    localError(x.info, "'toArray' needs an array literal")
  r.res.add(")")

proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
  if p.target == targetPHP:
    localError(n.info, "'repr' not available for PHP backend")
    return
  let t = skipTypes(n.sons[1].typ, abstractVarRange)
  case t.kind
  of tyInt..tyUInt64:
    unaryExpr(p, n, r, "", "(\"\"+ ($1))")
  of tyEnum, tyOrdinal:
    gen(p, n.sons[1], r)
    useMagic(p, "cstrToNimstr")
    r.kind = resExpr
    r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res]
  else:
    # XXX:
    internalError(n.info, "genRepr: Not implemented")

proc genOf(p: PProc, n: PNode, r: var TCompRes) =
  var x: TCompRes
  let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc})
  gen(p, n.sons[1], x)
  if tfFinal in t.flags:
    r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)]
  else:
    useMagic(p, "isObj")
    r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)]
  r.kind = resExpr

proc genReset(p: PProc, n: PNode) =
  var x: TCompRes
  useMagic(p, "genericReset")
  gen(p, n.sons[1], x)
  addf(p.body, "$1 = genericReset($1, $2);$n", [x.res,
                genTypeInfo(p, n.sons[1].typ)])

proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
  var
    a: TCompRes
    line, filen: Rope
  var op = n.sons[0].sym.magic
  case op
  of mOr: genOr(p, n.sons[1], n.sons[2], r)
  of mAnd: genAnd(p, n.sons[1], n.sons[2], r)
  of mAddI..mStrToStr: arith(p, n, r, op)
  of mRepr: genRepr(p, n, r)
  of mSwap: genSwap(p, n)
  of mUnaryLt:
    # XXX: range checking?
    if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
    else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
  of mAppendStrCh:
    if p.target == targetJS:
      binaryExpr(p, n, r, "addChar",
          "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
    else:
      binaryExpr(p, n, r, "",
          "$1 .= chr($2)")
  of mAppendStrStr:
    if p.target == targetJS:
      if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
          binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
      else:
        binaryExpr(p, n, r, "",
          "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
      # XXX: make a copy of $2, because of Javascript's sucking semantics
    else:
      binaryExpr(p, n, r, "",
          "$1 .= $2;")
  of mAppendSeqElem:
    if p.target == targetJS:
      binaryExpr(p, n, r, "",
          "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }")
    else:
      binaryExpr(p, n, r, "",
          "$1[] = $2")
  of mConStrStr:
    if p.target == targetJS:
      genConStrStr(p, n, r)
    else:
      genConStrStrPHP(p, n, r)
  of mEqStr:
    if p.target == targetJS:
      binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
    else:
      binaryExpr(p, n, r, "", "($1 == $2)")
  of mLeStr:
    if p.target == targetJS:
      binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
    else:
      binaryExpr(p, n, r, "", "($1 <= $2)")
  of mLtStr:
    if p.target == targetJS:
      binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
    else:
      binaryExpr(p, n, r, "", "($1 < $2)")
  of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
  of mEnumToStr: genRepr(p, n, r)
  of mNew, mNewFinalize: genNew(p, n)
  of mSizeOf: r.res = rope(getSize(n.sons[1].typ))
  of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
  of mOrd: genOrd(p, n, r)
  of mLengthStr:
    unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" |
                           "strlen($1)")
  of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)")
  of mLengthSeq, mLengthOpenArray, mLengthArray:
    unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" |
                           "count($1)")
  of mXLenSeq:
    unaryExpr(p, n, r, "", "$1.length" | "count($1)")
  of mHigh:
    if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)" |
                             "(strlen($1)-1)")
    else:
      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)" |
                             "(count($1)-1)")
  of mInc:
    if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
      binaryUintExpr(p, n, r, "+", true)
    else:
      if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
      else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
  of ast.mDec:
    if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
      binaryUintExpr(p, n, r, "-", true)
    else:
      if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
      else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
  of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "")
  of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2" | "")
  of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
  of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
  of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
  of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)")
  of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
  of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
  of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
  of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
  of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]")
  of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)" | "isset($1[$2])")
  of mNewSeq: genNewSeq(p, n)
  of mOf: genOf(p, n, r)
  of mReset: genReset(p, n)
  of mEcho: genEcho(p, n, r)
  of mNLen..mNError, mSlurp, mStaticExec:
    localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
  of mCopyStr:
    binaryExpr(p, n, r, "", "($1.slice($2))" | "substr($1, $2)")
  of mCopyStrLast:
    if p.target == targetJS:
      ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
    else:
      ternaryExpr(p, n, r, "nimSubstr", "nimSubstr($#, $#, $#)")
  of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
  of mNewStringOfCap:
    if p.target == targetJS:
      unaryExpr(p, n, r, "mnewString", "mnewString(0)")
    else:
      unaryExpr(p, n, r, "", "''")
  of mDotDot:
    genProcForSymIfNeeded(p, n.sons[0].sym)
    genCall(p, n, r)
  of mParseBiggestFloat:
    useMagic(p, "nimParseBiggestFloat")
    genCall(p, n, r)
  of mArray:
    if p.target == targetPHP: genToArray(p, n, r)
    else: genCall(p, n, r)
  else:
    genCall(p, n, r)
    #else internalError(e.info, 'genMagic: ' + magicToStr[op]);

proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
  var
    a, b: TCompRes
  useMagic(p, "SetConstr")
  r.res = rope("SetConstr(")
  r.kind = resExpr
  for i in countup(0, sonsLen(n) - 1):
    if i > 0: add(r.res, ", ")
    var it = n.sons[i]
    if it.kind == nkRange:
      gen(p, it.sons[0], a)
      gen(p, it.sons[1], b)
      addf(r.res, "[$1, $2]" | "array($#,$#)", [a.res, b.res])
    else:
      gen(p, it, a)
      add(r.res, a.res)
  add(r.res, ")")

proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  r.res = rope("[" | "array(")
  r.kind = resExpr
  for i in countup(0, sonsLen(n) - 1):
    if i > 0: add(r.res, ", ")
    gen(p, n.sons[i], a)
    add(r.res, a.res)
  add(r.res, "]" | ")")

proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  r.res = rope("{" | "array(")
  r.kind = resExpr
  for i in countup(0, sonsLen(n) - 1):
    if i > 0: add(r.res, ", ")
    var it = n.sons[i]
    if it.kind == nkExprColonExpr: it = it.sons[1]
    gen(p, it, a)
    addf(r.res, "Field$#: $#" | "$2", [i.rope, a.res])
  r.res.add("}" | ")")

proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  r.kind = resExpr
  var initList : Rope
  var fieldIDs = initIntSet()
  for i in countup(1, sonsLen(n) - 1):
    if i > 1: add(initList, ", ")
    var it = n.sons[i]
    internalAssert it.kind == nkExprColonExpr
    gen(p, it.sons[1], a)
    var f = it.sons[0].sym
    if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
    fieldIDs.incl(f.id)
    addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res])
  let t = skipTypes(n.typ, abstractInst + skipPtrs)
  createObjInitList(p, t, fieldIDs, initList)
  r.res = ("{$1}" | "array($#)") % [initList]

proc genConv(p: PProc, n: PNode, r: var TCompRes) =
  var dest = skipTypes(n.typ, abstractVarRange)
  var src = skipTypes(n.sons[1].typ, abstractVarRange)
  gen(p, n.sons[1], r)
  if dest.kind == src.kind:
    # no-op conversion
    return
  case dest.kind:
  of tyBool:
    r.res = "(($1)? 1:0)" % [r.res]
    r.kind = resExpr
  of tyInt:
    r.res = "($1|0)" % [r.res]
  else:
    # TODO: What types must we handle here?
    discard

proc upConv(p: PProc, n: PNode, r: var TCompRes) =
  gen(p, n.sons[0], r)        # XXX

proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
  var a, b: TCompRes
  gen(p, n.sons[0], r)
  if optRangeCheck in p.options:
    gen(p, n.sons[1], a)
    gen(p, n.sons[2], b)
    useMagic(p, "chckRange")
    r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res]
    r.kind = resExpr

proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
  # we do an optimization here as this is likely to slow down
  # much of the code otherwise:
  if n.sons[0].kind == nkCStringToString:
    gen(p, n.sons[0].sons[0], r)
  else:
    gen(p, n.sons[0], r)
    if r.res == nil: internalError(n.info, "convStrToCStr")
    useMagic(p, "toJSStr")
    r.res = "toJSStr($1)" % [r.res]
    r.kind = resExpr

proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
  # we do an optimization here as this is likely to slow down
  # much of the code otherwise:
  if n.sons[0].kind == nkStringToCString:
    gen(p, n.sons[0].sons[0], r)
  else:
    gen(p, n.sons[0], r)
    if r.res == nil: internalError(n.info, "convCStrToStr")
    useMagic(p, "cstrToNimstr")
    r.res = "cstrToNimstr($1)" % [r.res]
    r.kind = resExpr

proc genReturnStmt(p: PProc, n: PNode) =
  if p.procDef == nil: internalError(n.info, "genReturnStmt")
  p.beforeRetNeeded = true
  if n.sons[0].kind != nkEmpty:
    genStmt(p, n.sons[0])
  else:
    genLineDir(p, n)
  addf(p.body, "break BeforeRet;$n" | "goto BeforeRet;$n", [])

proc frameCreate(p: PProc; procname, filename: Rope): Rope =
  result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$nframePtr = F;$n" |
             "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n$$framePtr = &$$F;$n")) % [
            procname, filename]

proc frameDestroy(p: PProc): Rope =
  result = rope(("framePtr = framePtr.prev;" | "$framePtr = $framePtr['prev'];") & tnl)

proc genProcBody(p: PProc, prc: PSym): Rope =
  if optStackTrace in prc.options:
    result = frameCreate(p,
              makeJSString(prc.owner.name.s & '.' & prc.name.s),
              makeJSString(toFilename(prc.info)))
  else:
    result = nil
  if p.beforeRetNeeded:
    addf(result, "BeforeRet: do {$n$1} while (false); $n" |
                 "$# BeforeRet:;$n", [p.body])
  else:
    add(result, p.body)
  if prc.typ.callConv == ccSysCall and p.target == targetJS:
    result = ("try {$n$1} catch (e) {$n" &
      " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
  if optStackTrace in prc.options:
    add(result, frameDestroy(p))

proc genProc(oldProc: PProc, prc: PSym): Rope =
  var
    resultSym: PSym
    a: TCompRes
  #if gVerbosity >= 3:
  #  echo "BEGIN generating code for: " & prc.name.s
  var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
  p.up = oldProc
  var returnStmt: Rope = nil
  var resultAsgn: Rope = nil
  let name = mangleName(prc, p.target)
  let header = generateHeader(p, prc.typ)
  if prc.typ.sons[0] != nil and sfPure notin prc.flags:
    resultSym = prc.ast.sons[resultPos].sym
    resultAsgn = ("var $# = $#;$n" | "$$$# = $#;$n") % [
        mangleName(resultSym, p.target),
        createVar(p, resultSym.typ, isIndirect(resultSym))]
    gen(p, prc.ast.sons[resultPos], a)
    if mapType(p, resultSym.typ) == etyBaseIndex:
      returnStmt = "return [$#, $#];$n" % [a.address, a.res]
    else:
      returnStmt = "return $#;$n" % [a.res]
  genStmt(p, prc.getBody)
  result = "function $#($#) {$n$#$#$#$#}$n" %
            [name, header, p.locals, resultAsgn,
             genProcBody(p, prc), returnStmt]
  #if gVerbosity >= 3:
  #  echo "END   generated code for: " & prc.name.s

proc genStmt(p: PProc, n: PNode) =
  var r: TCompRes
  gen(p, n, r)
  if r.res != nil: addf(p.body, "$#;$n", [r.res])

proc genPragma(p: PProc, n: PNode) =
  for it in n.sons:
    case whichPragma(it)
    of wEmit: genAsmOrEmitStmt(p, it.sons[1])
    else: discard

proc genCast(p: PProc, n: PNode, r: var TCompRes) =
  var dest = skipTypes(n.typ, abstractVarRange)
  var src = skipTypes(n.sons[1].typ, abstractVarRange)
  gen(p, n.sons[1], r)
  if dest.kind == src.kind:
    # no-op conversion
    return
  let toInt = (dest.kind in tyInt .. tyInt32)
  let toUint = (dest.kind in tyUInt .. tyUInt32)
  let fromInt = (src.kind in tyInt .. tyInt32)
  let fromUint = (src.kind in tyUInt .. tyUInt32)

  if toUint and (fromInt or fromUint):
    let trimmer = unsignedTrimmer(dest.size)
    r.res = "($1 $2)" % [r.res, trimmer]
  elif toInt:
    if fromInt:
      let trimmer = unsignedTrimmer(dest.size)
      r.res = "($1 $2)" % [r.res, trimmer]
    elif fromUint:
      if src.size == 4 and dest.size == 4:
        # XXX prevent multi evaluations
        r.res = "($1|0)" % [r.res] |
          "($1>(float)2147483647?(int)$1-4294967296:$1)" % [r.res]
      else:
        let trimmer = unsignedTrimmer(dest.size)
        let minuend = case dest.size
          of 1: "0xfe"
          of 2: "0xfffe"
          of 4: "0xfffffffe"
          else: ""
        r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]

proc gen(p: PProc, n: PNode, r: var TCompRes) =
  r.typ = etyNone
  r.kind = resNone
  #r.address = nil
  r.res = nil
  case n.kind
  of nkSym:
    genSym(p, n, r)
  of nkCharLit..nkUInt32Lit:
    if n.typ.kind == tyBool:
      r.res = if n.intVal == 0: rope"false" else: rope"true"
    else:
      r.res = rope(n.intVal)
    r.kind = resExpr
  of nkNilLit:
    if isEmptyType(n.typ):
      discard
    elif mapType(p, n.typ) == etyBaseIndex:
      r.typ = etyBaseIndex
      r.address = rope"null"
      r.res = rope"0"
      r.kind = resExpr
    else:
      r.res = rope"null"
      r.kind = resExpr
  of nkStrLit..nkTripleStrLit:
    if skipTypes(n.typ, abstractVarRange).kind == tyString and
       p.target == targetJS:
      useMagic(p, "makeNimstrLit")
      r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
    else:
      r.res = makeJSString(n.strVal, false)
    r.kind = resExpr
  of nkFloatLit..nkFloat64Lit:
    let f = n.floatVal
    if f != f: r.res = rope"NaN"
    elif f == 0.0: r.res = rope"0.0"
    elif f == 0.5 * f:
      if f > 0.0: r.res = rope"Infinity"
      else: r.res = rope"-Infinity"
    else: r.res = rope(f.toStrMaxPrecision)
    r.kind = resExpr
  of nkCallKinds:
    if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
      genMagic(p, n, r)
    elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
        n.len >= 2:
      genInfixCall(p, n, r)
    else:
      genCall(p, n, r)
  of nkCurly: genSetConstr(p, n, r)
  of nkBracket: genArrayConstr(p, n, r)
  of nkPar: genTupleConstr(p, n, r)
  of nkObjConstr: genObjConstr(p, n, r)
  of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
  of nkAddr, nkHiddenAddr:
    if p.target == targetJS:
      genAddr(p, n, r)
    else:
      gen(p, n.sons[0], r)
  of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
  of nkBracketExpr: genArrayAccess(p, n, r)
  of nkDotExpr: genFieldAccess(p, n, r)
  of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
  of nkObjDownConv: gen(p, n.sons[0], r)
  of nkObjUpConv: upConv(p, n, r)
  of nkCast: genCast(p, n, r)
  of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
  of nkChckRange64: genRangeChck(p, n, r, "chckRange64")
  of nkChckRange: genRangeChck(p, n, r, "chckRange")
  of nkStringToCString: convStrToCStr(p, n, r)
  of nkCStringToString: convCStrToStr(p, n, r)
  of nkEmpty: discard
  of nkLambdaKinds:
    let s = n.sons[namePos].sym
    discard mangleName(s, p.target)
    r.res = s.loc.r
    if lfNoDecl in s.loc.flags or s.magic != mNone: discard
    elif not p.g.generatedSyms.containsOrIncl(s.id):
      add(p.locals, genProc(p, s))
  of nkType: r.res = genTypeInfo(p, n.typ)
  of nkStmtList, nkStmtListExpr:
    # this shows the distinction is nice for backends and should be kept
    # in the frontend
    let isExpr = not isEmptyType(n.typ)
    for i in countup(0, sonsLen(n) - 1 - isExpr.ord):
      genStmt(p, n.sons[i])
    if isExpr:
      gen(p, lastSon(n), r)
  of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
  of nkIfStmt, nkIfExpr: genIf(p, n, r)
  of nkWhen:
    # This is "when nimvm" node
    gen(p, n.sons[1].sons[0], r)
  of nkWhileStmt: genWhileStmt(p, n)
  of nkVarSection, nkLetSection: genVarStmt(p, n)
  of nkConstSection: discard
  of nkForStmt, nkParForStmt:
    internalError(n.info, "for statement not eliminated")
  of nkCaseStmt: genCaseJS(p, n, r)
  of nkReturnStmt: genReturnStmt(p, n)
  of nkBreakStmt: genBreakStmt(p, n)
  of nkAsgn: genAsgn(p, n)
  of nkFastAsgn: genFastAsgn(p, n)
  of nkDiscardStmt:
    if n.sons[0].kind != nkEmpty:
      genLineDir(p, n)
      gen(p, n.sons[0], r)
  of nkAsmStmt: genAsmOrEmitStmt(p, n)
  of nkTryStmt: genTry(p, n, r)
  of nkRaiseStmt: genRaiseStmt(p, n)
  of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
     nkFromStmt, nkTemplateDef, nkMacroDef: discard
  of nkPragma: genPragma(p, n)
  of nkProcDef, nkMethodDef, nkConverterDef:
    var s = n.sons[namePos].sym
    if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
      genSym(p, n.sons[namePos], r)
      r.res = nil
  of nkGotoState, nkState:
    internalError(n.info, "first class iterators not implemented")
  of nkPragmaBlock: gen(p, n.lastSon, r)
  else: internalError(n.info, "gen: unknown node type: " & $n.kind)

var globals: PGlobals

proc newModule(module: PSym): BModule =
  new(result)
  result.module = module
  if globals == nil:
    globals = newGlobals()

proc genHeader(target: TTarget): Rope =
  if target == targetJS:
    result = (
      "/* Generated by the Nim Compiler v$1 */$n" &
      "/*   (c) 2016 Andreas Rumpf */$n$n" &
      "var framePtr = null;$n" &
      "var excHandler = 0;$n" &
      "var lastJSError = null;$n" &
      "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" &
      "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" &
      "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" &
      "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" &
      "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" &
      "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" &
      "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" &
      "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") %
      [rope(VersionAsString)]
  else:
    result = ("<?php$n" &
              "/* Generated by the Nim Compiler v$1 */$n" &
              "/*   (c) 2016 Andreas Rumpf */$n$n" &
              "$$framePtr = null;$n" &
              "$$excHandler = 0;$n" &
              "$$lastJSError = null;$n") %
             [rope(VersionAsString)]

proc genModule(p: PProc, n: PNode) =
  if optStackTrace in p.options:
    add(p.body, frameCreate(p,
        makeJSString("module " & p.module.module.name.s),
        makeJSString(toFilename(p.module.module.info))))
  genStmt(p, n)
  if optStackTrace in p.options:
    add(p.body, frameDestroy(p))

proc myProcess(b: PPassContext, n: PNode): PNode =
  if passes.skipCodegen(n): return n
  result = n
  var m = BModule(b)
  if m.module == nil: internalError(n.info, "myProcess")
  var p = newProc(globals, m, nil, m.module.options)
  genModule(p, n)
  add(p.g.code, p.locals)
  add(p.g.code, p.body)

proc wholeCode*(m: BModule): Rope =
  for prc in globals.forwarded:
    if not globals.generatedSyms.containsOrIncl(prc.id):
      var p = newProc(globals, m, nil, m.module.options)
      attachProc(p, prc)

  var disp = generateMethodDispatchers()
  for i in 0..sonsLen(disp)-1:
    let prc = disp.sons[i].sym
    if not globals.generatedSyms.containsOrIncl(prc.id):
      var p = newProc(globals, m, nil, m.module.options)
      attachProc(p, prc)

  result = globals.typeInfo & globals.code

proc getClassName(t: PType): Rope =
  var s = t.sym
  if s.isNil or sfAnon in s.flags:
    s = skipTypes(t, abstractPtrs).sym
  if s.isNil or sfAnon in s.flags:
    internalError("cannot retrieve class name")
  result = mangleName(s, targetPHP)

proc genClass(obj: PType; content: Rope; ext: string) =
  let cls = getClassName(obj)
  let t = skipTypes(obj, abstractPtrs)
  let extends = if t.kind == tyObject and t.sons[0] != nil:
      " extends " & getClassName(t.sons[0])
    else: nil
  let result = ("<?php$n" &
            "/* Generated by the Nim Compiler v$# */$n" &
            "/*   (c) 2016 Andreas Rumpf */$n$n" &
            "class $#$# {$n$#$n}$n") %
           [rope(VersionAsString), cls, extends, content]

  let outfile = changeFileExt(completeCFilePath($cls), ext)
  discard writeRopeIfNotEqual(result, outfile)

proc myClose(b: PPassContext, n: PNode): PNode =
  if passes.skipCodegen(n): return n
  result = myProcess(b, n)
  var m = BModule(b)
  if sfMainModule in m.module.flags:
    let ext = if m.target == targetJS: "js" else: "php"
    let code = wholeCode(m)
    let outfile =
      if options.outFile.len > 0:
        if options.outFile.isAbsolute: options.outFile
        else: getCurrentDir() / options.outFile
      else:
        changeFileExt(completeCFilePath(m.module.filename), ext)
    discard writeRopeIfNotEqual(genHeader(m.target) & code, outfile)
    for obj, content in items(globals.classes):
      genClass(obj, content, ext)

proc myOpenCached(s: PSym, rd: PRodReader): PPassContext =
  internalError("symbol files are not possible with the JS code generator")
  result = nil

proc myOpen(s: PSym): PPassContext =
  var r = newModule(s)
  r.target = if gCmd == cmdCompileToPHP: targetPHP else: targetJS
  result = r

const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)