nwasmcal.pas 26 KB

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