njvmutil.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. {
  2. Copyright (c) 20011 by Jonas Maebe
  3. JVM version of some node tree helper routines
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit njvmutil;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. node,nbas,
  22. ngenutil,
  23. symtype,symconst,symsym,symdef;
  24. type
  25. tjvmnodeutils = class(tnodeutils)
  26. class function initialize_data_node(p:tnode; force: boolean):tnode; override;
  27. class function finalize_data_node(p:tnode):tnode; override;
  28. class function force_init: boolean; override;
  29. class procedure insertbssdata(sym: tstaticvarsym); override;
  30. class function create_main_procdef(const name: string; potype: tproctypeoption; ps: tprocsym): tdef; override;
  31. class function check_insert_trashing(pd: tprocdef): boolean; override;
  32. class function trashable_sym(p: tsym): boolean; override;
  33. class procedure maybe_trash_variable(var stat: tstatementnode; p: tabstractnormalvarsym; trashn: tnode); override;
  34. class procedure InsertInitFinalTable; override;
  35. class procedure InsertThreadvarTablesTable; override;
  36. class procedure InsertThreadvars; override;
  37. class procedure InsertWideInitsTablesTable; override;
  38. class procedure InsertWideInits; override;
  39. class procedure InsertResourceTablesTable; override;
  40. class procedure InsertResourceInfo(ResourcesUsed : boolean); override;
  41. class procedure InsertResStrTablesTable; override;
  42. class procedure InsertResStrInits; override;
  43. class procedure InsertMemorySizes; override;
  44. strict protected
  45. class procedure add_main_procdef_paras(pd: tdef); override;
  46. end;
  47. implementation
  48. uses
  49. verbose,cutils,globtype,globals,constexp,fmodule,
  50. aasmdata,aasmtai,cpubase,aasmbase,aasmcpu,
  51. symbase,symcpu,symtable,defutil,jvmdef,
  52. ncnv,ncon,ninl,ncal,nld,nmem,
  53. ppu,
  54. pass_1;
  55. class function tjvmnodeutils.initialize_data_node(p:tnode; force: boolean):tnode;
  56. var
  57. normaldim: longint;
  58. temp: ttempcreatenode;
  59. stat: tstatementnode;
  60. def: tdef;
  61. paras: tcallparanode;
  62. proc: string;
  63. begin
  64. result:=nil;
  65. proc:='';
  66. temp:=nil;
  67. if not assigned(p.resultdef) then
  68. typecheckpass(p);
  69. if ((p.resultdef.typ=stringdef) and
  70. not is_shortstring(p.resultdef) and
  71. not is_longstring(p.resultdef)) or
  72. is_dynamic_array(p.resultdef) then
  73. begin
  74. { Always initialise with empty string/array rather than nil. Java
  75. makes a distinction between an empty string/array and a null
  76. string/array, but we don't. We therefore have to pick which one we
  77. use to represent empty strings/arrays. I've chosen empty rather than
  78. null structures, because otherwise it becomes impossible to return
  79. an empty string to Java code (it would return null).
  80. On the consumer side, we do interpret both null and empty as the same
  81. thing, so Java code can pass in null strings/arrays and we'll
  82. interpret them correctly.
  83. }
  84. result:=cinlinenode.create(in_setlength_x,false,
  85. ccallparanode.create(genintconstnode(0),
  86. ccallparanode.create(p,nil)));
  87. end
  88. else if force then
  89. begin
  90. { an explicit call to initialize() }
  91. if p.resultdef.typ=recorddef then
  92. result:=ccallnode.createinternmethod(p,'FPCINITIALIZEREC',nil)
  93. else if p.resultdef.typ=arraydef then
  94. begin
  95. stat:=nil;
  96. { in case it's an open array whose elements are regular arrays, put the
  97. dimension of the regular arrays on the stack (otherwise pass 0) }
  98. normaldim:=0;
  99. def:=tarraydef(p.resultdef).elementdef;
  100. while (def.typ=arraydef) and
  101. not is_dynamic_array(def) do
  102. begin
  103. inc(normaldim);
  104. def:=tarraydef(def).elementdef;
  105. end;
  106. if jvmimplicitpointertype(p.resultdef) then
  107. begin
  108. p:=caddrnode.create(p);
  109. include(p.flags,nf_typedaddr);
  110. end;
  111. paras:=ccallparanode.create(ctypeconvnode.create_explicit(p,
  112. search_system_type('TJOBJECTARRAY').typedef),nil);
  113. paras:=ccallparanode.create(genintconstnode(normaldim),paras);
  114. if is_wide_or_unicode_string(def) then
  115. proc:='fpc_initialize_array_unicodestring'
  116. else if is_ansistring(def) then
  117. proc:='fpc_initialize_array_ansistring'
  118. else if is_dynamic_array(def) then
  119. proc:='fpc_initialize_array_dynarr'
  120. else if is_record(def) then
  121. begin
  122. result:=internalstatements(stat);
  123. temp:=ctempcreatenode.create(def,def.size,tt_persistent,true);
  124. addstatement(stat,temp);
  125. paras:=ccallparanode.create(ctemprefnode.create(temp),paras);
  126. proc:='fpc_initialize_array_record'
  127. end;
  128. if assigned(stat) then
  129. begin
  130. addstatement(stat,ccallnode.createintern(proc,paras));
  131. addstatement(stat,ctempdeletenode.create(temp));
  132. end
  133. else
  134. result:=ccallnode.createintern(proc,paras);
  135. end
  136. else
  137. result:=cassignmentnode.create(p,cnilnode.create);
  138. end
  139. else
  140. begin
  141. p.free;
  142. { records/arrays/... are automatically initialised }
  143. result:=cnothingnode.create;
  144. end;
  145. end;
  146. class function tjvmnodeutils.finalize_data_node(p:tnode):tnode;
  147. begin
  148. // do nothing
  149. p.free;
  150. result:=cnothingnode.create;
  151. end;
  152. class function tjvmnodeutils.force_init: boolean;
  153. begin
  154. { we need an initialisation in case the al_globals list is not empty
  155. (that's where the initialisation for global records etc is added) }
  156. { problem: some bss symbols are only registered while processing the main
  157. program (e.g. constant sets) -> cannot predict whether or not we'll
  158. need it in advance }
  159. result:=true;
  160. end;
  161. class procedure tjvmnodeutils.insertbssdata(sym: tstaticvarsym);
  162. var
  163. enuminitsym,
  164. vs: tstaticvarsym;
  165. block: tblocknode;
  166. stat: tstatementnode;
  167. temp: ttempcreatenode;
  168. initnode: tnode;
  169. eledef: tdef;
  170. ndim: longint;
  171. initnodefinished: boolean;
  172. begin
  173. { handled while generating the unit/program init code, or class
  174. constructor; add something to al_globals to indicate that we need to
  175. insert an init section though }
  176. if current_asmdata.asmlists[al_globals].empty and
  177. jvmimplicitpointertype(sym.vardef) then
  178. current_asmdata.asmlists[al_globals].concat(cai_align.Create(1));
  179. { in case of a threadvar, allocate a separate sym that's a subtype of the
  180. java.lang.ThreadLocal class which will wrap the actual variable value }
  181. if vo_is_thread_var in sym.varoptions then
  182. begin
  183. vs:=cstaticvarsym.create(sym.realname+'$threadvar',sym.varspez,
  184. jvmgetthreadvardef(sym.vardef),
  185. sym.varoptions - [vo_is_thread_var],true);
  186. sym.owner.insert(vs);
  187. { make sure that the new sym does not get allocated (we will allocate
  188. it when encountering the original sym, because only then we know
  189. that it's a threadvar) }
  190. include(vs.symoptions,sp_static);
  191. { switch around the mangled names of sym and vs, since the wrapper
  192. should map to the declared name }
  193. sym.set_mangledbasename(vs.realname);
  194. vs.set_mangledbasename(sym.realname);
  195. { add initialization code for the wrapper }
  196. block:=internalstatements(stat);
  197. if assigned(current_module.tcinitcode) then
  198. addstatement(stat,tnode(current_module.tcinitcode));
  199. current_module.tcinitcode:=block;
  200. { create initialization value if necessary }
  201. initnode:=nil;
  202. initnodefinished:=false;
  203. temp:=nil;
  204. { in case of enum type, initialize with enum(0) if it exists }
  205. if sym.vardef.typ=enumdef then
  206. begin
  207. enuminitsym:=tstaticvarsym(tcpuenumdef(tenumdef(sym.vardef).getbasedef).classdef.symtable.Find('__FPC_ZERO_INITIALIZER'));
  208. if assigned(enuminitsym) then
  209. initnode:=cloadnode.create(enuminitsym,enuminitsym.owner);
  210. end
  211. { normal array -> include dimensions and element type so we can
  212. create a deep copy }
  213. else if (sym.vardef.typ=arraydef) and
  214. not is_dynamic_array(sym.vardef) then
  215. begin
  216. temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true);
  217. addstatement(stat,temp);
  218. initnode:=ccallparanode.create(
  219. ctypeconvnode.create_explicit(
  220. caddrnode.create_internal(ctemprefnode.create(temp)),
  221. java_jlobject),
  222. nil);
  223. jvmgetarraydimdef(sym.vardef,eledef,ndim);
  224. initnode:=ccallparanode.create(genintconstnode(ndim),initnode);
  225. initnode:=ccallparanode.create(
  226. cordconstnode.create(ord(jvmarrtype_setlength(eledef)),
  227. cwidechartype,false),
  228. initnode);
  229. initnodefinished:=true;
  230. end
  231. { implicitpointertype -> allocate (get temp and assign address) }
  232. else if jvmimplicitpointertype(sym.vardef) then
  233. begin
  234. temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true);
  235. addstatement(stat,temp);
  236. initnode:=caddrnode.create_internal(ctemprefnode.create(temp));
  237. end
  238. { unicodestring/ansistring -> empty string }
  239. else if is_wide_or_unicode_string(sym.vardef) or
  240. is_ansistring(sym.vardef) then
  241. begin
  242. temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true);
  243. addstatement(stat,temp);
  244. addstatement(stat,cassignmentnode.create(
  245. ctemprefnode.create(temp),
  246. cstringconstnode.createstr('')));
  247. initnode:=ctemprefnode.create(temp);
  248. end
  249. { dynamic array -> empty array }
  250. else if is_dynamic_array(sym.vardef) then
  251. begin
  252. temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true);
  253. addstatement(stat,temp);
  254. addstatement(stat,cinlinenode.create(in_setlength_x,false,
  255. ccallparanode.create(genintconstnode(0),
  256. ccallparanode.create(ctemprefnode.create(temp),nil))
  257. )
  258. );
  259. initnode:=ctemprefnode.create(temp);
  260. end;
  261. if assigned(initnode) and
  262. not initnodefinished then
  263. initnode:=ccallparanode.create(ctypeconvnode.create_explicit(initnode,java_jlobject),nil);
  264. addstatement(stat,cassignmentnode.create(
  265. cloadnode.create(vs,vs.owner),
  266. ccallnode.createinternmethod(
  267. cloadvmtaddrnode.create(ctypenode.create(vs.vardef)),
  268. 'CREATE',initnode)));
  269. { deallocate the temp if we allocated one }
  270. if assigned(temp) then
  271. addstatement(stat,ctempdeletenode.create(temp));
  272. end;
  273. end;
  274. class function tjvmnodeutils.create_main_procdef(const name: string; potype: tproctypeoption; ps: tprocsym): tdef;
  275. begin
  276. if (potype=potype_proginit) then
  277. begin
  278. result:=inherited create_main_procdef('main', potype, ps);
  279. include(tprocdef(result).procoptions,po_global);
  280. tprocdef(result).visibility:=vis_public;
  281. end
  282. else
  283. result:=inherited create_main_procdef(name, potype, ps);
  284. end;
  285. class function tjvmnodeutils.check_insert_trashing(pd: tprocdef): boolean;
  286. begin
  287. { initialise locals with 0 }
  288. if ts_init_locals in current_settings.targetswitches then
  289. localvartrashing:=high(trashintvalues);
  290. result:=inherited;
  291. end;
  292. class function tjvmnodeutils.trashable_sym(p: tsym): boolean;
  293. begin
  294. result:=
  295. inherited and
  296. not jvmimplicitpointertype(tabstractnormalvarsym(p).vardef);
  297. end;
  298. class procedure tjvmnodeutils.maybe_trash_variable(var stat: tstatementnode; p: tabstractnormalvarsym; trashn: tnode);
  299. var
  300. enumdef: tenumdef;
  301. trashintval: int64;
  302. trashenumval: longint;
  303. trashable: boolean;
  304. begin
  305. trashable:=trashable_sym(p);
  306. trashintval:=trashintvalues[localvartrashing];
  307. { widechar is a separate type in the JVM, can't cast left hand to integer
  308. like in common code }
  309. if trashable and
  310. is_widechar(tabstractvarsym(p).vardef) then
  311. trash_small(stat,trashn,
  312. cordconstnode.create(word(trashintval),tabstractvarsym(p).vardef,false))
  313. { enums are class instances in the JVM -> create a valid instance }
  314. else if trashable and
  315. is_enum(tabstractvarsym(p).vardef) then
  316. begin
  317. enumdef:=tenumdef(tabstractvarsym(p).vardef);
  318. trashenumval:=longint(trashintval);
  319. if not assigned(enumdef.int2enumsym(trashenumval)) then
  320. trashintval:=longint(enumdef.min);
  321. trash_small(stat,trashn,
  322. cordconstnode.create(trashintval,enumdef,false))
  323. end
  324. { can't init pointers with arbitrary values; procvardef and objectdef are
  325. always pointer-sized here because tjvmnodeutils.trashablesym returns
  326. false for jvm implicit pointer types }
  327. else if trashable and
  328. (tabstractvarsym(p).vardef.typ in [pointerdef,classrefdef,objectdef,procvardef]) then
  329. trash_small(stat,trashn,cnilnode.create)
  330. else if trashable and
  331. is_real(tabstractvarsym(p).vardef) then
  332. trash_small(stat,trashn,crealconstnode.create(trashintval,tabstractvarsym(p).vardef))
  333. { don't use inherited routines because it typecasts left to the target
  334. type, and that doesn't always work in the JVM }
  335. else if trashable and
  336. (is_integer(tabstractvarsym(p).vardef) or
  337. is_cbool(tabstractvarsym(p).vardef) or
  338. is_anychar(tabstractvarsym(p).vardef) or
  339. is_currency(tabstractvarsym(p).vardef)) then
  340. trash_small(stat,trashn,cordconstnode.create(trashintval,tabstractvarsym(p).vardef,false))
  341. else if trashable and
  342. is_pasbool(tabstractvarsym(p).vardef) then
  343. trash_small(stat,trashn,cordconstnode.create(trashintval and 1,tabstractvarsym(p).vardef,false))
  344. else
  345. inherited;
  346. end;
  347. class procedure tjvmnodeutils.InsertInitFinalTable;
  348. var
  349. hp : tused_unit;
  350. unitinits : TAsmList;
  351. unitclassname: string;
  352. mainpsym: tsym;
  353. mainpd: tprocdef;
  354. begin
  355. unitinits:=TAsmList.Create;
  356. hp:=tused_unit(usedunits.first);
  357. while assigned(hp) do
  358. begin
  359. { class constructors are automatically handled by the JVM }
  360. { call the unit init code and make it external }
  361. if (hp.u.flags and (uf_init or uf_finalize))<>0 then
  362. begin
  363. { trigger init code by referencing the class representing the
  364. unit; if necessary, it will register the fini code to run on
  365. exit}
  366. unitclassname:='';
  367. if assigned(hp.u.namespace) then
  368. begin
  369. unitclassname:=hp.u.namespace^+'/';
  370. replace(unitclassname,'.','/');
  371. end;
  372. unitclassname:=unitclassname+hp.u.realmodulename^;
  373. unitinits.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(unitclassname,AT_METADATA)));
  374. unitinits.concat(taicpu.op_none(a_pop));
  375. end;
  376. hp:=tused_unit(hp.next);
  377. end;
  378. { insert in main program routine }
  379. mainpsym:=tsym(current_module.localsymtable.find(mainaliasname));
  380. if not assigned(mainpsym) or
  381. (mainpsym.typ<>procsym) then
  382. internalerror(2011041901);
  383. mainpd:=tprocsym(mainpsym).find_procdef_bytype(potype_proginit);
  384. if not assigned(mainpd) then
  385. internalerror(2011041902);
  386. tcpuprocdef(mainpd).exprasmlist.insertList(unitinits);
  387. unitinits.free;
  388. end;
  389. class procedure tjvmnodeutils.InsertThreadvarTablesTable;
  390. begin
  391. { not yet supported }
  392. end;
  393. class procedure tjvmnodeutils.InsertThreadvars;
  394. begin
  395. { not yet supported }
  396. end;
  397. class procedure tjvmnodeutils.InsertWideInitsTablesTable;
  398. begin
  399. { not required }
  400. end;
  401. class procedure tjvmnodeutils.InsertWideInits;
  402. begin
  403. { not required }
  404. end;
  405. class procedure tjvmnodeutils.InsertResourceTablesTable;
  406. begin
  407. { not supported }
  408. end;
  409. class procedure tjvmnodeutils.InsertResourceInfo(ResourcesUsed: boolean);
  410. begin
  411. { not supported }
  412. end;
  413. class procedure tjvmnodeutils.InsertResStrTablesTable;
  414. begin
  415. { not supported }
  416. end;
  417. class procedure tjvmnodeutils.InsertResStrInits;
  418. begin
  419. { not supported }
  420. end;
  421. class procedure tjvmnodeutils.InsertMemorySizes;
  422. begin
  423. { not required }
  424. end;
  425. class procedure tjvmnodeutils.add_main_procdef_paras(pd: tdef);
  426. var
  427. pvs: tparavarsym;
  428. begin
  429. if (tprocdef(pd).proctypeoption=potype_proginit) then
  430. begin
  431. { add the args parameter }
  432. pvs:=cparavarsym.create('$args',1,vs_const,search_system_type('TJSTRINGARRAY').typedef,[]);
  433. tprocdef(pd).parast.insert(pvs);
  434. tprocdef(pd).calcparas;
  435. end;
  436. end;
  437. begin
  438. cnodeutils:=tjvmnodeutils;
  439. end.