Browse Source

Correctly handle reference counted classes inside their constructors.

psub.pas:
  * generate_bodyentry_block: force the reference count of reference counted classes manually to one after the class' instance was successfully created using NewInstance
  * tcgprocinfo.maybe_add_constructor_wrapper: once construction is successfully finished we need to decrease the implicit reference count we did after NewInstance, but without destroying the class should it reference count reach 0 (for this we pass the negative offset)

git-svn-id: branches/svenbarth/arc@28868 -
svenbarth 10 years ago
parent
commit
0c9cca0a20
1 changed files with 68 additions and 17 deletions
  1. 68 17
      compiler/psub.pas

+ 68 - 17
compiler/psub.pas

@@ -278,7 +278,8 @@ implementation
         { occurs                                                            }
         { occurs                                                            }
         if (tsym(p).typ=localvarsym) and
         if (tsym(p).typ=localvarsym) and
            (tlocalvarsym(p).refs>0) and
            (tlocalvarsym(p).refs>0) and
-           is_managed_type(tlocalvarsym(p).vardef) then
+           is_managed_type(tlocalvarsym(p).vardef) and
+           not (vo_is_weakref in tlocalvarsym(p).varoptions) then
           begin
           begin
             include(current_procinfo.flags,pi_needs_implicit_finally);
             include(current_procinfo.flags,pi_needs_implicit_finally);
             if is_rtti_managed_type(tlocalvarsym(p).vardef) and
             if is_rtti_managed_type(tlocalvarsym(p).vardef) and
@@ -427,6 +428,9 @@ implementation
         call         : tcallnode;
         call         : tcallnode;
         newstatement : tstatementnode;
         newstatement : tstatementnode;
         def          : tabstractrecorddef;
         def          : tabstractrecorddef;
+        elsenode     : tnode;
+        offsetsym    : tfieldvarsym;
+        paranode     : tcallparanode;
       begin
       begin
         result:=internalstatements(newstatement);
         result:=internalstatements(newstatement);
 
 
@@ -528,12 +532,35 @@ implementation
                   calling fail instead of exit is useless because
                   calling fail instead of exit is useless because
                   there is nothing to dispose (PFV) }
                   there is nothing to dispose (PFV) }
                 if is_class_or_object(current_structdef) then
                 if is_class_or_object(current_structdef) then
-                  addstatement(newstatement,cifnode.create(
-                    caddnode.create(equaln,
-                        load_self_pointer_node,
-                        cnilnode.create),
-                    cexitnode.create(nil),
-                    nil));
+                  begin
+                    if is_class(current_structdef) and (oo_is_reference_counted in tobjectdef(current_structdef).objectoptions) then
+                      begin
+                        { if the instance was successfully created we need to
+                          force the reference count to 1 }
+                        offsetsym:=tfieldvarsym(tobjectdef(current_structdef).refcount_field);
+                        if not assigned(offsetsym) or (offsetsym.typ<>fieldvarsym) then
+                          internalerror(2014092301);
+                        elsenode:=cifnode.create(
+                          caddnode.create_internal(equaln,
+                              ctypeconvnode.create_internal(
+                                  load_vmt_pointer_node,
+                                  voidpointertype),
+                              cpointerconstnode.create(1,voidpointertype)),
+                          cassignmentnode.create(
+                            csubscriptnode.create(offsetsym,load_self_node),
+                            cordconstnode.create(1,s32inttype,false)
+                          ),
+                          nil);
+                      end
+                    else
+                      elsenode:=nil;
+                    addstatement(newstatement,cifnode.create(
+                      caddnode.create(equaln,
+                          load_self_pointer_node,
+                          cnilnode.create),
+                      cexitnode.create(nil),
+                      elsenode));
+                  end;
               end;
               end;
 
 
             { maybe call BeforeDestruction for classes }
             { maybe call BeforeDestruction for classes }
