njvmutil.pas 18 KB

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