From 92526c41a9ea39714e23ab4ba1d9694963ddce7a Mon Sep 17 00:00:00 2001
From: nickysn <nickysn@gmail.com>
Date: Wed, 29 Jul 2020 17:07:58 +0000
Subject: [PATCH] [PATCH 25/83] adding nwasmcal (copied over from njvmcal) in
 order to properly handle function results

From 7652ef7e443b90453d6e4559e5c1641add53daf2 Mon Sep 17 00:00:00 2001
From: Dmitry Boyarintsev <skalogryz.lists@gmail.com>
Date: Wed, 11 Sep 2019 22:57:08 -0400

git-svn-id: branches/wasm@45902 -
---
 .gitattributes             |   1 +
 compiler/wasm/cpunode.pas  |   2 +-
 compiler/wasm/nwasmcal.pas | 628 +++++++++++++++++++++++++++++++++++++
 3 files changed, 630 insertions(+), 1 deletion(-)
 create mode 100644 compiler/wasm/nwasmcal.pas

diff --git a/.gitattributes b/.gitattributes
index 3e632e8c95..261b7d48a5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -932,6 +932,7 @@ compiler/wasm/cputarg.pas svneol=native#text/plain
 compiler/wasm/hlcgcpu.pas svneol=native#text/plain
 compiler/wasm/itcpuwasm.pas svneol=native#text/plain
 compiler/wasm/nwasmadd.pas svneol=native#text/plain
+compiler/wasm/nwasmcal.pas svneol=native#text/plain
 compiler/wasm/nwasmflw.pas svneol=native#text/plain
 compiler/wasm/rgcpu.pas svneol=native#text/plain
 compiler/wasm/rwasmcon.inc svneol=native#text/plain
diff --git a/compiler/wasm/cpunode.pas b/compiler/wasm/cpunode.pas
index dd910f4720..ba96366fb5 100644
--- a/compiler/wasm/cpunode.pas
+++ b/compiler/wasm/cpunode.pas
@@ -33,7 +33,7 @@ implementation
     ncgbas,ncgflw,ncgcnv,ncgld,ncgmem,ncgcon,ncgset,
     ncgadd, ncgcal,ncgmat,ncginl,
     
