njvmutil.pas 18 KB

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