From c6d22aac1825ffbbbe79c14a91db07ab3f0a8c84 Mon Sep 17 00:00:00 2001 From: Graeme Geldenhuys Date: Sun, 28 Jun 2026 00:48:03 +0100 Subject: [PATCH] fix(opdf): emit complete recProperty layout (method-name strings) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- compiler/src/main/pascal/uDebugOPDF.pas | 29 ++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/compiler/src/main/pascal/uDebugOPDF.pas b/compiler/src/main/pascal/uDebugOPDF.pas index 235c0a4..1e35ae1 100644 --- a/compiler/src/main/pascal/uDebugOPDF.pas +++ b/compiler/src/main/pascal/uDebugOPDF.pas @@ -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;