-    nwasmadd, nwasmflw,
+    nwasmadd, nwasmcal, nwasmflw,
     (* todo: WASM
     njvmadd,njvmcal,njvmmat,njvmcnv,njvmcon,njvminl,njvmmem,njvmflw,njvmld,
     njvmset,njvmvmt
diff --git a/compiler/wasm/nwasmcal.pas b/compiler/wasm/nwasmcal.pas
new file mode 100644
index 0000000000..94e646ac54
--- /dev/null
+++ b/compiler/wasm/nwasmcal.pas
@@ -0,0 +1,628 @@
+{
+    Copyright (c) 2011 by Dmitry Boyarintsev
+
+    WebAssembly-specific code for call nodes
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit nwasmcal;
+
+{$i fpcdefs.inc}
+
+interface
+
+    uses
+      cgbase,
+      symtype,symdef,
+      node,ncal,ncgcal;
+
+    type
+       twasmcallparanode = class(tcgcallparanode)
+        protected
+         function push_zero_sized_value_para: boolean; override;
+
+         procedure push_formal_para; override;
+         procedure push_copyout_para; override;
+
+         procedure handlemanagedbyrefpara(orgparadef: tdef); override;
+       end;
+
+       { tjvmcallnode }
+
+       twasmcallnode = class(tcgcallnode)
+        protected
+         procedure wrapcomplexinlinepara(para: tcallparanode); override;
+         procedure extra_pre_call_code; override;
+         procedure set_result_location(realresdef: tstoreddef); override;
+         procedure do_release_unused_return_value;override;
+         procedure extra_post_call_code; override;
+         function dispatch_procvar: tnode;
+         procedure remove_hidden_paras;
+         procedure gen_vmt_entry_load; override;
+        public
+         function pass_typecheck: tnode; override;
+         function pass_1: tnode; override;
+       end;
+
+
+implementation
+
+    uses
+      verbose,globals,globtype,constexp,cutils,compinnr,
+      symconst,symtable,symsym,symcpu,defutil,
+      cgutils,tgobj,procinfo,htypechk,
+      cpubase,aasmbase,aasmdata,aasmcpu,
+      hlcgobj,hlcgcpu,
+      pass_1,nutils,nadd,nbas,ncnv,ncon,nflw,ninl,nld,nmem;
+
+{*****************************************************************************
+                           TJVMCALLPARANODE
+*****************************************************************************}
+
+    function twasmcallparanode.push_zero_sized_value_para: boolean;
+      begin
+        { part of the signature -> need to be pushed }
+        result:=true;
+      end;
+
+
+    procedure twasmcallparanode.push_formal_para;
+      begin
+        { primitive values are boxed, so in all cases this is a pointer to
+          something and since it cannot be changed (or is not supposed to be
+          changed anyway), we don't have to create a temporary array to hold a
+          pointer to this value and can just pass the pointer to this value
+          directly.
+
+          In case the value can be changed (formal var/out), then we have
+          already created a temporary array of one element that holds the boxed
+          (or in case of a non-primitive type: original) value. The reason is
+          that copying it back out may be a complex operation which we don't
+          want to handle at the code generator level.
+
+          -> always push a value parameter (which is either an array of one
+          element, or an object) }
+        push_value_para
+      end;
+
+
+    procedure twasmcallparanode.push_copyout_para;
+      begin
+        { everything is wrapped and replaced by handlemanagedbyrefpara() in
+          pass_1 }
+        push_value_para;
+      end;
+
+
+    procedure getparabasenodes(p: tnode; out basenode: tnode; out parent: tunarynode);
+      begin
+        parent:=nil;
+        while assigned(p) do
+          begin
+            case p.nodetype of
+              inlinen:
+                begin
+                  if tinlinenode(p).inlinenumber=in_box_x then
+                    begin
+                      parent:=tunarynode(p);
+                      p:=parent.left;
+                    end
+                  else
+                    break;
+                end;
+              subscriptn,
+              vecn:
+                begin
+                  break;
+                end;
+              typeconvn:
+                begin
+                  parent:=tunarynode(p);
+                  { skip typeconversions that don't change the node type }
+                  p:=actualtargetnode(@p)^;
+                end;
+              derefn:
+                begin
+                  parent:=tunarynode(p);
+                  p:=tunarynode(p).left;
+                end
+              else
+                break;
+            end;
+          end;
+        basenode:=p;
+      end;
+
+
+    function replacewithtemp(var orgnode:tnode): ttempcreatenode;
+      begin
+        if valid_for_var(orgnode,false) then
+          result:=ctempcreatenode.create_reference(
+            orgnode.resultdef,orgnode.resultdef.size,
+            tt_persistent,true,orgnode,true)
+        else
+          result:=ctempcreatenode.create_value(
+            orgnode.resultdef,orgnode.resultdef.size,
+            tt_persistent,true,orgnode);
+        { this node is reused while constructing the temp }
+        orgnode:=ctemprefnode.create(result);
+        typecheckpass(orgnode);
+      end;
+
+
+    procedure twasmcallparanode.handlemanagedbyrefpara(orgparadef: tdef);
+      var
+        arrdef: tarraydef;
+        arreledef: tdef;
+        initstat,
+        copybackstat,
+        finistat: tstatementnode;
+        finiblock: tblocknode;
+        realpara, tempn, unwrappedele0, unwrappedele1: tnode;
+        realparaparent: tunarynode;
+        realparatemp, arraytemp: ttempcreatenode;
+        leftcopy: tnode;
+        implicitptrpara,
+        verifyout: boolean;
+      begin
+        { the original version doesn't do anything for garbage collected
+          platforms, but who knows in the future }
+        inherited;
+        { implicit pointer types are already pointers -> no need to stuff them
+          in an array to pass them by reference (except in case of a formal
+          parameter, in which case everything is passed in an array since the
+          callee can't know what was passed in) }
+        //if jvmimplicitpointertype(orgparadef) and
+           //(parasym.vardef.typ<>formaldef) then
+           //exit;
+
+        fparainit:=internalstatements(initstat);
+        fparacopyback:=internalstatements(copybackstat);
+        finiblock:=internalstatements(finistat);
+        getparabasenodes(left,realpara,realparaparent);
+        { make sure we can get a copy of left safely, so we can use it both
+          to load the original parameter value and to assign the result again
+          afterwards (if required) }
+
+        { special case for access to string character, because those are
+          translated into function calls that differ depending on which side of
+          an assignment they are on }
+        if (realpara.nodetype=vecn) and
+           (tvecnode(realpara).left.resultdef.typ=stringdef) then
+          begin
+            if node_complexity(tvecnode(realpara).left)>1 then
+              begin
+                realparatemp:=replacewithtemp(tvecnode(realpara).left);
+                addstatement(initstat,realparatemp);
+                addstatement(finistat,ctempdeletenode.create(realparatemp));
+              end;
+            if node_complexity(tvecnode(realpara).right)>1 then
+              begin
+                realparatemp:=replacewithtemp(tvecnode(realpara).right);
+                addstatement(initstat,realparatemp);
+                addstatement(finistat,ctempdeletenode.create(realparatemp));
+              end;
+          end
+        else
+          begin
+            { general case: if it's possible that there's a function call
+              involved, use a temp to prevent double evaluations }
+            if assigned(realparaparent) then
+              begin
+                realparatemp:=replacewithtemp(realparaparent.left);
+                addstatement(initstat,realparatemp);
+                addstatement(finistat,ctempdeletenode.create(realparatemp));
+              end;
+          end;
+        { create a copy of the original left (with temps already substituted),
+          so we can use it if required to handle copying the return value back }
+        leftcopy:=left.getcopy;
+
+        //todo:
+        //implicitptrpara:=jvmimplicitpointertype(orgparadef);
+        implicitptrpara := false;
+
+        { create the array temp that that will serve as the paramter }
+        if parasym.vardef.typ=formaldef then
+          arreledef:=java_jlobject
+        else if implicitptrpara then
+          arreledef:=cpointerdef.getreusable(orgparadef)
+        else
+          arreledef:=parasym.vardef;
+        arrdef:=carraydef.getreusable(arreledef,1+ord(cs_check_var_copyout in current_settings.localswitches));
+        { the -1 means "use the array's element count to determine the number
+          of elements" in the JVM temp generator }
+        arraytemp:=ctempcreatenode.create(arrdef,-1,tt_persistent,true);
+        addstatement(initstat,arraytemp);
+        addstatement(finistat,ctempdeletenode.create(arraytemp));
+
+        { we can also check out-parameters if we are certain that they'll be
+          valid according to the JVM. That's basically everything except for
+          local variables (fields, arrays etc are all initialized on creation) }
+        verifyout:=
+          (cs_check_var_copyout in current_settings.localswitches) and
+          ((actualtargetnode(@left)^.nodetype<>loadn) or
+           (tloadnode(actualtargetnode(@left)^).symtableentry.typ<>localvarsym));
+
+        { in case of a non-out parameter, pass in the original value (also
+          always in case of implicitpointer type, since that pointer points to
+          the data that will be changed by the callee) }
+        if (parasym.varspez<>vs_out) or
+           verifyout or
+           ((parasym.vardef.typ<>formaldef) and
+            implicitptrpara) then
+          begin
+            if implicitptrpara then
+              begin
+                { pass pointer to the struct }
+                left:=caddrnode.create_internal(left);
+                include(taddrnode(left).addrnodeflags,anf_typedaddr);
+                typecheckpass(left);
+              end;
+            { wrap the primitive type in an object container
+              if required }
+            if parasym.vardef.typ=formaldef then
+              begin
+                if (left.resultdef.typ in [orddef,floatdef]) then
+                  begin
+                    left:=cinlinenode.create(in_box_x,false,ccallparanode.create(left,nil));
+                    typecheckpass(left);
+                  end;
+                left:=ctypeconvnode.create_explicit(left,java_jlobject);
+              end;
+            { put the parameter value in the array }
+            addstatement(initstat,cassignmentnode.create(
+              cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0)),
+              left));
+            { and the copy for checking }
+            if (cs_check_var_copyout in current_settings.localswitches) then
+              addstatement(initstat,cassignmentnode.create(
+                cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(1)),
+                cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0))));
+          end
+        else
+          left.free;
+        { replace the parameter with the temp array }
+        left:=ctemprefnode.create(arraytemp);
+        { generate the code to copy back the changed value into the original
+          parameter in case of var/out.
+
+          In case of a formaldef, changes to the parameter in the callee change
+          the pointer inside the array -> we have to copy back the changes in
+          all cases.
+
+          In case of a regular parameter, we only have to copy things back in
+          case it's not an implicit pointer type. The reason is that for
+          implicit pointer types, any changes will have been directly applied
+          to the original parameter via the implicit pointer that we passed in }
+        if (parasym.varspez in [vs_var,vs_out]) and
+           ((parasym.vardef.typ=formaldef) or
+            not implicitptrpara) then
+          begin
+            { add the extraction of the parameter and assign it back to the
+              original location }
+            tempn:=ctemprefnode.create(arraytemp);
+            tempn:=cvecnode.create(tempn,genintconstnode(0));
+            { unbox if necessary }
+            if parasym.vardef.typ=formaldef then
+              begin
+                if orgparadef.typ in [orddef,floatdef] then
+                  tempn:=cinlinenode.create(in_unbox_x_y,false,ccallparanode.create(
+                    ctypenode.create(orgparadef),ccallparanode.create(tempn,nil)))
+                else if implicitptrpara then
+                  tempn:=ctypeconvnode.create_explicit(tempn,cpointerdef.getreusable(orgparadef))
+              end;
+            if implicitptrpara then
+              tempn:=cderefnode.create(tempn)
+            else
+              begin
+                { add check to determine whether the location passed as
+                  var-parameter hasn't been modified directly to a different
+                  value than the returned var-parameter in the mean time }
+                if ((parasym.varspez=vs_var) or
+                    verifyout) and
+                   (cs_check_var_copyout in current_settings.localswitches) then
+                  begin
+                    unwrappedele0:=cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0));
+                    unwrappedele1:=cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(1));
+                    if (parasym.vardef.typ=formaldef) and
+                       (orgparadef.typ in [orddef,floatdef]) then
+                      begin
+                        unwrappedele0:=cinlinenode.create(in_unbox_x_y,false,ccallparanode.create(
+                          ctypenode.create(orgparadef),ccallparanode.create(unwrappedele0,nil)));
+                        unwrappedele1:=cinlinenode.create(in_unbox_x_y,false,ccallparanode.create(
+                          ctypenode.create(orgparadef),ccallparanode.create(unwrappedele1,nil)))
+                      end;
+                    addstatement(copybackstat,cifnode.create(
+                      caddnode.create(andn,
+                        caddnode.create(unequaln,leftcopy.getcopy,ctypeconvnode.create_explicit(unwrappedele0,orgparadef)),
+                        caddnode.create(unequaln,leftcopy.getcopy,ctypeconvnode.create_explicit(unwrappedele1,orgparadef))),
+                      ccallnode.createintern('fpc_var_copyout_mismatch',
+                        ccallparanode.create(genintconstnode(fileinfo.column),
+                          ccallparanode.create(genintconstnode(fileinfo.line),nil))
+                      ),nil
+                    ));
+                  end;
+              end;
+            addstatement(copybackstat,cassignmentnode.create(leftcopy,
+              ctypeconvnode.create_explicit(tempn,orgparadef)));
+          end
+        else
+          leftcopy.free;
+        addstatement(copybackstat,finiblock);
+        firstpass(fparainit);
+        firstpass(left);
+        firstpass(fparacopyback);
+      end;
+
+
+{*****************************************************************************
+                             TWASMCALLNODE
+*****************************************************************************}
+
+    procedure twasmcallnode.wrapcomplexinlinepara(para: tcallparanode);
+      var
+        tempnode: ttempcreatenode;
+      begin
+        { don't use caddrnodes for the JVM target, because we can't take the
+          address of every kind of type (e.g., of ansistrings). A temp-reference
+          node does work for any kind of memory reference (and the expectloc
+          is LOC_(C)REFERENCE when this routine is called), but is not (yet)
+          supported for other targets }
+        tempnode:=ctempcreatenode.create_reference(para.parasym.vardef,para.parasym.vardef.size,
+          tt_persistent,tparavarsym(para.parasym).is_regvar(false),para.left,false);
+        addstatement(inlineinitstatement,tempnode);
+        addstatement(inlinecleanupstatement,ctempdeletenode.create(tempnode));
+        para.left:=ctemprefnode.create(tempnode);
+        { inherit addr_taken flag }
+        if (tabstractvarsym(para.parasym).addr_taken) then
+          tempnode.includetempflag(ti_addr_taken);
+      end;
+
+
+    procedure twasmcallnode.extra_pre_call_code;
+      begin
+        { when calling a constructor, first create a new instance, except
+          when calling it from another constructor (because then this has
+          already been done before calling the current constructor) }
+        if procdefinition.proctypeoption<>potype_constructor then
+          exit;
+        if not(methodpointer.resultdef.typ in [classrefdef,recorddef]) then
+          exit;
+        { in case of an inherited constructor call in a class, the methodpointer
+          is an objectdef rather than a classrefdef. That's not true in case
+          of records though, so we need an extra check }
+        if (current_procinfo.procdef.proctypeoption=potype_constructor) and
+           (cnf_inherited in callnodeflags) then
+          exit;
+
+        exit;
+        //current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(procdefinition.owner.defowner).jvm_full_typename(true),AT_METADATA)));
+        { the constructor doesn't return anything, so put a duplicate of the
+          self pointer on the evaluation stack for use as function result
+          after the constructor has run }
+        //current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup));
+        //thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,2);
+      end;
+
+
+    procedure twasmcallnode.set_result_location(realresdef: tstoreddef);
+      begin
+        location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1,[]);
+        { in case of jvmimplicitpointertype(), the function will have allocated
+          it already and we don't have to allocate it again here }
+        //if not jvmimplicitpointertype(realresdef) then
+          tg.gethltemp(current_asmdata.CurrAsmList,realresdef,realresdef.size,tt_normal,location.reference)
+        //else
+          //tg.gethltemp(current_asmdata.CurrAsmList,java_jlobject,java_jlobject.size,tt_normal,location.reference);
+      end;
+
+
+    procedure twasmcallnode.do_release_unused_return_value;
+      begin
+        if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
+           (current_procinfo.procdef.proctypeoption=potype_constructor) then
+          exit;
+        if is_void(resultdef) then
+          exit;
+        if (location.loc=LOC_REFERENCE) then
+          tg.ungetiftemp(current_asmdata.CurrAsmList,location.reference);
+        if assigned(funcretnode) then
+          exit;
+        //if jvmimplicitpointertype(resultdef) or
+        //   (resultdef.size in [1..4]) then
+        //  begin
+            current_asmdata.CurrAsmList.concat(taicpu.op_none(a_drop));
+            thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
+        //  end
+        //else if resultdef.size=8 then
+        //  begin
+        //    current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop2));
+        //    thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,2);
+        //  end
+        //else
+        //  internalerror(2011010305);
+      end;
+
+
+    procedure twasmcallnode.extra_post_call_code;
+      var
+        realresdef: tdef;
+      begin
+        thlcgwasm(hlcg).g_adjust_stack_after_call(current_asmdata.CurrAsmList,procdefinition,pushedparasize,typedef);
+        { a constructor doesn't actually return a value in the jvm }
+        if (tabstractprocdef(procdefinition).proctypeoption<>potype_constructor) then
+          begin
+            if cnf_return_value_used in callnodeflags then
+              begin
+                if not assigned(typedef) then
+                  realresdef:=tstoreddef(resultdef)
+                else
+                  realresdef:=tstoreddef(typedef);
+                thlcgwasm(hlcg).maybe_resize_stack_para_val(current_asmdata.CurrAsmList,realresdef,false);
+              end;
+          end;
+
+        { if this was an inherited constructor call, initialise all fields that
+          are wrapped types following it }
+        if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
+           (cnf_inherited in callnodeflags) then
+          thlcgwasm(hlcg).gen_initialize_fields_code(current_asmdata.CurrAsmList);
+      end;
+
+
+  procedure twasmcallnode.remove_hidden_paras;
+    var
+      prevpara, para, nextpara: tcallparanode;
+    begin
+      prevpara:=nil;
+      para:=tcallparanode(left);
+      while assigned(para) do
+        begin
+          nextpara:=tcallparanode(para.right);
+          if vo_is_hidden_para in para.parasym.varoptions then
+            begin
+              if assigned(prevpara) then
+                prevpara.right:=nextpara
+              else
+                left:=nextpara;
+              para.right:=nil;
+              para.free;
+            end
+          else
+            prevpara:=para;
+          para:=nextpara;
+        end;
+    end;
+
+
+  procedure twasmcallnode.gen_vmt_entry_load;
+    begin
+      { nothing to do }
+    end;
+
+
+  function twasmcallnode.pass_typecheck: tnode;
+    begin
+      result:=inherited pass_typecheck;
+      if assigned(result) or
+         codegenerror then
+        exit;
+      { unfortunately, we cannot handle a call to a virtual constructor for
+        the current instance from inside another constructor. The reason is
+        that these must be called via reflection, but before an instance has
+        been fully initialized (which can only be done by calling either an
+        inherited constructor or another constructor of this class) you can't
+        perform reflection.
+
+        Replacing virtual constructors with plain virtual methods that are
+        called after the instance has been initialized causes problems if they
+        in turn call plain constructors from inside the JDK (you cannot call
+        constructors anymore once the instance has been constructed). It also
+        causes problems regarding which other constructor to call then instead
+        before to initialize the instance (we could add dummy constructors for
+        that purpose to Pascal classes, but that scheme breaks when a class
+        inherits from a JDK class other than JLObject).
+      }
+      if (current_procinfo.procdef.proctypeoption=potype_constructor) and
+         not(cnf_inherited in callnodeflags) and
+         (procdefinition.proctypeoption=potype_constructor) and
+         (po_virtualmethod in procdefinition.procoptions) and
+         (cnf_member_call in callnodeflags) then
+        CGMessage(parser_e_jvm_invalid_virtual_constructor_call);
+    end;
+
+
+  function twasmcallnode.dispatch_procvar: tnode;
+    var
+      pdclass: tobjectdef;
+    begin
+      pdclass:=tcpuprocvardef(right.resultdef).classdef;
+      { convert procvar type into corresponding class }
+      if not tprocvardef(right.resultdef).is_addressonly then
+        begin
+          right:=caddrnode.create_internal(right);
+          include(taddrnode(right).addrnodeflags,anf_typedaddr);
+        end;
+      right:=ctypeconvnode.create_explicit(right,pdclass);
+      include(right.flags,nf_load_procvar);
+      typecheckpass(right);
+
+      { call the invoke method with these parameters. It will take care of the
+        wrapping and typeconversions; first filter out the automatically added
+        hidden parameters though }
+      remove_hidden_paras;
+      result:=ccallnode.createinternmethod(right,'INVOKE',left);
+      { reused }
+      left:=nil;
+      right:=nil;
+    end;
+
+
+  function twasmcallnode.pass_1: tnode;
+    var
+      sym: tsym;
+      wrappername: shortstring;
+    begin
+      { transform procvar calls }
+      if assigned(right) then
+        result:=dispatch_procvar
+      else
+        begin
+          { replace virtual class method and constructor calls in case they may
+            be indirect; make sure we don't replace the callthrough to the
+            original constructor with another call to the wrapper }
+          if (procdefinition.typ=procdef) and
+             not(current_procinfo.procdef.synthetickind in [tsk_callthrough,tsk_callthrough_nonabstract]) and
+             not(cnf_inherited in callnodeflags) and
+             ((procdefinition.proctypeoption=potype_constructor) or
+              (po_classmethod in procdefinition.procoptions)) and
+             (po_virtualmethod in procdefinition.procoptions) and
+             (methodpointer.nodetype<>loadvmtaddrn) then
+            begin
+              wrappername:=symtableprocentry.name+'__FPCVIRTUALCLASSMETHOD__';
+              sym:=
+                search_struct_member(tobjectdef(procdefinition.owner.defowner),
+                  wrappername);
+              if not assigned(sym) or
+                 (sym.typ<>procsym) then
+                internalerror(2011072801);
+                { do not simply replace the procsym/procdef in case we could
+                  in theory do that, because the parameter nodes have already
+                  been bound to the current procdef's parasyms }
+                remove_hidden_paras;
+                result:=ccallnode.create(left,tprocsym(sym),symtableproc,methodpointer,callnodeflags,nil);
+                result.flags:=flags;
+                left:=nil;
+                methodpointer:=nil;
+                exit;
+            end;
+          result:=inherited pass_1;
+          if assigned(result) then
+            exit;
+          { set fforcedprocname so that even virtual method calls will be
+            name-based (instead of based on VMT entry numbers) }
+          if procdefinition.typ=procdef then
+            fforcedprocname:=tprocdef(procdefinition).mangledname
+        end;
+    end;
+
+
+begin
+  ccallnode:=twasmcallnode;
+  ccallparanode:=twasmcallparanode;
+end.