@@ -712,17 +712,22 @@ TPyDelphiVarParameter = class(TPyObject)
712
712
{ Base class for exposing Records and Interfaces when Extended RTTI is available }
713
713
TPyRttiObject = class (TPyObject)
714
714
private
715
- fAddr: Pointer;
716
- fRttiType: TRttiStructuredType;
717
- function GetValue : TValue; virtual ; abstract ;
715
+ FCopy: PValue;
716
+ fAddr: Pointer;
717
+ fRttiType: TRttiStructuredType;
718
+ function GetValue : TValue; virtual ; abstract ;
719
+ function CreateCopy (const AValue: TValue): pointer;
718
720
protected
719
721
// Exposed Methods
720
722
function SetProps (args, keywords : PPyObject) : PPyObject; cdecl;
721
723
function Dir_Wrapper (args: PPyObject): PPyObject; cdecl;
722
724
public
723
725
PyDelphiWrapper : TPyDelphiWrapper;
724
726
constructor Create( APythonType : TPythonType ); override;
727
+ destructor Destroy; override;
725
728
procedure SetAddrAndType (Address: Pointer; Typ: TRttiStructuredType);
729
+ procedure SetValueAndType (const AValue: TValue;
730
+ const ACopy: boolean = false);
726
731
727
732
function GetAttrO ( key: PPyObject) : PPyObject; override;
728
733
function SetAttrO ( key, value : PPyObject) : Integer; override;
@@ -950,11 +955,16 @@ TPyDelphiWrapper = class(TEngineClient, IFreeNotificationSubscriber)
950
955
function WrapClass (AClass: TClass): PPyObject;
951
956
{ $IFDEF EXTENDED_RTTI}
952
957
// Function that provides a Python object wrapping a record
953
- function WrapRecord (Address: Pointer; Typ: TRttiStructuredType): PPyObject;
958
+ function WrapRecord (Address: Pointer; Typ: TRttiStructuredType): PPyObject; overload;
959
+ function WrapRecord (const AValue: TValue; const ACopy: boolean = false): PPyObject; overload;
954
960
// Function that provides a Python object wrapping an interface
955
961
// Note the the interface must be compiled in {$M+} mode and have a guid
956
962
// Usage: WrapInterface(TValue.From(YourInterfaceReference))
957
- function WrapInterface (const IValue: TValue): PPyObject;
963
+ // Warning: WrapInterface represents a weak (uncounted) reference!
964
+ // Use WrapInterfaceCopy to retrieve a normal counted reference
965
+ // that will keep the interface alive as long as python has a
966
+ // reference to it.
967
+ function WrapInterface (const IValue: TValue; const ACopy: boolean = false): PPyObject;
958
968
{ $ENDIF}
959
969
// properties
960
970
property EventHandlers : TEventHandlers read fEventHandlerList;
@@ -1766,7 +1776,7 @@ function SimpleValueToPython(const Value: TValue; out ErrMsg: string): PPyObject
1766
1776
tkArray, tkDynArray:
1767
1777
Result := DynArrayToPython(Value );
1768
1778
tkClass, tkMethod,
1769
- tkRecord, tkInterface,
1779
+ tkRecord, tkInterface, { $IFDEF MANAGED_RECORD } tkMRecord, { $ENDIF }
1770
1780
tkClassRef, tkPointer, tkProcedure:
1771
1781
ErrMsg := rs_ErrValueToPython;
1772
1782
else
@@ -1832,7 +1842,7 @@ function SimplePythonToValue(PyValue: PPyObject; TypeInfo: PTypeInfo;
1832
1842
Result := True;
1833
1843
end ;
1834
1844
tkClass, tkMethod, tkArray,
1835
- tkRecord, tkInterface,
1845
+ tkRecord, tkInterface,{ $IFDEF MANAGED_RECORD } tkMRecord, { $ENDIF }
1836
1846
tkClassRef, tkPointer, tkProcedure:
1837
1847
ErrMsg := rs_ErrPythonToValue;
1838
1848
else
@@ -2838,6 +2848,22 @@ function RttiCall(ParentAddress: pointer; PythonType: TPythonType;
2838
2848
Break
2839
2849
end
2840
2850
end
2851
+ else if Param.ParamType.TypeKind = tkInterface then
2852
+ begin
2853
+ if not ValidateInterfaceProperty(PyValue, Param.ParamType as TRttiInterfaceType, Args[Index], ErrMsg) then
2854
+ begin
2855
+ Result := nil ;
2856
+ Break
2857
+ end
2858
+ end
2859
+ else if Param.ParamType.TypeKind in [tkRecord{ $IFDEF MANAGED_RECORD} ,tkMRecord{ $ENDIF} ] then
2860
+ begin
2861
+ if not ValidateRecordProperty(PyValue, Param.ParamType.Handle, Args[Index], ErrMsg) then
2862
+ begin
2863
+ Result := nil ;
2864
+ Break
2865
+ end
2866
+ end
2841
2867
else if (Param.ParamType.TypeKind = tkDynArray) and PythonType.Engine.PyList_Check(PyValue) then
2842
2868
begin
2843
2869
if ParamAsDynArray(PyValue, Param, Args[Index]) then
@@ -2916,6 +2942,10 @@ function RttiCall(ParentAddress: pointer; PythonType: TPythonType;
2916
2942
Result := DelphiWrapper.Wrap(ret.AsObject)
2917
2943
else if ret.Kind = tkClassRef then
2918
2944
Result := DelphiWrapper.WrapClass(ret.AsClass)
2945
+ else if ret.Kind = tkInterface then
2946
+ Result := DelphiWrapper.WrapInterface(ret, true)
2947
+ else if ret.Kind in [tkRecord{ $IFDEF MANAGED_RECORD} ,tkMRecord{ $ENDIF} ] then
2948
+ Result := DelphiWrapper.WrapRecord(ret, true)
2919
2949
else begin
2920
2950
Result := SimpleValueToPython(ret, ErrMsg);
2921
2951
if Result = nil then
@@ -2972,7 +3002,12 @@ function GetRttiAttr(ParentAddr: Pointer; ParentType: TRttiStructuredType;
2972
3002
tkClassRef:
2973
3003
Result := PyDelphiWrapper.WrapClass(Prop.GetValue(ParentAddr).AsClass);
2974
3004
tkInterface:
2975
- Result := PyDelphiWrapper.WrapInterface(Prop.GetValue(ParentAddr));
3005
+ // Must be a copy, otherwise reference counting fails and the returned
3006
+ // interface might be freed while python is holding a reference to it
3007
+ Result := PyDelphiWrapper.WrapInterface(Prop.GetValue(ParentAddr), true);
3008
+ tkRecord{ $IFDEF MANAGED_RECORD} ,tkMRecord{ $ENDIF} :
3009
+ // Must be a copy, property getters are not allowed to leak access to underlying storage
3010
+ Result := PyDelphiWrapper.WrapRecord(Prop.GetValue(ParentAddr), true);
2976
3011
tkMethod:
2977
3012
if (ParentType is TRttiInstanceType) and (Prop is TRttiInstanceProperty) then
2978
3013
Result := PyDelphiWrapper.fEventHandlerList.GetCallable(TObject(ParentAddr),
@@ -2997,11 +3032,19 @@ function GetRttiAttr(ParentAddr: Pointer; ParentType: TRttiStructuredType;
2997
3032
tkClassRef:
2998
3033
Result := PyDelphiWrapper.WrapClass(Field.GetValue(ParentAddr).AsClass); // Returns None if Field is nil
2999
3034
tkInterface:
3000
- Result := PyDelphiWrapper.WrapInterface(Field.GetValue(ParentAddr));
3001
- tkRecord:
3035
+ // Must be a copy, otherwise reference counting fails and the returned
3036
+ // interface might be freed while python is holding a reference to it
3037
+ Result := PyDelphiWrapper.WrapInterface(Field.GetValue(ParentAddr), true);
3038
+ tkRecord{ $IFDEF MANAGED_RECORD} ,tkMRecord{ $ENDIF} :
3002
3039
if Field.FieldType is TRttiStructuredType then
3003
- // Result := PyDelphiWrapper.WrapRecord(Pointer(PPByte(ParentAddr)^ + Field.Offset), TRttiStructuredType(Field.FieldType));
3004
- Result := PyDelphiWrapper.WrapRecord(PByte(ParentAddr) + Field.Offset, TRttiStructuredType(Field.FieldType));
3040
+ // Potentially dangerous as the returned value, which is a pointer into the object,
3041
+ // could be stored on the python side, then the object freed, and the stored pointer later
3042
+ // used to access no longer allocated memory
3043
+ // But I can't see any good alternative if Python should be able to write directly into
3044
+ // fields of a record that's part of an object.
3045
+ // Maybe a relationship should be established between this wrapper and the wrapper of the parent object
3046
+ // such that a free notification on the parent object will disable access through this wrapper?
3047
+ Result := PyDelphiWrapper.WrapRecord(PByte(ParentAddr) + Field.Offset, TRttiStructuredType(Field.FieldType));
3005
3048
else
3006
3049
Result := SimpleValueToPython(Field.GetValue(ParentAddr), ErrMsg)
3007
3050
end ;
@@ -3057,7 +3100,7 @@ function SetRttiAttr(const ParentAddr: Pointer; ParentType: TRttiStructuredType
3057
3100
Prop.SetValue(ParentAddr, ValueOut);
3058
3101
Result := True;
3059
3102
end ;
3060
- tkRecord:
3103
+ tkRecord{ $IFDEF MANAGED_RECORD } ,tkMRecord { $ENDIF } :
3061
3104
if ValidateRecordProperty(Value , Prop.PropertyType.Handle, ValueOut, ErrMsg) then begin
3062
3105
Prop.SetValue(ParentAddr, ValueOut);
3063
3106
Result := True;
@@ -3107,7 +3150,7 @@ function SetRttiAttr(const ParentAddr: Pointer; ParentType: TRttiStructuredType
3107
3150
Field.SetValue(ParentAddr, ValueOut);
3108
3151
Result := True;
3109
3152
end ;
3110
- tkRecord:
3153
+ tkRecord{ $IFDEF MANAGED_RECORD } ,tkMRecord { $ENDIF } :
3111
3154
if ValidateRecordProperty(Value , Field.FieldType.Handle, ValueOut, ErrMsg) then begin
3112
3155
Field.SetValue(ParentAddr, ValueOut);
3113
3156
Result := True;
@@ -3137,6 +3180,13 @@ constructor TPyRttiObject.Create(APythonType: TPythonType);
3137
3180
PyDelphiWrapper := TPyDelphiWrapper(APythonType.Owner);
3138
3181
end ;
3139
3182
3183
+ destructor TPyRttiObject.Destroy;
3184
+ begin
3185
+ if Assigned(FCopy) then
3186
+ Dispose(FCopy);
3187
+ inherited ;
3188
+ end ;
3189
+
3140
3190
function TPyRttiObject.Dir_Wrapper (args: PPyObject): PPyObject;
3141
3191
var
3142
3192
i : Integer;
@@ -3232,6 +3282,31 @@ class procedure TPyRttiObject.SetupType(PythonType: TPythonType);
3232
3282
PythonType.Services.Basic := [bsGetAttrO, bsSetAttrO, bsRepr, bsStr];
3233
3283
end ;
3234
3284
3285
+ function TPyRttiObject.CreateCopy (const AValue: TValue): pointer;
3286
+ var
3287
+ LContext: TRttiContext;
3288
+ LRttiType: TRttiStructuredType;
3289
+ begin
3290
+ LContext := TRttiContext.Create();
3291
+ try
3292
+ LRttiType := LContext.GetType(AValue.TypeInfo) as TRttiStructuredType;
3293
+ finally
3294
+ LContext.Free;
3295
+ end ;
3296
+
3297
+ if Assigned(FCopy) then begin
3298
+ Dispose(FCopy);
3299
+ FCopy := nil ;
3300
+ end ;
3301
+
3302
+ New(FCopy);
3303
+ FCopy^ := AValue;
3304
+ Result := FCopy^.GetReferenceToRawData();
3305
+
3306
+ if LRttiType.TypeKind = tkInterface then
3307
+ Result := PPointer(Addr)^;
3308
+ end ;
3309
+
3235
3310
procedure TPyRttiObject.SetAddrAndType (Address: Pointer; Typ: TRttiStructuredType);
3236
3311
begin
3237
3312
fAddr := Address;
@@ -3240,11 +3315,36 @@ procedure TPyRttiObject.SetAddrAndType(Address: Pointer; Typ: TRttiStructuredTyp
3240
3315
fRttiType := Typ;
3241
3316
end ;
3242
3317
3318
+ procedure TPyRttiObject.SetValueAndType (const AValue: TValue;
3319
+ const ACopy: boolean);
3320
+ var
3321
+ LRttiCtx: TRttiContext;
3322
+ LRttiType: TRttiStructuredType;
3323
+ begin
3324
+ LRttiCtx := TRttiContext.Create();
3325
+ try
3326
+ LRttiType := LRttiCtx.GetType(AValue.TypeInfo) as TRttiStructuredType;
3327
+ finally
3328
+ LRttiCtx.Free();
3329
+ end ;
3330
+
3331
+ if ACopy then
3332
+ SetAddrAndType(CreateCopy(AValue), LRttiType)
3333
+ else if RttiType.TypeKind in [tkRecord{ $IFDEF MANAGED_RECORD} , tkMRecord{ $ENDIF} ] then
3334
+ SetAddrAndType(AValue.GetReferenceToRawData(), LRttiType)
3335
+ else if RttiType.TypeKind = tkInterface then
3336
+ SetAddrAndType(Pointer(AValue.GetReferenceToRawData()^), LRttiType)
3337
+ end ;
3338
+
3339
+
3243
3340
{ TPyPascalRecord }
3244
3341
3245
3342
function TPyPascalRecord.GetValue : TValue;
3246
3343
begin
3247
- TValue.Make(fAddr, RttiType.Handle, Result);
3344
+ if Assigned(fCopy) then
3345
+ Result := fCopy^
3346
+ else
3347
+ TValue.Make(fAddr, RttiType.Handle, Result);
3248
3348
end ;
3249
3349
3250
3350
class procedure TPyPascalRecord.SetupType (PythonType: TPythonType);
@@ -3258,7 +3358,10 @@ class procedure TPyPascalRecord.SetupType(PythonType: TPythonType);
3258
3358
3259
3359
function TPyPascalInterface.GetValue : TValue;
3260
3360
begin
3261
- TValue.Make(@fAddr, RttiType.Handle, Result);
3361
+ if Assigned(fCopy) then
3362
+ Result := fCopy^
3363
+ else
3364
+ TValue.Make(@fAddr, RttiType.Handle, Result);
3262
3365
end ;
3263
3366
3264
3367
class procedure TPyPascalInterface.SetupType (PythonType: TPythonType);
@@ -5261,28 +5364,52 @@ function TPyDelphiWrapper.WrapRecord(Address: Pointer; Typ: TRttiStructuredType)
5261
5364
end ;
5262
5365
end ;
5263
5366
5264
- function TPyDelphiWrapper.WrapInterface (const IValue : TValue): PPyObject;
5367
+ function TPyDelphiWrapper.WrapRecord (const AValue : TValue; const ACopy: boolean ): PPyObject;
5265
5368
var
5266
- PythonType: TPythonType;
5267
- Address: Pointer;
5268
- Typ: TRttiStructuredType;
5369
+ LPythonType: TPythonType;
5269
5370
begin
5270
- CheckEngine;
5371
+ CheckEngine();
5372
+
5373
+ if AValue.IsEmpty then begin
5374
+ Result := Engine.ReturnNone();
5375
+ Exit;
5376
+ end ;
5377
+
5378
+ LPythonType := GetHelperType(' PascalRecordType' );
5379
+ if not Assigned(LPythonType) or not (AValue.Kind in [tkRecord{ $IFDEF MANAGED_RECORD} , tkMRecord{ $ENDIF} ]) then
5380
+ begin
5381
+ Result := Engine.ReturnNone();
5382
+ Exit;
5383
+ end ;
5384
+
5385
+ Result := LPythonType.CreateInstance();
5386
+ with PythonToDelphi(Result) as TPyPascalRecord do begin
5387
+ SetValueAndType(AValue, ACopy);
5388
+ PyDelphiWrapper := Self;
5389
+ end ;
5390
+ end ;
5391
+
5392
+ function TPyDelphiWrapper.WrapInterface (const IValue: TValue; const ACopy: boolean): PPyObject;
5393
+ var
5394
+ LPythonType: TPythonType;
5395
+ begin
5396
+ CheckEngine();
5397
+
5271
5398
if IValue.IsEmpty then begin
5272
- Result := Engine.ReturnNone;
5399
+ Result := Engine.ReturnNone() ;
5273
5400
Exit;
5274
5401
end ;
5275
- PythonType := GetHelperType(' PascalInterfaceType' );
5276
- if not Assigned(PythonType) or (IValue.Kind <> tkInterface) then
5402
+
5403
+ LPythonType := GetHelperType(' PascalInterfaceType' );
5404
+ if not Assigned(LPythonType) or (IValue.Kind <> tkInterface) then
5277
5405
begin
5278
- Result := Engine.ReturnNone;
5406
+ Result := Engine.ReturnNone() ;
5279
5407
Exit;
5280
5408
end ;
5281
- Result := PythonType.CreateInstance;
5282
- Typ := TRttiContext.Create.GetType(IValue.TypeInfo) as TRttiStructuredType;
5283
- Address := Pointer(IValue.GetReferenceToRawData^);
5409
+
5410
+ Result := LPythonType.CreateInstance();
5284
5411
with PythonToDelphi(Result) as TPyPascalInterface do begin
5285
- SetAddrAndType(Address, Typ );
5412
+ SetValueAndType(IValue, ACopy );
5286
5413
PyDelphiWrapper := Self;
5287
5414
end ;
5288
5415
end ;
0 commit comments