Skip to content

Commit cef7271

Browse files
authored
Merge pull request #72 from Embarcadero/prmerge
Partially merging #5
2 parents 320f920 + 6118bb4 commit cef7271

File tree

1 file changed

+156
-29
lines changed

1 file changed

+156
-29
lines changed

Source/WrapDelphi.pas

Lines changed: 156 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -712,17 +712,22 @@ TPyDelphiVarParameter = class(TPyObject)
712712
{ Base class for exposing Records and Interfaces when Extended RTTI is available }
713713
TPyRttiObject = class (TPyObject)
714714
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;
718720
protected
719721
// Exposed Methods
720722
function SetProps(args, keywords : PPyObject) : PPyObject; cdecl;
721723
function Dir_Wrapper(args: PPyObject): PPyObject; cdecl;
722724
public
723725
PyDelphiWrapper : TPyDelphiWrapper;
724726
constructor Create( APythonType : TPythonType ); override;
727+
destructor Destroy; override;
725728
procedure SetAddrAndType(Address: Pointer; Typ: TRttiStructuredType);
729+
procedure SetValueAndType(const AValue: TValue;
730+
const ACopy: boolean = false);
726731

727732
function GetAttrO( key: PPyObject) : PPyObject; override;
728733
function SetAttrO( key, value: PPyObject) : Integer; override;
@@ -950,11 +955,16 @@ TPyDelphiWrapper = class(TEngineClient, IFreeNotificationSubscriber)
950955
function WrapClass(AClass: TClass): PPyObject;
951956
{$IFDEF EXTENDED_RTTI}
952957
// 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;
954960
// Function that provides a Python object wrapping an interface
955961
// Note the the interface must be compiled in {$M+} mode and have a guid
956962
// 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;
958968
{$ENDIF}
959969
// properties
960970
property EventHandlers : TEventHandlers read fEventHandlerList;
@@ -1766,7 +1776,7 @@ function SimpleValueToPython(const Value: TValue; out ErrMsg: string): PPyObject
17661776
tkArray, tkDynArray:
17671777
Result := DynArrayToPython(Value);
17681778
tkClass, tkMethod,
1769-
tkRecord, tkInterface,
1779+
tkRecord, tkInterface, {$IFDEF MANAGED_RECORD} tkMRecord,{$ENDIF}
17701780
tkClassRef, tkPointer, tkProcedure:
17711781
ErrMsg := rs_ErrValueToPython;
17721782
else
@@ -1832,7 +1842,7 @@ function SimplePythonToValue(PyValue: PPyObject; TypeInfo: PTypeInfo;
18321842
Result := True;
18331843
end;
18341844
tkClass, tkMethod, tkArray,
1835-
tkRecord, tkInterface,
1845+
tkRecord, tkInterface,{$IFDEF MANAGED_RECORD} tkMRecord,{$ENDIF}
18361846
tkClassRef, tkPointer, tkProcedure:
18371847
ErrMsg := rs_ErrPythonToValue;
18381848
else
@@ -2838,6 +2848,22 @@ function RttiCall(ParentAddress: pointer; PythonType: TPythonType;
28382848
Break
28392849
end
28402850
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
28412867
else if (Param.ParamType.TypeKind = tkDynArray) and PythonType.Engine.PyList_Check(PyValue) then
28422868
begin
28432869
if ParamAsDynArray(PyValue, Param, Args[Index]) then
@@ -2916,6 +2942,10 @@ function RttiCall(ParentAddress: pointer; PythonType: TPythonType;
29162942
Result := DelphiWrapper.Wrap(ret.AsObject)
29172943
else if ret.Kind = tkClassRef then
29182944
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)
29192949
else begin
29202950
Result := SimpleValueToPython(ret, ErrMsg);
29212951
if Result = nil then
@@ -2972,7 +3002,12 @@ function GetRttiAttr(ParentAddr: Pointer; ParentType: TRttiStructuredType;
29723002
tkClassRef:
29733003
Result := PyDelphiWrapper.WrapClass(Prop.GetValue(ParentAddr).AsClass);
29743004
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);
29763011
tkMethod:
29773012
if (ParentType is TRttiInstanceType) and (Prop is TRttiInstanceProperty) then
29783013
Result := PyDelphiWrapper.fEventHandlerList.GetCallable(TObject(ParentAddr),
@@ -2997,11 +3032,19 @@ function GetRttiAttr(ParentAddr: Pointer; ParentType: TRttiStructuredType;
29973032
tkClassRef:
29983033
Result := PyDelphiWrapper.WrapClass(Field.GetValue(ParentAddr).AsClass); // Returns None if Field is nil
29993034
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}:
30023039
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));
30053048
else
30063049
Result := SimpleValueToPython(Field.GetValue(ParentAddr), ErrMsg)
30073050
end;
@@ -3057,7 +3100,7 @@ function SetRttiAttr(const ParentAddr: Pointer; ParentType: TRttiStructuredType
30573100
Prop.SetValue(ParentAddr, ValueOut);
30583101
Result := True;
30593102
end;
3060-
tkRecord:
3103+
tkRecord{$IFDEF MANAGED_RECORD},tkMRecord{$ENDIF}:
30613104
if ValidateRecordProperty(Value, Prop.PropertyType.Handle, ValueOut, ErrMsg) then begin
30623105
Prop.SetValue(ParentAddr, ValueOut);
30633106
Result := True;
@@ -3107,7 +3150,7 @@ function SetRttiAttr(const ParentAddr: Pointer; ParentType: TRttiStructuredType
31073150
Field.SetValue(ParentAddr, ValueOut);
31083151
Result := True;
31093152
end;
3110-
tkRecord:
3153+
tkRecord{$IFDEF MANAGED_RECORD},tkMRecord{$ENDIF}:
31113154
if ValidateRecordProperty(Value, Field.FieldType.Handle, ValueOut, ErrMsg) then begin
31123155
Field.SetValue(ParentAddr, ValueOut);
31133156
Result := True;
@@ -3137,6 +3180,13 @@ constructor TPyRttiObject.Create(APythonType: TPythonType);
31373180
PyDelphiWrapper := TPyDelphiWrapper(APythonType.Owner);
31383181
end;
31393182

3183+
destructor TPyRttiObject.Destroy;
3184+
begin
3185+
if Assigned(FCopy) then
3186+
Dispose(FCopy);
3187+
inherited;
3188+
end;
3189+
31403190
function TPyRttiObject.Dir_Wrapper(args: PPyObject): PPyObject;
31413191
var
31423192
i : Integer;
@@ -3232,6 +3282,31 @@ class procedure TPyRttiObject.SetupType(PythonType: TPythonType);
32323282
PythonType.Services.Basic := [bsGetAttrO, bsSetAttrO, bsRepr, bsStr];
32333283
end;
32343284

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+
32353310
procedure TPyRttiObject.SetAddrAndType(Address: Pointer; Typ: TRttiStructuredType);
32363311
begin
32373312
fAddr := Address;
@@ -3240,11 +3315,36 @@ procedure TPyRttiObject.SetAddrAndType(Address: Pointer; Typ: TRttiStructuredTyp
32403315
fRttiType := Typ;
32413316
end;
32423317

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+
32433340
{ TPyPascalRecord }
32443341

32453342
function TPyPascalRecord.GetValue: TValue;
32463343
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);
32483348
end;
32493349

32503350
class procedure TPyPascalRecord.SetupType(PythonType: TPythonType);
@@ -3258,7 +3358,10 @@ class procedure TPyPascalRecord.SetupType(PythonType: TPythonType);
32583358

32593359
function TPyPascalInterface.GetValue: TValue;
32603360
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);
32623365
end;
32633366

32643367
class procedure TPyPascalInterface.SetupType(PythonType: TPythonType);
@@ -5261,28 +5364,52 @@ function TPyDelphiWrapper.WrapRecord(Address: Pointer; Typ: TRttiStructuredType)
52615364
end;
52625365
end;
52635366

5264-
function TPyDelphiWrapper.WrapInterface(const IValue: TValue): PPyObject;
5367+
function TPyDelphiWrapper.WrapRecord(const AValue: TValue; const ACopy: boolean): PPyObject;
52655368
var
5266-
PythonType: TPythonType;
5267-
Address: Pointer;
5268-
Typ: TRttiStructuredType;
5369+
LPythonType: TPythonType;
52695370
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+
52715398
if IValue.IsEmpty then begin
5272-
Result := Engine.ReturnNone;
5399+
Result := Engine.ReturnNone();
52735400
Exit;
52745401
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
52775405
begin
5278-
Result := Engine.ReturnNone;
5406+
Result := Engine.ReturnNone();
52795407
Exit;
52805408
end;
5281-
Result := PythonType.CreateInstance;
5282-
Typ := TRttiContext.Create.GetType(IValue.TypeInfo) as TRttiStructuredType;
5283-
Address := Pointer(IValue.GetReferenceToRawData^);
5409+
5410+
Result := LPythonType.CreateInstance();
52845411
with PythonToDelphi(Result) as TPyPascalInterface do begin
5285-
SetAddrAndType(Address, Typ);
5412+
SetValueAndType(IValue, ACopy);
52865413
PyDelphiWrapper := Self;
52875414
end;
52885415
end;

0 commit comments

Comments
 (0)