@@ -690,11 +717,15 @@ implementation
     procedure tcgprocinfo.maybe_add_constructor_wrapper(var tocode: tnode; withexceptblock: boolean);
     procedure tcgprocinfo.maybe_add_constructor_wrapper(var tocode: tnode; withexceptblock: boolean);
       var
       var
         oldlocalswitches: tlocalswitches;
         oldlocalswitches: tlocalswitches;
+        offsetsym : tfieldvarsym;
         srsym: tsym;
         srsym: tsym;
+        thenblock,
         afterconstructionblock,
         afterconstructionblock,
         exceptblock,
         exceptblock,
         newblock: tblocknode;
         newblock: tblocknode;
+        thenstatement,
         newstatement: tstatementnode;
         newstatement: tstatementnode;
+        thennode : tnode;
         pd: tprocdef;
         pd: tprocdef;
       begin
       begin
         if assigned(procdef.struct) and
         if assigned(procdef.struct) and
@@ -732,6 +763,24 @@ implementation
                         final_used:=true;
                         final_used:=true;
                       end;
                       end;
 
 
+                    thennode:=ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]);
+                    if oo_is_reference_counted in procdef.struct.objectoptions then
+                      begin
+                        thenblock:=internalstatements(thenstatement);
+                        addstatement(thenstatement,thennode);
+                        offsetsym:=tfieldvarsym(tobjectdef(current_structdef).refcount_field);
+                        if not assigned(offsetsym) or (offsetsym.typ<>fieldvarsym) then
+                          internalerror(2014092301);
+                        addstatement(thenstatement,ccallnode.createintern('fpc_refcountclass_decr_ref',
+                                 ccallparanode.create(
+                                      { tell the helper to not call Destroy }
+                                      cordconstnode.create(-offsetsym.fieldoffset,s32inttype,false),
+                                      ccallparanode.create(
+                             ctypeconvnode.create_internal(load_self_pointer_node,voidpointertype),
+                             nil))));
+                        thennode:=thenblock;
+                      end;
+
                     { Self can be nil when fail is called }
                     { Self can be nil when fail is called }
                     { if self<>nil and vmt<>nil then afterconstruction }
                     { if self<>nil and vmt<>nil then afterconstruction }
                     addstatement(newstatement,cifnode.create(
                     addstatement(newstatement,cifnode.create(
@@ -742,7 +791,7 @@ implementation
                         caddnode.create(unequaln,
                         caddnode.create(unequaln,
                           load_vmt_pointer_node,
                           load_vmt_pointer_node,
                           cnilnode.create)),
                           cnilnode.create)),
-                        ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]),
+                        thennode,
                         nil));
                         nil));
                     tocode:=afterconstructionblock;
                     tocode:=afterconstructionblock;
                   end
                   end
@@ -763,15 +812,17 @@ implementation
                     exceptblock:=internalstatements(newstatement);
                     exceptblock:=internalstatements(newstatement);
                     { first free the instance if non-nil }
                     { first free the instance if non-nil }
                     if assigned(pd) then
                     if assigned(pd) then
-                      { if vmt<>0 then call destructor }
-                      addstatement(newstatement,
-                        cifnode.create(
-                          caddnode.create(unequaln,
-                            load_vmt_pointer_node,
-                            cnilnode.create),
-                          { cnf_create_failed -> don't call BeforeDestruction }
-                          ccallnode.create(nil,tprocsym(pd.procsym),pd.procsym.owner,load_self_node,[cnf_create_failed]),
-                          nil))
+                      begin
+                        { if vmt<>0 then call destructor }
+                        addstatement(newstatement,
+                          cifnode.create(
+                            caddnode.create(unequaln,
+                              load_vmt_pointer_node,
+                              cnilnode.create),
+                            { cnf_create_failed -> don't call BeforeDestruction }
+                            ccallnode.create(nil,tprocsym(pd.procsym),pd.procsym.owner,load_self_node,[cnf_create_failed]),
+                            nil));
+                      end
                     else
                     else
                       { object without destructor, call 'fail' helper }
                       { object without destructor, call 'fail' helper }
                       addstatement(newstatement,
                       addstatement(newstatement,