fix(opdf): emit complete recProperty layout (method-name strings)

The OPDF recProperty record written by EmitProperties did not match the reader's
TDefProperty layout: it emitted only the property Name and omitted the
ReadMethodNameLen / WriteMethodNameLen length words and the two method-name
strings the reader expects.  A consumer (pdr) therefore read a misaligned
record — a garbage property name and an empty getter symbol — so a method-backed
property such as `property Bar: String read GetBar` could not be resolved
(`print x.Bar` reported "field or property not found").

EmitProperties now writes the full format the reader specifies: the three length
words (ReadMethodNameLen, WriteMethodNameLen, NameLen) followed by the
ReadMethodName, WriteMethodName and Name strings.  For a method-backed accessor
the method-name string is the mangled getter/setter symbol — the same symbol
written as ReadAddr/WriteAddr and emitted as the recFunctionScope name, so the
debugger can resolve it via FindFunctionByName and inject the getter call.

Verified end-to-end against the OPDF reference debugger (pdr): `print x.Bar`
now invokes the getter and prints the value; the pdr integration suite is green.
This commit is contained in:
Graeme Geldenhuys 2026-06-28 00:48:03 +01:00
parent 43a0f9e4eb
commit c6d22aac18

View file

@ -975,6 +975,7 @@ var
ReadType, WriteType: Integer;
RecSize: Integer;
RF: TFieldInfo;
ReadMethSym, WriteMethSym: string;
begin
if AClass.Properties.Count = 0 then Exit;
ClassTypeID := GetOrAllocTypeID(AClass.Name);
@ -1002,8 +1003,23 @@ begin
else
WriteType := PAT_NONE;
RecSize := 28 + Length(PI.Name);
{ 4(ClassTypeID)+4(PropTypeID)+1(ReadType)+1(WriteType)+8(ReadAddr)+8(WriteAddr)+2(NameLen) }
{ Method-backed accessors carry the getter/setter SYMBOL name so the
debugger can resolve it via FindFunctionByName (which matches against the
recFunctionScope name = the mangled symbol) and inject the call. The
symbol is the same one written as ReadAddr/WriteAddr below. }
if ReadType = PAT_METHOD then
ReadMethSym := MangledClassSym(AClass.Name) + '_' + PI.ReadMethod
else
ReadMethSym := '';
if WriteType = PAT_METHOD then
WriteMethSym := MangledClassSym(AClass.Name) + '_' + PI.WriteMethod
else
WriteMethSym := '';
{ Fixed-size payload (30 bytes) + the three variable-length strings.
4(ClassTypeID)+4(PropTypeID)+1(ReadType)+1(WriteType)+8(ReadAddr)+
8(WriteAddr)+2(ReadMethodNameLen)+2(WriteMethodNameLen)+2(NameLen). }
RecSize := 32 + Length(ReadMethSym) + Length(WriteMethSym) + Length(PI.Name);
L('');
L(' # recProperty: ' + PI.Name);
@ -1041,7 +1057,14 @@ begin
else
L(' .quad 0 # WriteAddr (none)');
EmitStrField(PI.Name);
{ The reader (TDefProperty) expects all three length words FIRST, then the
three strings in order: ReadMethodName, WriteMethodName, Name. }
L(' .word ' + IntToStr(Length(ReadMethSym)) + ' # ReadMethodNameLen');
L(' .word ' + IntToStr(Length(WriteMethSym)) + ' # WriteMethodNameLen');
L(' .word ' + IntToStr(Length(PI.Name)) + ' # NameLen');
EmitNameData(ReadMethSym);
EmitNameData(WriteMethSym);
EmitNameData(PI.Name);
end;
end;