njvmcal.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. {
  2. Copyright (c) 2011 by Jonas Maebe
  3. JVM-specific code for call nodes
  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 njvmcal;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cgbase,
  22. symtype,symdef,
  23. node,ncal,ncgcal;
  24. type
  25. tjvmcallparanode = class(tcgcallparanode)
  26. protected
  27. procedure push_formal_para; override;
  28. procedure push_copyout_para; override;
  29. procedure handlemanagedbyrefpara(orgparadef: tdef); override;
  30. end;
  31. { tjvmcallnode }
  32. tjvmcallnode = class(tcgcallnode)
  33. protected
  34. procedure wrapcomplexinlinepara(para: tcallparanode); override;
  35. procedure extra_pre_call_code; override;
  36. procedure set_result_location(realresdef: tstoreddef); override;
  37. procedure do_release_unused_return_value;override;
  38. procedure extra_post_call_code; override;
  39. function dispatch_procvar: tnode;
  40. procedure remove_hidden_paras;
  41. public
  42. function pass_typecheck: tnode; override;
  43. function pass_1: tnode; override;
  44. end;
  45. implementation
  46. uses
  47. verbose,globals,globtype,constexp,cutils,
  48. symconst,symtable,symsym,defutil,
  49. cgutils,tgobj,procinfo,htypechk,
  50. cpubase,aasmdata,aasmcpu,
  51. hlcgobj,hlcgcpu,
  52. pass_1,nutils,nbas,ncnv,ncon,ninl,nld,nmem,
  53. jvmdef;
  54. {*****************************************************************************
  55. TJVMCALLPARANODE
  56. *****************************************************************************}
  57. procedure tjvmcallparanode.push_formal_para;
  58. begin
  59. { primitive values are boxed, so in all cases this is a pointer to
  60. something and since it cannot be changed (or is not supposed to be
  61. changed anyway), we don't have to create a temporary array to hold a
  62. pointer to this value and can just pass the pointer to this value
  63. directly.
  64. In case the value can be changed (formal var/out), then we have
  65. already created a temporary array of one element that holds the boxed
  66. (or in case of a non-primitive type: original) value. The reason is
  67. that copying it back out may be a complex operation which we don't
  68. want to handle at the code generator level.
  69. -> always push a value parameter (which is either an array of one
  70. element, or an object) }
  71. push_value_para
  72. end;
  73. procedure tjvmcallparanode.push_copyout_para;
  74. begin
  75. { everything is wrapped and replaced by handlemanagedbyrefpara() in
  76. pass_1 }
  77. push_value_para;
  78. end;
  79. procedure getparabasenodes(p: tnode; out basenode: tnode; out parent: tunarynode);
  80. begin
  81. parent:=nil;
  82. while assigned(p) do
  83. begin
  84. case p.nodetype of
  85. inlinen:
  86. begin
  87. if tinlinenode(p).inlinenumber=in_box_x then
  88. begin
  89. parent:=tunarynode(p);
  90. p:=parent.left;
  91. end
  92. else
  93. break;
  94. end;
  95. subscriptn,
  96. vecn:
  97. begin
  98. break;
  99. end;
  100. typeconvn:
  101. begin
  102. parent:=tunarynode(p);
  103. { skip typeconversions that don't change the node type }
  104. p:=p.actualtargetnode;
  105. end;
  106. derefn:
  107. begin
  108. parent:=tunarynode(p);
  109. p:=tunarynode(p).left;
  110. end
  111. else
  112. break;
  113. end;
  114. end;
  115. basenode:=p;
  116. end;
  117. function replacewithtemp(var orgnode:tnode): ttempcreatenode;
  118. begin
  119. if valid_for_var(orgnode,false) then
  120. result:=ctempcreatenode.create_reference(
  121. orgnode.resultdef,orgnode.resultdef.size,
  122. tt_persistent,true,orgnode,true)
  123. else
  124. result:=ctempcreatenode.create_value(
  125. orgnode.resultdef,orgnode.resultdef.size,
  126. tt_persistent,true,orgnode);
  127. { this node is reused while constructing the temp }
  128. orgnode:=ctemprefnode.create(result);
  129. typecheckpass(orgnode);
  130. end;
  131. procedure tjvmcallparanode.handlemanagedbyrefpara(orgparadef: tdef);
  132. var
  133. arrdef: tarraydef;
  134. arreledef: tdef;
  135. initstat,
  136. copybackstat,
  137. finistat: tstatementnode;
  138. finiblock: tblocknode;
  139. realpara, tempn: tnode;
  140. realparaparent: tunarynode;
  141. realparatemp, arraytemp: ttempcreatenode;
  142. leftcopy: tnode;
  143. implicitptrpara: boolean;
  144. begin
  145. { implicit pointer types are already pointers -> no need to stuff them
  146. in an array to pass them by reference (except in case of a formal
  147. parameter, in which case everything is passed in an array since the
  148. callee can't know what was passed in) }
  149. if jvmimplicitpointertype(orgparadef) and
  150. (parasym.vardef.typ<>formaldef) then
  151. exit;
  152. fparainit:=internalstatements(initstat);
  153. fparacopyback:=internalstatements(copybackstat);
  154. finiblock:=internalstatements(finistat);
  155. getparabasenodes(left,realpara,realparaparent);
  156. { make sure we can get a copy of left safely, so we can use it both
  157. to load the original parameter value and to assign the result again
  158. afterwards (if required) }
  159. { special case for access to string character, because those are
  160. translated into function calls that differ depending on which side of
  161. an assignment they are on }
  162. if (realpara.nodetype=vecn) and
  163. (tvecnode(realpara).left.resultdef.typ=stringdef) then
  164. begin
  165. if node_complexity(tvecnode(realpara).left)>1 then
  166. begin
  167. realparatemp:=replacewithtemp(tvecnode(realpara).left);
  168. addstatement(initstat,realparatemp);
  169. addstatement(finistat,ctempdeletenode.create(realparatemp));
  170. end;
  171. if node_complexity(tvecnode(realpara).right)>1 then
  172. begin
  173. realparatemp:=replacewithtemp(tvecnode(realpara).right);
  174. addstatement(initstat,realparatemp);
  175. addstatement(finistat,ctempdeletenode.create(realparatemp));
  176. end;
  177. end
  178. else
  179. begin
  180. { general case: if it's possible that there's a function call
  181. involved, use a temp to prevent double evaluations }
  182. if assigned(realparaparent) then
  183. begin
  184. realparatemp:=replacewithtemp(realparaparent.left);
  185. addstatement(initstat,realparatemp);
  186. addstatement(finistat,ctempdeletenode.create(realparatemp));
  187. end;
  188. end;
  189. { create a copy of the original left (with temps already substituted),
  190. so we can use it if required to handle copying the return value back }
  191. leftcopy:=left.getcopy;
  192. implicitptrpara:=jvmimplicitpointertype(orgparadef);
  193. { create the array temp that that will serve as the paramter }
  194. if parasym.vardef.typ=formaldef then
  195. arreledef:=java_jlobject
  196. else if implicitptrpara then
  197. arreledef:=getpointerdef(orgparadef)
  198. else
  199. arreledef:=parasym.vardef;
  200. arrdef:=getsingletonarraydef(arreledef);
  201. { the -1 means "use the array's element count to determine the number
  202. of elements" in the JVM temp generator }
  203. arraytemp:=ctempcreatenode.create(arrdef,-1,tt_persistent,true);
  204. addstatement(initstat,arraytemp);
  205. addstatement(finistat,ctempdeletenode.create(arraytemp));
  206. { in case of a non-out parameter, pass in the original value (also
  207. always in case of implicitpointer type, since that pointer points to
  208. the data that will be changed by the callee) }
  209. if (parasym.varspez<>vs_out) or
  210. ((parasym.vardef.typ<>formaldef) and
  211. implicitptrpara) then
  212. begin
  213. if implicitptrpara then
  214. begin
  215. { pass pointer to the struct }
  216. left:=caddrnode.create_internal(left);
  217. include(left.flags,nf_typedaddr);
  218. typecheckpass(left);
  219. end;
  220. { wrap the primitive type in an object container
  221. if required }
  222. if parasym.vardef.typ=formaldef then
  223. begin
  224. if (left.resultdef.typ in [orddef,floatdef]) then
  225. begin
  226. left:=cinlinenode.create(in_box_x,false,ccallparanode.create(left,nil));
  227. typecheckpass(left);
  228. end;
  229. left:=ctypeconvnode.create_explicit(left,java_jlobject);
  230. end;
  231. { put the parameter value in the array }
  232. addstatement(initstat,cassignmentnode.create(
  233. cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0)),
  234. left));
  235. end
  236. else
  237. left.free;
  238. { replace the parameter with the temp array }
  239. left:=ctemprefnode.create(arraytemp);
  240. { generate the code to copy back the changed value into the original
  241. parameter in case of var/out.
  242. In case of a formaldef, changes to the parameter in the callee change
  243. the pointer inside the array -> we have to copy back the changes in
  244. all cases.
  245. In case of a regular parameter, we only have to copy things back in
  246. case it's not an implicit pointer type. The reason is that for
  247. implicit pointer types, any changes will have been directly applied
  248. to the original parameter via the implicit pointer that we passed in }
  249. if (parasym.varspez in [vs_var,vs_out]) and
  250. ((parasym.vardef.typ=formaldef) or
  251. not implicitptrpara) then
  252. begin
  253. { add the extraction of the parameter and assign it back to the
  254. original location }
  255. tempn:=ctemprefnode.create(arraytemp);
  256. tempn:=cvecnode.create(tempn,genintconstnode(0));
  257. { unbox if necessary }
  258. if parasym.vardef.typ=formaldef then
  259. begin
  260. if orgparadef.typ in [orddef,floatdef] then
  261. tempn:=cinlinenode.create(in_unbox_x_y,false,ccallparanode.create(
  262. ctypenode.create(orgparadef),ccallparanode.create(tempn,nil)))
  263. else if implicitptrpara then
  264. tempn:=ctypeconvnode.create_explicit(tempn,getpointerdef(orgparadef))
  265. end;
  266. if implicitptrpara then
  267. tempn:=cderefnode.create(tempn);
  268. addstatement(copybackstat,cassignmentnode.create(leftcopy,
  269. ctypeconvnode.create_explicit(tempn,orgparadef)));
  270. end
  271. else
  272. leftcopy.free;
  273. addstatement(copybackstat,finiblock);
  274. firstpass(fparainit);
  275. firstpass(left);
  276. firstpass(fparacopyback);
  277. end;
  278. {*****************************************************************************
  279. TJVMCALLNODE
  280. *****************************************************************************}
  281. procedure tjvmcallnode.wrapcomplexinlinepara(para: tcallparanode);
  282. var
  283. tempnode: ttempcreatenode;
  284. begin
  285. { don't use caddrnodes for the JVM target, because we can't take the
  286. address of every kind of type (e.g., of ansistrings). A temp-reference
  287. node does work for any kind of memory reference (and the expectloc
  288. is LOC_(C)REFERENCE when this routine is called), but is not (yet)
  289. supported for other targets }
  290. tempnode:=ctempcreatenode.create_reference(para.parasym.vardef,para.parasym.vardef.size,
  291. tt_persistent,tparavarsym(para.parasym).is_regvar(false),para.left,false);
  292. addstatement(inlineinitstatement,tempnode);
  293. addstatement(inlinecleanupstatement,ctempdeletenode.create(tempnode));
  294. para.left:=ctemprefnode.create(tempnode);
  295. { inherit addr_taken flag }
  296. if (tabstractvarsym(para.parasym).addr_taken) then
  297. include(tempnode.tempinfo^.flags,ti_addr_taken);
  298. end;
  299. procedure tjvmcallnode.extra_pre_call_code;
  300. begin
  301. { when calling a constructor, first create a new instance, except
  302. when calling it from another constructor (because then this has
  303. already been done before calling the current constructor) }
  304. if procdefinition.typ<>procdef then
  305. exit;
  306. if tabstractprocdef(procdefinition).proctypeoption<>potype_constructor then
  307. exit;
  308. if not(methodpointer.resultdef.typ in [classrefdef,recorddef]) then
  309. exit;
  310. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(tabstractprocdef(procdefinition).owner.defowner).jvm_full_typename(true))));
  311. { the constructor doesn't return anything, so put a duplicate of the
  312. self pointer on the evaluation stack for use as function result
  313. after the constructor has run }
  314. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup));
  315. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,2);
  316. end;
  317. procedure tjvmcallnode.set_result_location(realresdef: tstoreddef);
  318. begin
  319. location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1);
  320. { in case of jvmimplicitpointertype(), the function will have allocated
  321. it already and we don't have to allocate it again here }
  322. if not jvmimplicitpointertype(realresdef) then
  323. tg.gethltemp(current_asmdata.CurrAsmList,realresdef,realresdef.size,tt_normal,location.reference)
  324. else
  325. tg.gethltemp(current_asmdata.CurrAsmList,java_jlobject,java_jlobject.size,tt_normal,location.reference);
  326. end;
  327. procedure tjvmcallnode.do_release_unused_return_value;
  328. begin
  329. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
  330. (current_procinfo.procdef.proctypeoption=potype_constructor) then
  331. exit;
  332. if (location.loc=LOC_REFERENCE) then
  333. tg.ungetiftemp(current_asmdata.CurrAsmList,location.reference);
  334. if assigned(funcretnode) then
  335. exit;
  336. case resultdef.size of
  337. 0:
  338. ;
  339. 1..4:
  340. begin
  341. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop));
  342. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  343. end;
  344. 8:
  345. begin
  346. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop2));
  347. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,2);
  348. end
  349. else
  350. internalerror(2011010305);
  351. end;
  352. end;
  353. procedure tjvmcallnode.extra_post_call_code;
  354. var
  355. totalremovesize: longint;
  356. realresdef: tdef;
  357. begin
  358. if not assigned(typedef) then
  359. realresdef:=tstoreddef(resultdef)
  360. else
  361. realresdef:=tstoreddef(typedef);
  362. { a constructor doesn't actually return a value in the jvm }
  363. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) then
  364. totalremovesize:=pushedparasize
  365. else
  366. begin
  367. { zero-extend unsigned 8/16 bit returns (we have to return them
  368. sign-extended to keep the Android verifier happy, and even if that
  369. one did not exist a plain Java routine could return a
  370. sign-extended value) }
  371. if cnf_return_value_used in callnodeflags then
  372. thlcgjvm(hlcg).maybe_resize_stack_para_val(current_asmdata.CurrAsmList,realresdef,false);
  373. { even a byte takes up a full stackslot -> align size to multiple of 4 }
  374. totalremovesize:=pushedparasize-(align(realresdef.size,4) shr 2);
  375. end;
  376. { remove parameters from internal evaluation stack counter (in case of
  377. e.g. no parameters and a result, it can also increase) }
  378. if totalremovesize>0 then
  379. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,totalremovesize)
  380. else if totalremovesize<0 then
  381. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,-totalremovesize);
  382. { if this was an inherited constructor call, initialise all fields that
  383. are wrapped types following it }
  384. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
  385. (cnf_inherited in callnodeflags) then
  386. thlcgjvm(hlcg).gen_initialize_fields_code(current_asmdata.CurrAsmList);
  387. end;
  388. procedure tjvmcallnode.remove_hidden_paras;
  389. var
  390. prevpara, para, nextpara: tcallparanode;
  391. begin
  392. prevpara:=nil;
  393. para:=tcallparanode(left);
  394. while assigned(para) do
  395. begin
  396. nextpara:=tcallparanode(para.right);
  397. if vo_is_hidden_para in para.parasym.varoptions then
  398. begin
  399. if assigned(prevpara) then
  400. prevpara.right:=nextpara
  401. else
  402. left:=nextpara;
  403. para.right:=nil;
  404. para.free;
  405. end
  406. else
  407. prevpara:=para;
  408. para:=nextpara;
  409. end;
  410. end;
  411. function tjvmcallnode.pass_typecheck: tnode;
  412. begin
  413. result:=inherited pass_typecheck;
  414. if assigned(result) or
  415. codegenerror then
  416. exit;
  417. { unfortunately, we cannot handle a call to a virtual constructor for
  418. the current instance from inside another constructor. The reason is
  419. that these must be called via reflection, but before an instance has
  420. been fully initialized (which can only be done by calling either an
  421. inherited constructor or another constructor of this class) you can't
  422. perform reflection.
  423. Replacing virtual constructors with plain virtual methods that are
  424. called after the instance has been initialized causes problems if they
  425. in turn call plain constructors from inside the JDK (you cannot call
  426. constructors anymore once the instance has been constructed). It also
  427. causes problems regarding which other constructor to call then instead
  428. before to initialize the instance (we could add dummy constructors for
  429. that purpose to Pascal classes, but that scheme breaks when a class
  430. inherits from a JDK class other than JLObject).
  431. }
  432. if (current_procinfo.procdef.proctypeoption=potype_constructor) and
  433. not(cnf_inherited in callnodeflags) and
  434. (procdefinition.proctypeoption=potype_constructor) and
  435. (po_virtualmethod in procdefinition.procoptions) and
  436. (cnf_member_call in callnodeflags) then
  437. CGMessage(parser_e_jvm_invalid_virtual_constructor_call);
  438. end;
  439. function tjvmcallnode.dispatch_procvar: tnode;
  440. var
  441. pdclass: tobjectdef;
  442. begin
  443. pdclass:=tprocvardef(right.resultdef).classdef;
  444. { convert procvar type into corresponding class }
  445. if not tprocvardef(right.resultdef).is_addressonly then
  446. begin
  447. right:=caddrnode.create_internal(right);
  448. include(right.flags,nf_typedaddr);
  449. end;
  450. right:=ctypeconvnode.create_explicit(right,pdclass);
  451. include(right.flags,nf_load_procvar);
  452. typecheckpass(right);
  453. { call the invoke method with these parameters. It will take care of the
  454. wrapping and typeconversions; first filter out the automatically added
  455. hidden parameters though }
  456. remove_hidden_paras;
  457. result:=ccallnode.createinternmethod(right,'INVOKE',left);
  458. { reused }
  459. left:=nil;
  460. right:=nil;
  461. end;
  462. function tjvmcallnode.pass_1: tnode;
  463. var
  464. sym: tsym;
  465. wrappername: shortstring;
  466. begin
  467. { transform procvar calls }
  468. if assigned(right) then
  469. result:=dispatch_procvar
  470. else
  471. begin
  472. { replace virtual class method and constructor calls in case they may
  473. be indirect; make sure we don't replace the callthrough to the
  474. original constructor with another call to the wrapper }
  475. if (procdefinition.typ=procdef) and
  476. (current_procinfo.procdef.synthetickind<>tsk_callthrough) and
  477. not(cnf_inherited in callnodeflags) and
  478. ((procdefinition.proctypeoption=potype_constructor) or
  479. (po_classmethod in procdefinition.procoptions)) and
  480. (po_virtualmethod in procdefinition.procoptions) and
  481. (methodpointer.nodetype<>loadvmtaddrn) then
  482. begin
  483. wrappername:=symtableprocentry.name+'__FPCVIRTUALCLASSMETHOD__';
  484. sym:=
  485. search_struct_member(tobjectdef(procdefinition.owner.defowner),
  486. wrappername);
  487. if not assigned(sym) or
  488. (sym.typ<>procsym) then
  489. internalerror(2011072801);
  490. { do not simply replace the procsym/procdef in case we could
  491. in theory do that, because the parameter nodes have already
  492. been bound to the current procdef's parasyms }
  493. remove_hidden_paras;
  494. result:=ccallnode.create(left,tprocsym(sym),symtableproc,methodpointer,callnodeflags);
  495. result.flags:=flags;
  496. left:=nil;
  497. methodpointer:=nil;
  498. exit;
  499. end;
  500. result:=inherited pass_1;
  501. if assigned(result) then
  502. exit;
  503. end;
  504. end;
  505. begin
  506. ccallnode:=tjvmcallnode;
  507. ccallparanode:=tjvmcallparanode;
  508. end.