FreeBASIC  0.91.0
ast-node-proc.bas
Go to the documentation of this file.
1 ''
2 '' AST proc body nodes
3 '' l = head node; r = tail node
4 ''
5 '' note: an implicit scope block isn't created, because the
6 '' implicit main() function (inside scope blocks only
7 '' non-decl statements are allowed)
8 ''
9 
10 
11 #include once "fb.bi"
12 #include once "fbint.bi"
13 #include once "list.bi"
14 #include once "lex.bi"
15 #include once "parser.bi"
16 #include once "ir.bi"
17 #include once "rtl.bi"
18 #include once "ast.bi"
19 
21  sym as FBSYMBOL_ ptr '' for symbol
22  initree as ASTNODE ptr '' can't store in sym, or emit will use it
23  has_dtor as integer
24 end type
25 
26 declare function hModLevelIsEmpty( byval p as ASTNODE ptr ) as integer
27 declare sub hLoadProcResult( byval proc as FBSYMBOL ptr )
28 declare function hDeclProcParams( byval proc as FBSYMBOL ptr ) as integer
29 declare sub hCallCtors( byval n as ASTNODE ptr, byval sym as FBSYMBOL ptr )
30 declare sub hCallDtors( byval proc as FBSYMBOL ptr )
31 declare sub hGenStaticInstancesDtors( byval proc as FBSYMBOL ptr )
32 declare sub hGenGlobalInstancesCtor( )
33 
34 ''::::
36 
37  ast.proc.head = NULL
38  ast.proc.tail = NULL
39  ast.proc.curr = NULL
40 
41  ''
42  listInit( @ast.globinst.list, 32, len( FB_GLOBINSTANCE ), LIST_FLAGS_NOCLEAR )
43  ast.globinst.ctorcnt = 0
44  ast.globinst.dtorcnt = 0
45 
46 end sub
47 
48 ''::::
50 
51  ast.globinst.dtorcnt = 0
52  ast.globinst.ctorcnt = 0
53  listEnd( @ast.globinst.list )
54 
55  ''
56  ast.proc.head = NULL
57  ast.proc.tail = NULL
58  ast.proc.curr = NULL
59 
60 end sub
61 
62 function hNewProcNode( ) as ASTNODE ptr
63  dim as ASTNODE ptr n = any
64 
65  n = astNewNode( AST_NODECLASS_PROC, FB_DATATYPE_INVALID, NULL )
66 
67  '' add to list
68  if( ast.proc.tail <> NULL ) then
69  ast.proc.tail->next = n
70  else
71  ast.proc.head = n
72  end if
73 
74  n->prev = ast.proc.tail
75  n->next = NULL
76  ast.proc.tail = n
77 
78  function = n
79 end function
80 
81 sub hDelProcNode( byval n as ASTNODE ptr )
82  n->l = NULL
83  n->r = NULL
84 
85  '' remove from list
86  if( n->prev <> NULL ) then
87  n->prev->next = n->next
88  else
89  ast.proc.head = n->next
90  end If
91 
92  if( n->next <> NULL ) then
93  n->next->prev = n->prev
94  else
95  ast.proc.tail = n->prev
96  end If
97 
98  astDelNode( n )
99 end sub
100 
101 ''::::
102 sub hProcFlush _
103  ( _
104  byval p as ASTNODE ptr, _
105  byval doemit as integer _
106  )
107 
108  dim as ASTNODE ptr n = any, nxt = any
109  dim as FBSYMBOL ptr sym = any
110 
111  sym = p->sym
112 
113  if( doemit ) then
114  '' emit the static dtor wrappers
116  end if
117 
118  ''
119  ast.proc.curr = p
120  ast.currblock = p
121  ast.doemit = doemit
122 
123  ''
124  parser.scope = iif( p->block.proc.ismain, FB_MAINSCOPE, FB_MAINSCOPE+1 )
125  parser.currproc = sym
126  parser.currblock = sym
127 
128  ''
129  symbNestBegin( sym, FALSE )
130 
131  '' emit header
132  if( ast.doemit ) then
133  symbSetProcIsEmitted( sym )
134 
135  irEmitPROCBEGIN( sym, p->block.initlabel )
136 
137  '' allocate the non-static local variables on stack
138  astScopeAllocLocals(symbGetProcSymbTbHead(sym))
139  end if
140 
141  '' flush nodes
142  n = p->l
143  do while( n <> NULL )
144  nxt = n->next
145  astLoad( n )
146  astDelNode( n )
147  n = nxt
148  loop
149 
150  '' emit footer
151  if( ast.doemit ) then
152  irEmitPROCEND( sym, p->block.initlabel, p->block.exitlabel )
153 
154  '' Emit static local variables
155  irProcAllocStaticVars( symbGetProcSymbTbHead( sym ) )
156  end if
157 
158  '' del symbols from hash and symbol tb's
159  symbDelSymbolTb( @sym->proc.symtb, FALSE )
160 
161  ''
162  symbNestEnd( FALSE )
163 
164  hDelProcNode( p )
165 
166  ast.doemit = TRUE
167 
168 end sub
169 
170 ''::::
171 sub hProcFlushAll _
172  ( _
173  _
174  )
175 
176  dim as ASTNODE ptr n = any
177  dim as integer doemit = any
178  dim as FBSYMBOL ptr sym = any
179 
180  '' procs should be sorted by include file
181 
182  '' gen the global ctors/dtors, if any (done before emitting main()
183  '' because the ctors/dtors could be private and not called yet)
185 
186  do
187  n = ast.proc.head
188  if( n = NULL ) then
189  exit do
190  end if
191 
192  sym = n->sym
193 
194  '' fully parsed?
195  if( symbGetIsParsed( sym ) ) then
196  doemit = TRUE
197  '' private?
198  if( symbIsPrivate( sym ) ) then
199  '' never called? skip
200  if( symbGetIsAccessed( sym ) = FALSE ) then
201  doemit = FALSE
202 
203  '' module-level?
204  elseif( symbGetIsModLevelProc( sym ) ) then
205  doemit = (hModLevelIsEmpty( n ) = FALSE)
206  end if
207  end if
208 
209  else
210  doemit = FALSE
211  end if
212 
213  hProcFlush( n, doemit )
214  loop
215 
216 end sub
217 
218 function astUpdate( byval n as ASTNODE ptr ) as ASTNODE ptr
219  if( n = NULL ) then
220  return NULL
221  end if
222 
223  '' RTL function and the result was discarded? do not allocate a result
224  if( astIsCALL( n ) ) then
225  if( n->call.isrtl ) then
226  if( astGetFullType( n ) <> FB_DATATYPE_VOID ) then
227  astSetType( n, FB_DATATYPE_VOID, NULL )
228  end if
229  end if
230  end if
231 
232  '' Note: Any updating here that can create temp vars with dtors (such
233  '' as astTypeIniUpdate()) should also be done in astBuildBranch() (for
234  '' the condition expression) and astNewIIF() (for the true/false
235  '' expressions) since those cases require dtor calls for temp vars to
236  '' be in specific locations, not just generically at the end of the
237  '' statement (which is what the astDtorListFlush() below will do).
238 
239  '' Turn TYPEINI trees into real assignments
240  '' Note: This can allocate temporary variables, so it must be done
241  '' while in the proper scope context!
242  n = astTypeIniUpdate( n )
243 
244  '' Bitfield assignment/access updating
245  n = astUpdateBitfields( n )
246 
247  '' Tree optimizations
248  n = astOptimizeTree( n )
249 
250  '' Assignment optimizations
251  n = astOptAssignment( n )
252 
253  '' Update string '+' BOPs to 'fb_StrConcat' CALLs
254  n = astUpdStrConcat( n )
255 
256  '' Destroy temporaries if needed
257  if( ast.flushdtorlist ) then
258  n = astNewLINK( n, astDtorListFlush( ) )
259  end if
260 
261  function = n
262 end function
263 
264 function astAdd( byval n as ASTNODE ptr ) as ASTNODE ptr
265  n = astUpdate( n )
266  if( n = NULL ) then
267  return NULL
268  end if
269 
270  '' Link the tree into the procedure's statement list
271  if( ast.proc.curr->r <> NULL ) then
272  ast.proc.curr->r->next = n
273  else
274  ast.proc.curr->l = n
275  end if
276  n->prev = ast.proc.curr->r
277  n->next = NULL
278  ast.proc.curr->r = n
279 
280  function = n
281 end function
282 
283 #if __FB_DEBUG__
284 function hNodeIsFromCurrentProc( byval n as ASTNODE ptr ) as integer
285  dim as ASTNODE ptr i = any
286 
287  i = ast.proc.curr->l
288  while( i )
289 
290  if( i = n ) then
291  function = TRUE
292  exit while
293  end if
294 
295  i = i->next
296  wend
297 end function
298 #endif
299 
300 function astAddAfter _
301  ( _
302  byval n as ASTNODE ptr, _
303  byval ref as ASTNODE ptr _
304  ) as ASTNODE ptr
305 
306  n = astUpdate( n )
307  if( n = NULL ) then
308  return NULL
309  end if
310 
311  if( ref ) then
312  assert( hNodeIsFromCurrentProc( ref ) )
313 
314  '' Insert behind this reference node in the current procedure
315  n->prev = ref
316  n->next = ref->next
317  if( ref->next ) then
318  if( ref->next->prev ) then
319  ref->next->prev = n
320  end if
321  else
322  assert( ast.proc.curr->r = ref )
323  ast.proc.curr->r = n
324  end if
325  ref->next = n
326  else
327  '' Insert at the top of the current procedure
328  n->prev = NULL
329  n->next = ast.proc.curr->l
330  if( ast.proc.curr->l ) then
331  ast.proc.curr->l->prev = n
332  else
333  assert( ast.proc.curr->r = NULL )
334  ast.proc.curr->r = n
335  end if
336  ast.proc.curr->l = n
337  end if
338 
339  function = n
340 end function
341 
342 ''::::
343 sub astAddUnscoped _
344  ( _
345  byval n as ASTNODE ptr _
346  )
347 
348  dim as ASTNODE ptr last = any
349 
350  if( n = NULL ) then
351  exit sub
352  end if
353 
354  last = ast.proc.curr->block.proc.decl_last
355  if( last = NULL ) then
356  last = ast.proc.curr->l
357  end if
358 
359  ast.flushdtorlist = FALSE
360 
361  if( last = NULL ) then
362  n = astAdd( n )
363  else
364  n = astAddAfter( n, last )
365  end if
366 
367  ast.flushdtorlist = TRUE
368 
369  ast.proc.curr->block.proc.decl_last = n
370 
371 end sub
372 
373 function astFindFirstCode( byval proc as ASTNODE ptr ) as ASTNODE ptr
374  dim as ASTNODE ptr i = any
375 
376  assert( proc->class = AST_NODECLASS_PROC )
377 
378  i = proc->l
379  while( i )
380  '' Skip over nodes that don't represent executable code
381  select case( i->class )
382  case AST_NODECLASS_NOP, AST_NODECLASS_LABEL, _
383  AST_NODECLASS_DECL, AST_NODECLASS_LIT, _
384  AST_NODECLASS_DATASTMT, AST_NODECLASS_DBG _
385 
386  case else
387  exit while
388  end select
389 
390  i = i->next
391  wend
392 
393  function = i
394 end function
395 
396 sub astProcBegin( byval sym as FBSYMBOL ptr, byval ismain as integer )
397  dim as ASTNODE ptr n = any
398 
399  n = hNewProcNode( )
400 
401  symbSymbTbInit( sym->proc.symtb, sym )
402 
404 
405  '' File name where the procedure body was found
406  sym->proc.ext->dbg.incfile = env.inf.incfile
407 
408  ast.proc.curr = n
409  ast.currblock = n
410 
411  parser.scope = iif( ismain, FB_MAINSCOPE, FB_MAINSCOPE+1 )
412  parser.currproc = sym
413  parser.currblock = sym
414 
415  symbNestBegin( sym, FALSE )
416 
417  '' add init and exit labels (see the note in the top,
418  '' procs don't create an implicit scope block)
419  n->block.initlabel = symbAddLabel( NULL )
420  n->block.exitlabel = symbAddLabel( NULL, FB_SYMBOPT_NONE )
421 
422  n->sym = sym
423  n->block.proc.ismain = ismain
424  n->block.parent = NULL
425  n->block.inistmt = parser.stmt.cnt
426  n->block.breaklist.head = NULL
427  n->block.breaklist.tail = NULL
428  n->block.proc.decl_last = NULL
429 
430  irProcBegin( sym )
431 
432  ' Don't allocate anything for a naked function, because they will be allowed
433  ' at ebp-N, which won't exist, no result is needed either
434  if( symbIsNaked( sym ) = FALSE ) then
435  '' alloc parameters
437 
438  '' alloc result local var
439  if( symbGetType( sym ) <> FB_DATATYPE_VOID ) then
441  end if
442  end if
443 
444  '' local error handler
445  with sym->proc.ext->err
446  .lasthnd = NULL
447  .lastmod = NULL
448  .lastfun = NULL
449  end with
450 
451  sym->proc.ext->stmtnum = parser.stmt.cnt
452 
453  '' main()?
454  if( symbGetIsMainProc( sym ) ) then
455  dim as FBSYMBOL ptr argc = any, argv = any
456 
457  assert( symbGetProcParams( sym ) = 2 )
458  argc = symbGetProcHeadParam( sym )
459  argv = symbGetProcTailParam( sym )
460 
461  '' fb_Init( argc, argv )
462  '' (plus some other calls depending on -exx etc.)
463  env.main.initnode = rtlInitApp( _
464  astNewVAR( symbGetParamVar( argc ) ), _
465  astNewVAR( symbGetParamVar( argv ) ) )
466  end if
467 
468  '' Label at beginning of lexical block, used by debug stabs output
469  astAdd( astNewLABEL( n->block.initlabel ) )
470 end sub
471 
472 '':::::
473 function hCheckErrHnd _
474  ( _
475  byval head_node as ASTNODE ptr, _
476  byval sym as FBSYMBOL ptr _
477  ) as ASTNODE ptr
478 
479  '' error check? add to head (must be done only when closing the proc body
480  '' or constructor's field would be initialized and break ctor chaining)
481  if( env.clopt.extraerrchk ) then
482  head_node = astAddAfter( rtlErrorSetModName( sym, _
483  astNewCONSTstr( @env.inf.name ) ), _
484  head_node )
485 
486  head_node = astAddAfter( rtlErrorSetFuncName( sym, _
487  astNewCONSTstr( symbGetName( sym ) ) ), _
488  head_node )
489  end if
490 
491  with sym->proc.ext->err
492  if( .lastfun <> NULL ) then
493  astAdd( rtlErrorSetFuncName( NULL, astNewVAR( .lastfun ) ) )
494  .lastfun = NULL
495  end if
496 
497  if( .lastmod <> NULL ) then
498  astAdd( rtlErrorSetModName( NULL, astNewVAR( .lastmod ) ) )
499  .lastmod = NULL
500  end if
501 
502  if( .lasthnd <> NULL ) then
503  rtlErrorSetHandler( astNewVAR( .lasthnd ), FALSE )
504  .lasthnd = NULL
505  end if
506  end with
507 
508  function = head_node
509 
510 end function
511 
512 function hMaybeCallResultCtor _
513  ( _
514  byval head_node as ASTNODE ptr, _
515  byval sym as FBSYMBOL ptr _
516  ) as ASTNODE ptr
517 
518  dim as FBSYMBOL ptr res = any, defctor = any
519 
520  '' Not returning BYVAL, or BYVAL but not an UDT?
521  if( symbProcReturnsByref( sym ) or _
522  (symbGetType( sym ) <> FB_DATATYPE_STRUCT) ) then
523  return head_node
524  end if
525 
526  '' Add result ctor call to the top of the function,
527  '' a) if FUNCTION= (and/or EXIT FUNCTION) was used,
528  '' b) or if neither FUNCTION= nor RETURN was used,
529  '' but not if RETURN was used, because that already calls the copy
530  '' ctor at every RETURN.
531  ''
532  '' This way the result will be constructed properly,
533  '' even if nothing was explicitly returned.
534 
535  '' only RETURN used?
536  if( (not symbGetProcStatAssignUsed( sym )) and symbGetProcStatReturnUsed( sym ) ) then
537  return head_node
538  end if
539 
540  '' UDT with default ctor? (if there is none, nothing needs to be done)
541  defctor = symbGetCompDefCtor( symbGetSubtype( sym ) )
542  if( defctor = NULL ) then
543  '' No default ctor, but others? Must show an error, because we
544  '' cannot leave the result unconstructed. It would be nicer to
545  '' detect & show this error at the top of the function already,
546  '' but that's not possible because of RETURN which doesn't
547  '' require a defctor...
548  if( symbHasCtor( sym ) ) then
549  errReport( FB_ERRMSG_RESULTHASNODEFCTOR )
550  end if
551  return head_node
552  end if
553 
554  res = symbGetProcResult( sym )
555  if( res = NULL ) then
556  return head_node
557  end if
558 
559  function = astAddAfter( astBuildCtorCall( symbGetSubtype( sym ), _
560  astBuildProcResultVar( sym, res ) ), _
561  head_node )
562 end function
563 
564 '':::::
565 function hCallProfiler _
566  ( _
567  byval head_node as ASTNODE ptr _
568  ) as ASTNODE ptr
569 
570  '' on all ports except dos _mcount() is just a normal call
571  if( env.clopt.profile ) then
572  if( env.clopt.target <> FB_COMPTARGET_DOS ) then
573  head_node = astAddAfter( rtlProfileCall_mcount(), head_node )
574  end if
575  end if
576 
577  function = head_node
578 
579 end function
580 
581 function astProcEnd( byval callrtexit as integer ) as integer
582  static as integer rec_cnt = 0
583  dim as integer res = any, do_flush = any, enable_implicit_code = any
584  dim as FBSYMBOL ptr sym = any
585  dim as ASTNODE ptr n = any
586 
587  n = ast.proc.curr
588  rec_cnt += 1
589  sym = n->sym
590  n->block.endstmt = parser.stmt.cnt
591 
592  '' No implicit code should be added for naked functions -- i.e. no stack
593  '' frame setup, no function result, no error checking, no profiling.
594  '' (Any calls would "trash" the stack)
595  '' No need to worry about any "explicit" code though (any statements),
596  '' including local variables and possibly resulting destructor calls;
597  '' they are "the coders fault", not ours.
598  enable_implicit_code = not symbIsNaked( sym )
599 
600  if( errGetCount( ) = 0 ) then
601  '' Constructor?
602  if( symbIsConstructor( sym ) and enable_implicit_code ) then
603  '' No constructor initialization code yet? (constructor chaining)
604  if( symbGetIsCtorInited( sym ) = FALSE ) then
605  symbSetIsCtorInited( sym )
606  '' Add constructor initialization code
607  hCallCtors( n, sym )
608  end if
609  end if
610 
611  astScopeDestroyVars(symbGetProcSymbTb(sym).tail)
612  end if
613 
614  ''
615  astAdd( astNewLABEL( n->block.exitlabel ) )
616 
617  '' Check for any undefined labels (labels can be forward references)
618  res = (symbCheckLabels(symbGetProcSymbTbHead(parser.currproc)) = 0)
619 
620  if( res ) then
621  '' Destructor?
622  if( symbIsDestructor( sym ) and enable_implicit_code ) then
623  '' Call destructors, behind the exit label, so they'll
624  '' always be called, even with early returns.
625  hCallDtors( sym )
626  end if
627 
628  '' update proc's breaks list, adding calls to destructors when needed
629  if( n->block.breaklist.head <> NULL ) then
630  res = astScopeUpdBreakList( n )
631  end if
632 
633  '' gosub used?
635 
636  dim as ASTNODE ptr head_node = n->l
637 
638  if( enable_implicit_code ) then
639  head_node = hCallProfiler( head_node )
640  head_node = hCheckErrHnd( head_node, sym )
641  end if
642 
643  '' if main(), END 0 must be called because it's not safe to return to crt if
644  '' an ON ERROR module-level handler was called while inside some proc
645  if( callrtexit ) then
646  if( n->block.proc.ismain ) then
647  rtlExitApp( NULL )
648  end if
649  end if
650 
651  if( enable_implicit_code ) then
652  '' if it's a function, load result
653  if( symbGetType( sym ) <> FB_DATATYPE_VOID ) then
654  head_node = hMaybeCallResultCtor( head_node, sym )
656  end if
657  end if
658  end if
659 
660  ''
661  irProcEnd( sym )
662 
663  do_flush = FALSE
664  if( res and (errGetCount( ) = 0) ) then
665  symbSetIsParsed( sym )
666 
667  '' not into a recursion?
668  if( rec_cnt = 1 ) then
669  if( n->block.proc.ismain = FALSE ) then
670  '' not private or inline? flush it..
671  if( symbIsPrivate( sym ) = FALSE ) then
672  do_flush = TRUE
673 
674  '' remove from hash tb only
675  else
676  symbDelSymbolTb( @sym->proc.symtb, TRUE )
677  end if
678 
679  '' main? flush all remaining, it's the latest
680  else
681  do_flush = TRUE
682  end if
683  end if
684 
685  '' errors.. remove everything from hash and symbol tb
686  else
687  symbDelSymbolTb( @sym->proc.symtb, FALSE )
688  end if
689 
690  ''
691  symbNestEnd( FALSE )
692 
693  if( env.clopt.vectorize >= FB_VECTORIZE_NORMAL ) then
694  astProcVectorize( n->l )
695  end if
696 
697  ''
698  if( do_flush ) then
699  if( n->block.proc.ismain = FALSE ) then
700  hProcFlush( n, TRUE )
701  else
702  hProcFlushAll( )
703  end if
704  end if
705 
706  '' back to main (or NULL if main was emitted already)
707  ast.proc.curr = ast.proc.head
708  ast.currblock = ast.proc.head
709 
710  parser.scope = FB_MAINSCOPE
711  parser.currproc = env.main.proc
712  parser.currblock = env.main.proc
713 
714  ''
715  rec_cnt -= 1
716 
717  function = res
718 
719 end function
720 
721 function hDeclProcParams( byval proc as FBSYMBOL ptr ) as integer
722  dim as integer i = any
723  dim as FBSYMBOL ptr p = any
724 
725  function = FALSE
726 
727  '' proc returns an UDT?
728  if( symbGetType( proc ) = FB_DATATYPE_STRUCT ) then
729  '' create an hidden arg if needed
731  end if
732 
733  '' Param vars must be declared in the order they should be allocated
734  '' on stack, i.e. depending on the calling convention
735  '' (see also irProcAllocArg())
736  i = 1
737  p = symbGetProcLastParam( proc )
738  do while( p <> NULL )
739  if( p->param.mode <> FB_PARAMMODE_VARARG ) then
740  p->param.var = symbAddVarForParam( p )
741  if( p->param.var = NULL ) then
742  errReportParam( proc, i, NULL, FB_ERRMSG_DUPDEFINITION )
743  exit function
744  end if
745  end if
746 
747  p = symbGetProcPrevParam( proc, p )
748  i += 1
749  loop
750 
751  function = TRUE
752 end function
753 
754 sub hLoadProcResult( byval proc as FBSYMBOL ptr )
755  dim as FBSYMBOL ptr s = any
756  dim as ASTNODE ptr n = any
757 
758  s = symbGetProcResult( proc )
759 
760  '' if result is a string, a temp descriptor is needed, as the current one (on stack)
761  '' will be trashed when the function returns (also, the string returned will be
762  '' set as temp, so any assignment or when passed as parameter to another proc
763  '' will deallocate this string)
764  if( (symbGetType( proc ) = FB_DATATYPE_STRING) and _
765  (not symbProcReturnsByref( proc )) ) then
766  n = rtlStrAllocTmpResult( astNewVAR( s ) )
767 
768  if( env.clopt.backend = FB_BACKEND_GCC ) then
769  n = astNewLOAD( n, symbGetFullType( proc ), TRUE )
770  end if
771  else
772  '' Use the real type, in case it's BYREF return or a UDT result
773  n = astNewLOAD( astNewVAR( s, 0, symbGetProcRealType( proc ), _
774  symbGetProcRealSubtype( proc ) ), _
775  symbGetProcRealType( proc ), TRUE )
776  end if
777 
778  astAdd( n )
779 end sub
780 
781 function hModLevelIsEmpty( byval p as ASTNODE ptr ) as integer
782  dim as ASTNODE ptr n = any, nxt = any
783 
784  '' an empty module-level proc will have just the
785  '' initial and final labels as nodes and nothing else
786  '' (note: when debugging it will be emitted even if empty)
787 
788  n = p->l
789  if( n = NULL ) then
790  return TRUE
791  end if
792  if( n->class <> AST_NODECLASS_LABEL ) then
793  return FALSE
794  end if
795 
796  n = n->next
797  if( n = NULL ) then
798  return TRUE
799  end if
800  if( n->class <> AST_NODECLASS_LABEL ) then
801  return FALSE
802  end if
803 
804  n = n->next
805  if( n = NULL ) then
806  return TRUE
807  end if
808 
809  return FALSE
810 end function
811 
812 function hCallCtorList _
813  ( _
814  byval is_ctor as integer, _
815  byval this_ as FBSYMBOL ptr, _
816  byval fld as FBSYMBOL ptr _
817  ) as ASTNODE ptr
818 
819  dim as FBSYMBOL ptr cnt = any, label = any, iter = any, subtype = any
820  dim as ASTNODE ptr fldexpr = any, tree = any
821  dim as integer dtype = any
822  dim as longint elements = any
823 
824  '' instance? (this function is also used by the static dtor wrapper)
825  if( fld <> NULL ) then
826  dtype = symbGetType( fld )
827  subtype = symbGetSubtype( fld )
828  elements = symbGetArrayElements( fld )
829  else
830  dtype = symbGetType( this_ )
831  subtype = symbGetSubtype( this_ )
832  elements = symbGetArrayElements( this_ )
833  end if
834 
835  cnt = symbAddTempVar( FB_DATATYPE_INTEGER )
836  label = symbAddLabel( NULL )
837  iter = symbAddTempVar( typeAddrOf( dtype ), subtype )
838 
839  '' Instance?
840  if( fld <> NULL ) then
841  if( is_ctor ) then
842  '' iter = @this.field(0)
843  fldexpr = astBuildInstPtr( this_, fld )
844  else
845  '' iter = @this.field(elements-1)
846  fldexpr = astBuildInstPtr( this_, fld, astNewCONSTi( elements - 1 ) )
847  end if
848  else
849  if( is_ctor ) then
850  '' iter = @symbol(0)
851  fldexpr = astBuildVarField( this_, NULL, 0 )
852  else
853  '' iter = @symbol(0) + (elements - 1)
854  fldexpr = astBuildVarField( this_, NULL, (elements - 1) * symbGetLen( subtype ) )
855  end if
856  end if
857 
858  tree = astBuildVarAssign( iter, astNewADDROF( fldexpr ) )
859 
860  '' for cnt = 0 to symbGetArrayElements( fld )-1
861  tree = astBuildForBegin( tree, cnt, label, 0 )
862 
863  if( is_ctor ) then
864  '' ctor( *iter )
865  tree = astNewLINK( tree, astBuildCtorCall( subtype, astBuildVarDeref( iter ) ) )
866  else
867  '' dtor( *iter )
868  tree = astNewLINK( tree, astBuildDtorCall( subtype, astBuildVarDeref( iter ) ) )
869  end if
870 
871  '' iter += 1
872  tree = astNewLINK( tree, astBuildVarInc( iter, iif( is_ctor, 1, -1 ) ) )
873 
874  '' next
875  tree = astBuildForEnd( tree, cnt, label, astNewCONSTi( elements ) )
876 
877  function = tree
878 end function
879 
880 function hCallFieldCtor _
881  ( _
882  byval this_ as FBSYMBOL ptr, _
883  byval fld as FBSYMBOL ptr _
884  ) as ASTNODE ptr
885 
886  '' Do not initialize?
887  if( symbGetDontInit( fld ) ) then
888  exit function
889  end if
890 
891  '' has a default ctor too?
892  if( symbHasDefCtor( fld ) ) then
893  '' !!!FIXME!!! assuming only static arrays will be allowed in fields
894 
895  '' not an array?
896  if( (symbGetArrayDimensions( fld ) = 0) or _
897  (symbGetArrayElements( fld ) = 1) ) then
898  '' ctor( this.field )
899  function = astBuildCtorCall( symbGetSubtype( fld ), astBuildInstPtr( this_, fld ) )
900  '' array..
901  else
902  function = hCallCtorList( TRUE, this_, fld )
903  end if
904 
905  exit function
906  end if
907 
908  '' bitfield?
909  if( symbGetType( fld ) = FB_DATATYPE_BITFIELD ) then
910  function = astNewASSIGN( astBuildInstPtr( this_, fld ), _
911  astNewCONSTi( 0, FB_DATATYPE_UINT ) )
912  else
913  function = astNewMEM( AST_OP_MEMCLEAR, _
914  astBuildInstPtr( this_, fld ), _
915  astNewCONSTi( symbGetLen( fld ) * symbGetArrayElements( fld ) ) )
916  end if
917 end function
918 
919 function hClearUnionFields _
920  ( _
921  byval this_ as FBSYMBOL ptr, _
922  byval base_fld as FBSYMBOL ptr, _
923  byval pfinalfield as FBSYMBOL ptr ptr _
924  ) as ASTNODE ptr
925 
926  dim as FBSYMBOL ptr fld = any
927  dim as longint bytes = any, lgt = any, base_ofs = any
928 
929  '' merge all union fields
930  fld = base_fld
931  bytes = 0
932  base_ofs = symbGetOfs( base_fld )
933 
934  do
935  lgt = (symbGetLen( fld ) * symbGetArrayElements( fld )) + _
936  (symbGetOfs( fld ) - base_ofs)
937  if( lgt > bytes ) then
938  bytes = lgt
939  end if
940 
941  fld = fld->next
942  if( fld = NULL ) then
943  exit do
944  end if
945  loop while( symbGetIsUnionField( fld ) )
946 
947  *pfinalfield = fld
948 
949  '' clear all them at once
950  function = astNewMEM( AST_OP_MEMCLEAR, _
951  astBuildInstPtr( this_, base_fld ), _
952  astNewCONSTi( bytes ) )
953 end function
954 
955 function hCallFieldCtors _
956  ( _
957  byval parent as FBSYMBOL ptr, _
958  byval proc as FBSYMBOL ptr _
959  ) as ASTNODE ptr
960 
961  dim as FBSYMBOL ptr fld = any, this_ = any
962  dim as ASTNODE ptr tree = NULL
963 
964  this_ = symbGetParamVar( symbGetProcHeadParam( proc ) )
965 
966  '' for each field..
967  fld = symbGetCompSymbTb( parent ).head
968  do while( fld <> NULL )
969 
970  if( symbIsField( fld ) ) then
971  '' super class 'base' field? skip.. ctor must be called from derived class' ctor
972  if( fld <> parent->udt.base ) then
973  '' part of an union?
974  if( symbGetIsUnionField( fld ) ) then
975  tree = astNewLINK( tree, hClearUnionFields( this_, fld, @fld ) )
976  '' skip next
977  continue do
978  else
979  '' not initialized?
980  if( symbGetTypeIniTree( fld ) = NULL ) then
981  tree = astNewLINK( tree, hCallFieldCtor( this_, fld ) )
982  '' flush the tree..
983  else
984  tree = astNewLINK( tree, _
985  astTypeIniFlush( astTypeIniClone( symbGetTypeIniTree( fld ) ), _
986  this_, AST_INIOPT_ISINI ) )
987  end if
988  end if
989  end if
990  end if
991 
992  fld = fld->next
993  loop
994 
995  function = tree
996 end function
997 
998 function hCallBaseCtor _
999  ( _
1000  byval parent as FBSYMBOL ptr, _
1001  byval proc as FBSYMBOL ptr _
1002  ) as ASTNODE ptr
1003 
1004  dim as ASTNODE ptr initree = any
1005  dim as FBSYMBOL ptr base_ = any, this_ = any, subtype = any, defctor = any
1006 
1007  base_ = parent->udt.base
1008 
1009  '' No base UDT? Then there's nothing to do.
1010  if( base_ = NULL ) then
1011  exit function
1012  end if
1013 
1014  this_ = symbGetParamVar( symbGetProcHeadParam( proc ) )
1015 
1016  '' Do we have a BASE() ctorcall/initializer?
1017  initree = proc->proc.ext->base_initree
1018  if( initree ) then
1019  proc->proc.ext->base_initree = NULL
1020  return astTypeIniFlush( initree, this_, AST_INIOPT_ISINI )
1021  end if
1022 
1023  subtype = symbGetSubtype( base_ )
1024  defctor = symbGetCompDefCtor( subtype )
1025 
1026  '' Otherwise, try to call a default ctor, if any
1027  if( defctor ) then
1028  '' Check access here, because (unlike fields) it's not done
1029  '' during the TYPE compound parsing
1030  if( symbCheckAccess( defctor ) = FALSE ) then
1031  errReport( FB_ERRMSG_NOACCESSTOBASEDEFCTOR )
1032  end if
1033  '' No default ctor, but others? Then BASE() should have been used,
1034  '' since a ctor must be called, but we cannot do it automatically.
1035  elseif( symbGetCompCtorHead( subtype ) ) then
1036  errReport( FB_ERRMSG_NOBASEINIT )
1037  end if
1038 
1039  function = hCallFieldCtor( this_, base_ )
1040 end function
1041 
1042 function hInitVptr _
1043  ( _
1044  byval parent as FBSYMBOL ptr, _
1045  byval proc as FBSYMBOL ptr _
1046  ) as ASTNODE ptr
1047 
1048  '' Only if there is a vptr
1049  if( symbGetHasRTTI( parent ) = FALSE ) then
1050  exit function
1051  end if
1052 
1053  '' The vtable must be present/known for this
1054  assert( parent->udt.ext->vtable )
1055 
1056  var this_ = symbGetParamVar( symbGetProcHeadParam( proc ) )
1057 
1058  '' this.vptr = cast( any ptr, (cast(byte ptr, @vtable) + sizeof(void *) * 2) )
1059  '' assuming that everything with a vptr extends fb_Object
1060  function = astNewASSIGN( _
1061  astBuildInstPtr( this_, symbUdtGetFirstField( symb.rtti.fb_object ) ), _
1062  astNewCONV( typeAddrOf( FB_DATATYPE_VOID ), NULL, _
1063  astNewADDROF( astNewVAR( parent->udt.ext->vtable, env.pointersize * 2 ) ) ) )
1064 end function
1065 
1066 sub hCallCtors( byval n as ASTNODE ptr, byval sym as FBSYMBOL ptr )
1067  dim as ASTNODE ptr tree = any
1068  dim as FBSYMBOL ptr parent = any
1069 
1070  parent = symbGetNamespace( sym )
1071 
1072  '' 1st) base ctor
1073  tree = hCallBaseCtor( parent, sym )
1074 
1075  '' 2nd) field ctors
1076  tree = astNewLINK( tree, hCallFieldCtors( parent, sym ) )
1077 
1078  '' 3rd) setup the vtable ptr
1079  tree = astNewLINK( tree, hInitVptr( parent, sym ) )
1080 
1081  '' Find the first statement that is executable code,
1082  '' and insert the constructor calls above it.
1083  n = astFindFirstCode( n )
1084  if( n ) then
1085  n = n->prev
1086  end if
1087  astAddAfter( tree, n )
1088 end sub
1089 
1090 sub hCallFieldDtor _
1091  ( _
1092  byval this_ as FBSYMBOL ptr, _
1093  byval fld as FBSYMBOL ptr _
1094  )
1095 
1096  if( symbGetType( fld ) = FB_DATATYPE_STRING ) then
1097  var fldexpr = astBuildInstPtr( this_, fld )
1098 
1099  '' assuming fields cannot be dynamic arrays
1100 
1101  '' not an array?
1102  if( (symbGetArrayDimensions( fld ) = 0) or _
1103  (symbGetArrayElements( fld ) = 1) ) then
1104  astAdd( rtlStrDelete( fldexpr ) )
1105  else
1106  astAdd( rtlArrayErase( fldexpr, FALSE, FALSE ) )
1107  end if
1108  else
1109  '' UDT field with dtor?
1110  if( symbHasDtor( fld ) ) then
1111  '' not an array?
1112  if( (symbGetArrayDimensions( fld ) = 0) or _
1113  (symbGetArrayElements( fld ) = 1) ) then
1114  '' dtor( this.field )
1115  astAdd( astBuildDtorCall( symbGetSubtype( fld ), astBuildInstPtr( this_, fld ) ) )
1116  else
1117  astAdd( hCallCtorList( FALSE, this_, fld ) )
1118  end if
1119  end if
1120  end if
1121 
1122 end sub
1123 
1124 sub hCallFieldDtors _
1125  ( _
1126  byval parent as FBSYMBOL ptr, _
1127  byval proc as FBSYMBOL ptr _
1128  )
1129 
1130  dim as FBSYMBOL ptr fld = any, this_ = any
1131 
1132  this_ = symbGetParamVar( symbGetProcHeadParam( proc ) )
1133 
1134  '' for each field (in inverse order)..
1135  fld = symbGetCompSymbTb( parent ).tail
1136  do while( fld <> NULL )
1137  '' !!!FIXME!!! assuming only static arrays will be allowed in fields
1138 
1139  if( symbIsField( fld ) ) then
1140  '' super class 'base' field? skip.. dtor must be called from derived class' dtor
1141  if( fld <> parent->udt.base ) Then
1142  hCallFieldDtor( this_, fld )
1143  end if
1144  end if
1145 
1146  fld = fld->prev
1147  loop
1148 
1149 end sub
1150 
1151 sub hCallBaseDtor _
1152  ( _
1153  byval parent as FBSYMBOL ptr, _
1154  byval proc as FBSYMBOL ptr _
1155  )
1156 
1157  dim as FBSYMBOL ptr base_ = any, dtor = any, this_ = any
1158 
1159  base_ = parent->udt.base
1160 
1161  '' No base UDT? Then there's nothing to do.
1162  if( base_ = NULL ) then
1163  exit sub
1164  end if
1165 
1166  '' Call its dtor, if there is any.
1167  ''
1168  '' Note: As in C++, the base class' destructor implementation is called
1169  '' from this derived class destructor, and no vtable lookup is done
1170  '' for this (it would just result in infinite recursion anyways).
1171  ''
1172  '' Just like derived classes are not responsible for initializing their
1173  '' base class, they shouldn't be made responsible for cleaning it up.
1174 
1175  dtor = symbGetCompDtor( symbGetSubtype( base_ ) )
1176  if( dtor = NULL ) then
1177  exit sub
1178  end if
1179 
1180  '' Check access here, because (unlike fields) it's not done
1181  '' during the TYPE compound parsing
1182  if( symbCheckAccess( dtor ) = FALSE ) then
1183  errReport( FB_ERRMSG_NOACCESSTOBASEDTOR )
1184  end if
1185 
1186  '' The only exception is if the base class' destructor is ABSTRACT,
1187  '' then there is no implementation to call.
1188  if( symbIsAbstract( dtor ) ) then
1189  exit sub
1190  end if
1191 
1192  this_ = symbGetParamVar( symbGetProcHeadParam( proc ) )
1193  astAdd( astBuildDtorCall( symbGetSubtype( base_ ), _
1194  astBuildInstPtr( this_, base_ ), _
1195  TRUE ) )
1196 end sub
1197 
1198 sub hCallDtors( byval proc as FBSYMBOL ptr )
1199  dim as FBSYMBOL ptr parent = any
1200 
1201  parent = symbGetNamespace( proc )
1202 
1203  '' 1st) fields dtors
1205 
1206  '' 2nd) base dtor
1208 end sub
1209 
1210 sub hCallStaticCtor _
1211  ( _
1212  byval sym as FBSYMBOL ptr, _
1213  byval initree as ASTNODE ptr _
1214  )
1215 
1216  astAdd( astTypeIniFlush( astTypeIniClone( initree ), sym, AST_INIOPT_ISINI ) )
1218 
1219 end sub
1220 
1221 sub hCallStaticDtor( byval sym as FBSYMBOL ptr )
1222  '' dynamic?
1223  if( symbIsDynamic( sym ) ) then
1224  astAdd( rtlArrayErase( astBuildVarField( sym, NULL, 0 ), TRUE, FALSE ) )
1225  else
1226  '' not an array?
1227  if( (symbGetArrayDimensions( sym ) = 0) or _
1228  (symbGetArrayElements( sym ) = 1) ) then
1229  '' dtor( var )
1230  astAdd( astBuildDtorCall( symbGetSubtype( sym ), astBuildVarField( sym, NULL, 0 ) ) )
1231  else
1232  astAdd( hCallCtorList( FALSE, sym, NULL ) )
1233  end if
1234  end if
1235 end sub
1236 
1237 sub hGenStaticInstancesDtors( byval proc as FBSYMBOL ptr )
1238  dim as TLIST ptr dtorlist = any
1239  dim as FB_DTORWRAPPER ptr wrap = any
1240  dim as ASTNODE ptr n = any
1241 
1242  dtorlist = proc->proc.ext->statdtor
1243 
1244  if( dtorlist = NULL ) then
1245  exit sub
1246  end if
1247 
1248  '' for each node..
1249  wrap = listGetHead( dtorlist )
1250  do while( wrap <> NULL )
1251  astProcBegin( wrap->proc, FALSE )
1252  n = ast.proc.curr
1253 
1254  '' call the dtor
1255  hCallStaticDtor( wrap->sym )
1256 
1257  astProcEnd( FALSE )
1258 
1259  '' must be flushed before the proc that has the static vars, because
1260  '' they will be removed from hash and symbols table right-after that
1261  '' proc is flushed
1262  hProcFlush( n, TRUE )
1263 
1264  wrap = listGetNext( wrap )
1265  loop
1266 
1267  '' destroy list
1268  listEnd( dtorlist )
1269  deallocate( proc->proc.ext->statdtor )
1270  proc->proc.ext->statdtor = NULL
1271 end sub
1272 
1273 '':::::
1274 function astProcAddStaticInstance _
1275  ( _
1276  byval sym as FBSYMBOL ptr _
1277  ) as FBSYMBOL ptr
1278 
1279  dim as TLIST ptr dtorlist = any
1280  dim as FB_DTORWRAPPER ptr wrap = any
1281  dim as FBSYMBOL ptr proc = any
1282 
1283  dtorlist = parser.currproc->proc.ext->statdtor
1284 
1285  '' create a new list
1286  if( dtorlist = NULL ) then
1287  dtorlist = xcallocate( len( TLIST ) )
1288  parser.currproc->proc.ext->statdtor = dtorlist
1289 
1290  listInit( dtorlist, 16, len( FB_DTORWRAPPER ), LIST_FLAGS_NOCLEAR )
1291  end if
1292 
1293  ''
1294  wrap = listNewNode( dtorlist )
1295 
1296  proc = symbAddProc( symbPreAddProc( NULL ), symbUniqueLabel( ), NULL, FB_DATATYPE_VOID, NULL, _
1297  FB_SYMBATTRIB_PRIVATE, FB_FUNCMODE_CDECL, FB_SYMBOPT_DECLARING )
1298 
1299  wrap->proc = proc
1300  wrap->sym = sym
1301 
1302  '' can't be undefined
1303  symbSetCantUndef( sym )
1304 
1305  function = proc
1306 
1307 end function
1308 
1309 '':::::
1311  ( _
1312  byval sym as FBSYMBOL ptr, _
1313  byval initree as ASTNODE ptr, _
1314  byval has_dtor as integer _
1315  )
1316 
1317  dim as FB_GLOBINSTANCE ptr wrap = any
1318 
1319  ''
1320  wrap = listNewNode( @ast.globinst.list )
1321 
1322  wrap->sym = sym
1323  wrap->initree = initree
1324  wrap->has_dtor = has_dtor
1325 
1326  '' can't be undefined
1327  symbSetCantUndef( sym )
1328 
1329  if( initree <> NULL ) then
1330  ast.globinst.ctorcnt += 1
1331  end if
1332 
1333  if( has_dtor ) then
1334  ast.globinst.dtorcnt += 1
1335  end if
1336 
1337 end sub
1338 
1339 sub hGlobCtorBegin( byval is_ctor as integer )
1340  dim as FBSYMBOL ptr proc = any
1341 
1342  '' sub ctorname|dtorname cdecl( ) constructor|destructor
1344  iif( is_ctor, @FB_GLOBCTORNAME, @FB_GLOBDTORNAME ), _
1345  FB_DATATYPE_VOID, NULL, FB_SYMBATTRIB_PRIVATE, _
1346  FB_FUNCMODE_CDECL, FB_SYMBOPT_DECLARING )
1347 
1348  if( is_ctor ) then
1350  else
1352  end if
1353  symbSetIsAccessed( proc )
1354  symbSetIsParsed( proc )
1355 
1356  astProcBegin( proc, FALSE )
1357 end sub
1358 
1360  dim as FB_GLOBINSTANCE ptr inst = any
1361  dim as FBSYMBOL ptr sym = any
1362 
1363  '' any global instance with ctors?
1364  if( ast.globinst.ctorcnt > 0 ) then
1365  '' sub ctor cdecl( ) constructor
1366  hGlobCtorBegin( TRUE )
1367 
1368  '' for each node..
1369  inst = listGetHead( @ast.globinst.list )
1370  while( inst )
1371  '' has ctor?
1372  if( inst->initree <> NULL ) then
1373  hCallStaticCtor( inst->sym, inst->initree )
1374  inst->initree = NULL
1375  end if
1376 
1377  inst = listGetNext( inst )
1378  wend
1379 
1380  '' end sub
1381  astProcEnd( FALSE )
1382  end if
1383 
1384  '' any global instance with dtors?
1385  if( ast.globinst.dtorcnt > 0 ) then
1386  '' sub dtor cdecl( ) destructor
1387  hGlobCtorBegin( FALSE )
1388 
1389  '' for each node (in inverse order)..
1390  inst = listGetTail( @ast.globinst.list )
1391  while( inst )
1392  '' has dtor?
1393  if( inst->has_dtor ) then
1394  '' call dtor
1395  hCallStaticDtor( inst->sym )
1396  end if
1397 
1398  inst = listGetPrev( inst )
1399  wend
1400 
1401  '' end sub
1402  astProcEnd( FALSE )
1403  end if
1404 
1405  '' list will be deleted by astProcListEnd( )
1406 end sub
1407