njvmflw.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. {
  2. Copyright (c) 1998-2011 by Florian Klaempfl and Jonas Maebe
  3. Generate assembler for nodes that influence the flow for the JVM
  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 njvmflw;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. aasmbase,node,nflw;
  22. type
  23. tjvmraisenode = class(traisenode)
  24. function pass_typecheck: tnode; override;
  25. procedure pass_generate_code;override;
  26. end;
  27. tjvmtryexceptnode = class(ttryexceptnode)
  28. procedure pass_generate_code;override;
  29. end;
  30. tjvmtryfinallynode = class(ttryfinallynode)
  31. procedure pass_generate_code;override;
  32. end;
  33. tjvmonnode = class(tonnode)
  34. procedure pass_generate_code;override;
  35. end;
  36. implementation
  37. uses
  38. verbose,globals,systems,globtype,constexp,
  39. symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,jvmdef,
  40. procinfo,cgbase,pass_2,parabase,
  41. cpubase,cpuinfo,
  42. nld,ncon,
  43. tgobj,paramgr,
  44. cgutils,hlcgobj,hlcgcpu
  45. ;
  46. {*****************************************************************************
  47. SecondRaise
  48. *****************************************************************************}
  49. var
  50. current_except_loc: tlocation;
  51. function tjvmraisenode.pass_typecheck: tnode;
  52. begin
  53. Result:=inherited pass_typecheck;
  54. if codegenerror then
  55. exit;
  56. { Java exceptions must descend from java.lang.Throwable }
  57. if assigned(left) and
  58. not(left.resultdef).is_related(java_jlthrowable) then
  59. MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'class(TJLThrowable)');
  60. { Java exceptions cannot be raised "at" a specific location }
  61. if assigned(right) then
  62. MessagePos(right.fileinfo,parser_e_illegal_expression);
  63. end;
  64. procedure tjvmraisenode.pass_generate_code;
  65. begin
  66. if assigned(left) then
  67. begin
  68. secondpass(left);
  69. thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
  70. end
  71. else
  72. thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
  73. current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
  74. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  75. end;
  76. {*****************************************************************************
  77. SecondTryExcept
  78. *****************************************************************************}
  79. var
  80. begintrylabel,
  81. endtrylabel: tasmlabel;
  82. endexceptlabel : tasmlabel;
  83. procedure tjvmtryexceptnode.pass_generate_code;
  84. var
  85. oldendexceptlabel,
  86. oldbegintrylabel,
  87. oldendtrylabel,
  88. defaultcatchlabel: tasmlabel;
  89. oldflowcontrol,tryflowcontrol,
  90. exceptflowcontrol : tflowcontrol;
  91. begin
  92. location_reset(location,LOC_VOID,OS_NO);
  93. oldflowcontrol:=flowcontrol;
  94. flowcontrol:=[fc_inflowcontrol];
  95. { this can be called recursivly }
  96. oldbegintrylabel:=begintrylabel;
  97. oldendtrylabel:=endtrylabel;
  98. oldendexceptlabel:=endexceptlabel;
  99. { get new labels for the control flow statements }
  100. current_asmdata.getaddrlabel(begintrylabel);
  101. current_asmdata.getaddrlabel(endtrylabel);
  102. current_asmdata.getjumplabel(endexceptlabel);
  103. { try block }
  104. { set control flow labels for the try block }
  105. flowcontrol:=[fc_inflowcontrol];
  106. hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
  107. secondpass(left);
  108. hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  109. tryflowcontrol:=flowcontrol;
  110. { jump over exception handling blocks }
  111. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  112. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  113. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  114. { set control flow labels for the except block }
  115. { and the on statements }
  116. flowcontrol:=[fc_inflowcontrol];
  117. { on-statements }
  118. if assigned(right) then
  119. secondpass(right);
  120. { default handling except handling }
  121. if assigned(t1) then
  122. begin
  123. current_asmdata.getaddrlabel(defaultcatchlabel);
  124. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  125. 'all',begintrylabel,endtrylabel,defaultcatchlabel));
  126. hlcg.a_label(current_asmdata.CurrAsmList,defaultcatchlabel);
  127. { here we don't have to reset flowcontrol }
  128. { the default and on flowcontrols are handled equal }
  129. { pop the exception object from the stack }
  130. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  131. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  132. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop));
  133. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  134. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  135. { and generate the exception handling code }
  136. secondpass(t1);
  137. exceptflowcontrol:=flowcontrol;
  138. end;
  139. hlcg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
  140. { restore all saved labels }
  141. begintrylabel:=oldbegintrylabel;
  142. endtrylabel:=oldendtrylabel;
  143. endexceptlabel:=oldendexceptlabel;
  144. { return all used control flow statements }
  145. flowcontrol:=oldflowcontrol+(exceptflowcontrol +
  146. tryflowcontrol - [fc_inflowcontrol]);
  147. end;
  148. {*****************************************************************************
  149. SecondOn
  150. *****************************************************************************}
  151. procedure tjvmonnode.pass_generate_code;
  152. var
  153. thisonlabel : tasmlabel;
  154. oldflowcontrol : tflowcontrol;
  155. exceptvarsym : tlocalvarsym;
  156. begin
  157. location_reset(location,LOC_VOID,OS_NO);
  158. oldflowcontrol:=flowcontrol;
  159. flowcontrol:=[fc_inflowcontrol];
  160. current_asmdata.getjumplabel(thisonlabel);
  161. hlcg.a_label(current_asmdata.CurrAsmList,thisonlabel);
  162. if assigned(excepTSymtable) then
  163. exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
  164. else
  165. internalerror(2011020402);
  166. { add exception catching information for the JVM: exception type
  167. (will have to be adjusted if/when support for catching class
  168. reference types is added), begin/end of code in which the exception
  169. can be raised, and start of this exception handling code }
  170. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  171. tobjectdef(exceptvarsym.vardef).jvm_full_typename(true),
  172. begintrylabel,endtrylabel,thisonlabel));
  173. { Retrieve exception variable }
  174. { 1) prepare the location where we'll store it }
  175. location_reset_ref(exceptvarsym.localloc,LOC_REFERENCE,OS_ADDR,sizeof(pint));
  176. tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),exceptvarsym.vardef,exceptvarsym.localloc.reference);
  177. current_except_loc:=exceptvarsym.localloc;
  178. { 2) the exception variable is at the top of the evaluation stack
  179. (placed there by the JVM) -> adjust stack count, then store it }
  180. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  181. thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,exceptvarsym.vardef,current_except_loc);
  182. if assigned(right) then
  183. secondpass(right);
  184. { clear some stuff }
  185. tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
  186. exceptvarsym.localloc.loc:=LOC_INVALID;
  187. current_except_loc.loc:=LOC_INVALID;
  188. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  189. flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]);
  190. { next on node }
  191. if assigned(left) then
  192. secondpass(left);
  193. end;
  194. {*****************************************************************************
  195. SecondTryFinally
  196. *****************************************************************************}
  197. procedure tjvmtryfinallynode.pass_generate_code;
  198. var
  199. begintrylabel,
  200. endtrylabel,
  201. reraiselabel,
  202. finallylabel,
  203. finallyexceptlabel,
  204. endfinallylabel,
  205. exitfinallylabel,
  206. continuefinallylabel,
  207. breakfinallylabel,
  208. oldCurrExitLabel,
  209. oldContinueLabel,
  210. oldBreakLabel : tasmlabel;
  211. oldflowcontrol,tryflowcontrol : tflowcontrol;
  212. finallycodecopy: tnode;
  213. reasonbuf,
  214. exceptreg: tregister;
  215. begin
  216. location_reset(location,LOC_VOID,OS_NO);
  217. { check if child nodes do a break/continue/exit }
  218. oldflowcontrol:=flowcontrol;
  219. flowcontrol:=[fc_inflowcontrol];
  220. current_asmdata.getjumplabel(finallylabel);
  221. current_asmdata.getjumplabel(endfinallylabel);
  222. current_asmdata.getjumplabel(reraiselabel);
  223. { the finally block must catch break, continue and exit }
  224. { statements }
  225. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  226. if implicitframe then
  227. exitfinallylabel:=finallylabel
  228. else
  229. current_asmdata.getjumplabel(exitfinallylabel);
  230. current_procinfo.CurrExitLabel:=exitfinallylabel;
  231. if assigned(current_procinfo.CurrBreakLabel) then
  232. begin
  233. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  234. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  235. if implicitframe then
  236. begin
  237. breakfinallylabel:=finallylabel;
  238. continuefinallylabel:=finallylabel;
  239. end
  240. else
  241. begin
  242. current_asmdata.getjumplabel(breakfinallylabel);
  243. current_asmdata.getjumplabel(continuefinallylabel);
  244. end;
  245. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  246. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  247. end;
  248. { allocate reg to store the reason why the finally block was entered
  249. (no exception, break, continue, exit), so we can continue to the
  250. right label afterwards. In case of an exception, we use a separate
  251. (duplicate) finally block because otherwise the JVM's bytecode
  252. verification cannot statically prove that the exception reraise code
  253. will only execute in case an exception actually happened }
  254. reasonbuf:=hlcg.getaddressregister(current_asmdata.CurrAsmList,s32inttype);
  255. { try code }
  256. begintrylabel:=nil;
  257. endtrylabel:=nil;
  258. if assigned(left) then
  259. begin
  260. current_asmdata.getaddrlabel(begintrylabel);
  261. current_asmdata.getaddrlabel(endtrylabel);
  262. hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
  263. secondpass(left);
  264. hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  265. tryflowcontrol:=flowcontrol;
  266. if codegenerror then
  267. exit;
  268. { reason: no exception occurred }
  269. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,0,reasonbuf);
  270. end;
  271. { begin of the finally code }
  272. hlcg.a_label(current_asmdata.CurrAsmList,finallylabel);
  273. { finally code }
  274. flowcontrol:=[fc_inflowcontrol];
  275. { duplicate finally code for case when exception happened }
  276. if assigned(begintrylabel) then
  277. finallycodecopy:=right.getcopy;
  278. secondpass(right);
  279. { goto is allowed if it stays inside the finally block,
  280. this is checked using the exception block number }
  281. if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then
  282. CGMessage(cg_e_control_flow_outside_finally);
  283. if codegenerror then
  284. begin
  285. if assigned(begintrylabel) then
  286. finallycodecopy.free;
  287. exit;
  288. end;
  289. { don't generate line info for internal cleanup }
  290. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  291. { the reasonbuf holds the reason why this (non-exception) finally code
  292. was executed:
  293. 0 = try code simply finished
  294. 1 = (unused) exception raised
  295. 2 = exit called
  296. 3 = break called
  297. 4 = continue called }
  298. if not(implicitframe) then
  299. begin
  300. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,0,reasonbuf,endfinallylabel);
  301. if fc_exit in tryflowcontrol then
  302. if ([fc_break,fc_continue]*tryflowcontrol)<>[] then
  303. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,2,reasonbuf,oldCurrExitLabel)
  304. else
  305. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
  306. if fc_break in tryflowcontrol then
  307. if fc_continue in tryflowcontrol then
  308. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,3,reasonbuf,oldBreakLabel)
  309. else
  310. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
  311. if fc_continue in tryflowcontrol then
  312. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
  313. { now generate the trampolines for exit/break/continue to load the reasonbuf }
  314. if fc_exit in tryflowcontrol then
  315. begin
  316. hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
  317. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,2,reasonbuf);
  318. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  319. end;
  320. if fc_break in tryflowcontrol then
  321. begin
  322. hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
  323. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,3,reasonbuf);
  324. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  325. end;
  326. if fc_continue in tryflowcontrol then
  327. begin
  328. hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
  329. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,4,reasonbuf);
  330. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  331. end;
  332. { jump over finally-code-in-case-an-exception-happened }
  333. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
  334. end;
  335. { generate finally code in case an exception occurred }
  336. if assigned(begintrylabel) then
  337. begin
  338. current_asmdata.getaddrlabel(finallyexceptlabel);
  339. hlcg.a_label(current_asmdata.CurrAsmList,finallyexceptlabel);
  340. { catch the exceptions }
  341. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  342. 'all',begintrylabel,endtrylabel,finallyexceptlabel));
  343. { store the generated exception object to a temp }
  344. exceptreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlthrowable);
  345. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  346. thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
  347. { generate the finally code again }
  348. secondpass(finallycodecopy);
  349. finallycodecopy.free;
  350. { in case of an implicit frame, also execute the exception handling
  351. code }
  352. if implicitframe then
  353. begin
  354. flowcontrol:=[fc_inflowcontrol];
  355. secondpass(t1);
  356. if flowcontrol<>[fc_inflowcontrol] then
  357. CGMessage(cg_e_control_flow_outside_finally);
  358. if codegenerror then
  359. exit;
  360. end;
  361. { reraise the exception }
  362. thlcgjvm(hlcg).a_load_reg_stack(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
  363. current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
  364. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  365. end;
  366. hlcg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
  367. { end cleanup }
  368. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  369. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  370. if assigned(current_procinfo.CurrBreakLabel) then
  371. begin
  372. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  373. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  374. end;
  375. flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]);
  376. end;
  377. begin
  378. craisenode:=tjvmraisenode;
  379. ctryexceptnode:=tjvmtryexceptnode;
  380. ctryfinallynode:=tjvmtryfinallynode;
  381. connode:=tjvmonnode;
  382. end.