summaryrefslogtreecommitdiff
path: root/fpcsrc/tests/webtbs/tw16592.pp
blob: 560b1d18566ccd1ae2705f806e021f502270f598 (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
{ %opt=-g-h }

program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, sysutils
  { you can add units after this };

type
  { TInterfacedObj }

  TInterfacedObj = class(TObject, IUnknown)
    private
      FOwner:TInterfacedObj;
      FDestructorCalled:boolean;

      function GetInterface(const iid: tguid; out obj): longint;
      procedure Log(const Str:string);
    protected
      FRefCount : longint;
    public
      function QueryInterface(constref iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
      function _AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
      function _Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};

      constructor Create;

      procedure AfterConstruction;override;
      procedure BeforeDestruction;override;
      class function NewInstance : TObject;override;

      property Owner:TInterfacedObj read FOwner write FOwner;
  end;


  IIntf1 = interface
    ['{EFB94FA8-4F38-4E44-8D12-74A84D07A78C}']
  end;

  IIntf2 = interface
   ['{EBC4A858-7BAC-4310-8426-E52B449D022A}']
    procedure Print;
    procedure SetI(const S:string);
  end;

  TClass1 = class(TInterfacedObj, IIntf1)

  end;

  { TClass2 }

  TClass2 = class(TInterfacedObj, IIntf2)
    i:string;
    procedure Print;
    procedure SetI(const S:string);
  end;

  TClass3 = class(TClass1, IIntf2)
    private
      FIntf2:IIntf2;
      property Intf2Prop:IIntf2 read FIntf2 implements IIntf2;
    public
      constructor Create;
  end;

{ TClass2 }

procedure TClass2.Print;
begin
  WriteLn('Print ', i);
end;

procedure TClass2.SetI(const S: string);
begin
  i:=S;
end;

  { TInterfacedObj }

  const Err = HResult($80004002);
  function TInterfacedObj.GetInterface(const iid: tguid; out obj): longint;
  begin
    if inherited GetInterface(IID, Obj) then
      Result:=0
    else
      Result:=Err;
  end;

  procedure TInterfacedObj.Log(const Str: string);
  begin
    WriteLn(Format('%s Obj=$%P class=%s RefCount=%d', [Str, Pointer(Self), ClassName, FRefCount]));
  end;

function TInterfacedObj.QueryInterface(constref iid: tguid; out obj): longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  begin
    Result:=GetInterface(iid, obj);

    //try to find interface in Owner
    if (FOwner <> nil) and (Result = Err) then
      Result:=FOwner.QueryInterface(iid, obj);
  end;

  function TInterfacedObj._AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};[public,alias:'TInterfacedObj_AddRef'];
  begin
    if not FDestructorCalled then
      begin
        _addref:=interlockedincrement(frefcount);
        Log('AddRef');

        if FOwner <> nil then
           FOwner._AddRef;
      end;
  end;

  function TInterfacedObj._Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  begin
    if FDestructorCalled then Exit;

    _Release:=interlockeddecrement(frefcount);
    Log('Release');
    if _Release=0 then
      begin
        FDestructorCalled:=True;

        Log('Destroy');
        self.destroy;
      end
      else
      if FOwner <> nil then
        FOwner._Release;
  end;

  procedure TInterfacedObj.AfterConstruction;
  begin
     { we need to fix the refcount we forced in newinstance }
     { further, it must be done in a thread safe way        }
     //declocked(frefcount);
    interlockeddecrement(frefcount);
    Log('AfterConstruction');
  end;

  procedure TInterfacedObj.BeforeDestruction;
  begin
     Log('BeforeDestruction');
     if frefcount<>0 then
       raise Exception.Create('Cannot free object still referenced.');
  end;

  class function TInterfacedObj.NewInstance : TObject;
  begin
     NewInstance:=inherited NewInstance;
     if NewInstance<>nil then
       TInterfacedObj(NewInstance).frefcount:=1;
  end;

  constructor TInterfacedObj.Create;
  begin
    FDestructorCalled:=false;
    inherited Create;
    FOwner:=nil;
  end;


{ TClass2 }

constructor TClass3.Create;
var O:TClass2;
begin
  inherited Create;
  O:=TClass2.Create;
  FIntf2:=O;
  O.Owner:=Self;

  FIntf2.SetI('AAA'); //this line is crucial for bug reproducing
end;

var O:TClass3;
    I1:IIntf1;
    I2:IIntf2;
begin
  HaltOnNotReleased := true;
  O:=TClass3.Create;
  I1:=O;

  //at this moment O object is already freed in rev.15156+ !!!
  I2:=I1 as IIntf2;
  I2.Print;
  Writeln('ok');
end.