summaryrefslogtreecommitdiff
path: root/compiler/jvm/njvmcal.pas
blob: f1384b7dd867eb822d54c8cbc28573f2ee4a58bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
{
    Copyright (c) 2011 by Jonas Maebe

    JVM-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 njvmcal;

{$i fpcdefs.inc}

interface

    uses
      cgbase,
      symtype,symdef,
      node,ncal,ncgcal;

    type
       tjvmcallparanode = class(tcgcallparanode)
        protected
         procedure push_formal_para; override;
         procedure push_copyout_para; override;

         procedure handlemanagedbyrefpara(orgparadef: tdef); override;
       end;

       { tjvmcallnode }

       tjvmcallnode = 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,
      jvmdef;

{*****************************************************************************
                           TJVMCALLPARANODE
*****************************************************************************}

    procedure tjvmcallparanode.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 tjvmcallparanode.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 tjvmcallparanode.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;
        implicitptrpara:=jvmimplicitpointertype(orgparadef);
        { 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;


{*****************************************************************************
                             TJVMCALLNODE
*****************************************************************************}

    procedure tjvmcallnode.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 tjvmcallnode.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;
        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 tjvmcallnode.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 tjvmcallnode.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_pop));
            thlcgjvm(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 tjvmcallnode.extra_post_call_code;
      var
        realresdef: tdef;
      begin
        thlcgjvm(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);
                thlcgjvm(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
          thlcgjvm(hlcg).gen_initialize_fields_code(current_asmdata.CurrAsmList);
      end;


  procedure tjvmcallnode.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 tjvmcallnode.gen_vmt_entry_load;
    begin
      { nothing to do }
    end;


  function tjvmcallnode.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 tjvmcallnode.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 tjvmcallnode.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 foverrideprocnamedef so that even virtual method calls will be
            name-based (instead of based on VMT entry numbers) }
          if procdefinition.typ=procdef then
            foverrideprocnamedef:=tprocdef(procdefinition)
        end;
    end;


begin
  ccallnode:=tjvmcallnode;
  ccallparanode:=tjvmcallparanode;
end.