Issue 240507.1: Add support for "properties"
| Author: | Martin Friebe |
|---|---|
| Champion: | Adrian Prantl |
| Date submitted: | 2024-05-07 |
| Date revised: | 2024-11-25 |
| Date closed: | 2025-03-31 |
| Type: | Enhancement |
| Status: | Accepted |
| DWARF version: | 6 |
Comparing original with 2024-11-25. [ View this version ] [ Return to the latest version ]
## Background
Pascal has a property construct, that allows a "variable like"
identifier, which can either point to a field (member variable) or a
getter/setter function.
TFoo = class
FField: integer;
function GetProp: integer;
procedure SetProp(AVal: Integer);
property MyProp: integer read GetProp write SetProp;
property MyOtherProp: integer read FField;
end;
Properties can exist in a structure, interface, or on a global level.
* Properties can exist in a structure, interface, or on a global level.
Properties can be read/write, read only, write only.
* Properties can be read/write, read only, write only.
Properties can have one or more array like indexes (of any type).
* Properties can have one or more array like indexes (of any type).
function GetProp(AIdx:word; AIdx2: boolean): integer;
function GetProp(AIdx:word; AIdx2: boolean): integer;
procedure SetProp(AIdx:word; AIdx2: boolean; AVal: Integer);
procedure SetProp(AIdx:word; AIdx2: boolean; AVal: Integer);
property MyProp[AIdx:word; AIdx2: boolean]: integer read GetProp write SetProp;
property MyProp[AIdx:word; AIdx2: boolean]: integer read GetProp write SetProp;
Properties can share a method, and provide an index (constant) to
* Properties can share a method, and provide an index (constant) to
identify which property called the method.
identify which property called the method.
function GetProp(AIndex: Integer): integer;
function GetProp(AIndex: Integer): integer;
procedure SetProp(AIndex: Integer; AValue: integer);
procedure SetProp(AIndex: Integer; AValue: integer);
property MyProp1: integer index 1 read GetProp write SetProp;
property MyProp1: integer index 1 read GetProp write SetProp;
property MyProp2: integer index 2 read GetProp write SetProp;
property MyProp2: integer index 2 read GetProp write SetProp;
Properties can have a "default" flag, indicating that the array []
* Properties can have a "default" flag, indicating that the array []
access can omit the property name. I.e. accessing "Object[n]" is a
access can omit the property name. I.e. accessing "Object[n]" is a
shortcut for the default property. (default for non-array properties is
shortcut for the default property. (default for non-array properties is
being considered for future addition)
being considered for future addition)
Properties can have "default" and "stored" values for streaming
* Properties can have "default" and "stored" values for streaming
(constant or via function).
(constant or via function).
Properties can be elevated to a higher visibility (private/public) in
* Properties can be elevated to a higher visibility (private/public) in
inherited classes.
inherited classes.
There may be partial overlaps with properties in Objective-C and C#.
### References - [FreePascal property doc][freepascal] - [Oxygene][oxygene] - [Apple extension for Objective-C][objc] [freepascal]: https://www.freepascal.org/docs-html/ref/refse27.html [oxygene]: https://docs.elementscompiler.com/Oxygene/Members/Properties/ [objc]: https://github.com/llvm/llvm-project/blob/main/llvm/docs/SourceLevelDebugging.rst#debugging-information-format-1
## Proposed Changes
### `DW_TAG_Property` or `DW_TAG_Property_Pascal`
### Section 2.1: The Debugging Information Entry
This tag can occur anywhere where `DW_TAG_MEMBER` can occur. It can also occur on a `global scope`.
In Table 2.1, add `DW_TAG_property`, `DW_TAG_property_getter`, `DW_TAG_property_setter`, and `DW_TAG_property_stored`.
It supports (at least) the following existing attributes:
### Section 5.7: Structure, Union and Class Type Entries
Add the following new subsection after Section 5.7.6 Data Member Entries:
* `DW_AT_Name` * `DW_AT_Type` * `DW_AT_Accessibility` * `DW_AT_external` * `DW_AT_virtuality` * `DW_AT_start_scope` * `DW_AT_decl_column`, `DW_AT_decl_file` and `DW_AT_decl_line`. * ... others from the var/member tags, except for actual value or location of value
> 5.7.x Property Entries > > [Non-normative] Many object-oriented languages like Pascal and > Objective-C have properties, which are member functions that > syntactically behave like data members of an object. Pascal can also > have global properties. > > A property is represented by a debugging information entry with the > tag `DW_TAG_property`. A property entry has a `DW_AT_name` string > attribute whose value is the property name. A property entry may have > a `DW_AT_type` attribute to denote the type of that property. > > A property may have `DW_AT_accessibility`, `DW_AT_external`, > `DW_AT_virtuality`, `DW_AT_start_scope`, `DW_AT_decl_column`, > `DW_AT_decl_file` and `DW_AT_decl_line` attributes with the respective > semantics described for these attributes for `DW_TAG_member` (see Section 5.7.6). > > A property may have one or several of `DW_TAG_property_getter`, > `DW_TAG_property_setter`, or `DW_TAG_property_stored` children to > represent the getter and setter (member) functions, or the Pascal-style > `stored` accessor for this property. Each of these tags have a > `DW_AT_property_forward` attribute to point to a (member) function > declaration or a data member. If they point to a function, they may also > have `DW_TAG_formal_parameter` children (matching the ones in the > function) that can have `DW_AT_default_value` attributes to declare > additional default arguments for when these functions are used as > property accessors. > > Some languages can automatically derive accessors for properties from a > field in property's parent object. In such cases the > `DW_AT_property_forward` attribute of the accessor entry points to the > `DW_TAG_property`'s sibling `DW_TAG_member` entry of field that holds > the properties underlying storage. > > Property accessors may also have any other attributes allowed in a > `DW_TAG_subprogram` function declaration. If the value of a property can > be derived by evaluating a DWARF expression, the > `DW_TAG_property_getter` may have a `DW_TAG_location` holding a DWARF > expression that may use `DW_OP_push_object_address` to inquire the > address of the property's parent object. > > To change the accessibility of a property in an inherited class, a > `DW_TAG_property` can be specified with just a name and accessibility. > For example if in a subclass property a becomes private it is sufficient > to add the following to the subclass entry: > > DW_TAG_property > DW_AT_name "a" > DW_AT_accessibility DW_ACCESS_private
It will support the following new attribute:
* `DW_AT_Default_Property` flag
Specify this is a default property
It will support the following new tags as children:
* `DW_TAG_Property_Setter`
* `DW_TAG_Property_Reader`
* `DW_TAG_Property_Default`
* `DW_TAG_Property_Stored`
### `DW_TAG_Property_[Setter|Getter|...]`
Specifies how the property is accessed for read/write and other access.
#### Reference to a field/var/function.
* `DW_AT_Property_Forward` reference/constant
A reference to an existing the field or function.
* This could be a constant for the reader/default/stored accessors.
* This could be a string to support the current `AT_APPLE_property_setter` extension
* `DW_AT_Property_Object` reference/expression/constant
The object on which the value is stored (value for
`DW_OP_push_object_address`). This can be omitted for inherited classes,
if it computes to the same address as the current class.
* `DW_TAG_Member`, `DW_TAG_Variable`, `DW_TAG_subprogram`.
For inlining the accessor. E.g. subprogram for C#
The referenced element must have the same `DW_AT_Type` as the property.
Except for "stored" which should be boolean.
#### function parameters
* A getter should:
* take one parameter, which is the `_this` value of the object. For
global properties no parameters are given
* A setter should
* take `_this` as the first parameter (except for global properties)
* the `value` as the 2nd parameter.
For shared/indexed properties the order of parameters should default to
* `_this`
* indexes from array-like access
* the `value` (setter only)
For the **shared getter/setter index** the value of the argument must be
specified. For this the list of arguments to a getter/setter can be
specified.
* `DW_TAG_Property_Argument_List`
* containing a list of `DW_TAG_Property_Argument`
* each having the following attributes.
* `DW_AT_Property_Argument_Number`
zero-based number of the function parameter for which a value is given
* `DW_AT_Property_Argument_Value` reference or constant or expression
(returning value)
The value that should be passed
For the position of the `value` in the parameter list
* `DW_AT_Property_Value_Argument_Number` const/expression
The values for `_this`, array-index and value (if not specified) are
passed to the function remaining parameters.
#### Other attributes for getter/setter/default/stored
### Appendix D: Examples
Add a new subsection:
The following attributes should be allowed in `DW_TAG_Property_[Setter|Getter|...]` to overwrite value given in the `DW_TAG_Property`
* `DW_AT_Accessibility` * `DW_AT_external` * `DW_AT_virtuality` * `DW_AT_start_scope`
> D.x Properties > > The properties in the Pascal object in this example are represented by > the following DWARF. > > TClass = class > FField: integer; > function GetProp: integer; > procedure SetProp(AVal: Integer); > property MyProp: integer read GetProp write SetProp; > property MyOtherProp: integer read FField; > function GetFoo(x: word; AIndex: Integer): char; > property Foo[x: word]: char index 1 read GetFoo; > end; > > > DW_TAG_class_type > DW_AT_name "TClass" > DW_TAG_member > DW_AT_name "FField" > DW_AT_type <ref to integer> > DW_TAG_subprogram > DW_AT_name "GetProp" > ... > DW_TAG_subprogram > DW_AT_name "SetProp" > ... > > DW_TAG_property > DW_AT_name "MyProp" > DW_TAG_property_getter > DW_AT_property_forward <ref to GetProp> > DW_TAG_property_setter > DW_AT_property_forward <ref to SetProp> > > DW_TAG_property > DW_AT_name "MyOtherProp" > DW_TAG_property_getter > DW_AT_property_forward <ref to FField> > > DW_TAG_subprogram > DW_AT_name "GetFoo" > ... > > DW_TAG_property > DW_AT_name "Foo" > > DW_TAG_property_getter > DW_AT_property_forward <ref to GetFoo> > > DW_TAG_formal_parameter ; _this (no default specified, details inherited from GetFoo > DW_TAG_formal_parameter ; x (no default specified) > DW_TAG_formal_parameter > DW_AT_default_value <DW_OP_lit 1> ; property index
### Other usage forms of `DW_TAG_Property`
---
2024-10-12: Revised after online discussion.
To change accessibility (private/public) of a property in an inherited
class, `DW_TAG_Property` will be specified with just a name and accessibility.
DW_TAG_PROPERTY
DW_AT_NAME
DW_AT_ACCESSIBILITY
In more generic terms, a property that has no getter or setter, and is
not "abstract" is modifying an inherited property. Either accessibility,
or addifng "default" or "stored". (This could alternatively be done by
specifing a new attribute `DW_AT_Property_Inherhit`)
### Possible compatibility to existing `AT_APPLE_property` extension
If a property getter/setter (but not stored/default) only needs
`DW_AT_Property_Forward` then instead of having each of them in a
`DW_TAG_Property_[Setter|Getter]` there could be attributes in
`DW_TAG_Property`: `DW_AT_Property_Forward_Setter` and
`DW_AT_Property_Forward_Getter`
## Examples
### Property with different visibility
type
TFoo = class
private
MyMember: integer;
function MyFunc: integer;
protected
// "read public" is currently from oxygene, but may be added to FreePascal
property MyProp: integer read public MyFunc write MyMember; default;
end;
TBar = class(TFoo)
public
property MyProp; // elevate to public
end;
DW_TAG_Structure_type
DW_AT_Name : "TFoo"
L1:
DW_TAG_Member
DW_AT_Name : "MyMember"
DW_AT_Type : <...>
DW_AT_Member_location : <...>
L2:
DW_TAG_subprogram
DW_AT_Name : "MyFunc"
DW_AT_Type : <...>
DW_TAG_Property
DW_AT_Name : "MyProp"
DW_AT_Type : <...>
DW_AT_Accessibility : DW_ACCESS_protected
DW_AT_Default_Property : TRUE
DW_TAG_Property_Getter
DW_AT_Property_Forward : reference to L2
DW_AT_Accessibility : DW_ACCESS_public
DW_TAG_Property_Setter
DW_AT_Property_Forward : reference to L1
DW_TAG_Structure_type
DW_AT_Name : "TBar"
DW_TAG_Inheritance
<...>
DW_TAG_Property
DW_AT_Name : "MyProp"
DW_AT_Accessibility : DW_ACCESS_public
### Property with access to nested field
type
TFoo = class
OtherData: DWORD;
FNested: record
MyMember: integer;
end;
property MyProp: integer read FNested.MyMember;
end;
L1:
DW_TAG_Structure_type
L2:
DW_TAG_Member
DW_AT_Name : "MyMember"
DW_AT_Type : <...>
DW_AT_Member_location : <...> ! inside FNested
DW_TAG_Structure_type
DW_AT_Name : "TFoo"
DW_TAG_Member
DW_AT_Name : "OtherData"
DW_TAG_Member
DW_AT_Name : "FNested"
DW_AT_Type : reference to L1
DW_AT_Member_location :
DW_OP_plus_uconst 4 ! where 4 == offset of MyMember in the instance data
DW_TAG_Property
DW_AT_Name : "MyProp"
DW_AT_Type : <...>
DW_TAG_Property_Getter
DW_AT_Property_Forward : reference to L2
DW_AT_Property_Object :
DW_OP_push_object_address ! maybe should be on stack by default
DW_OP_plus_uconst 4 ! where 4 == offset of MyMember in the instance data
! There could be several levels of nesting,
! so that expression could be more complex
In the example the property does not have a reference to `FNested` itself.
All it needs is the `object_address` of `FNested`, so it can calculate the
location of the referenced field `MyMember` (using the `member_location`).
## References
- [FreePascal property doc][1]
[1]: https://www.freepascal.org/docs-html/ref/refse27.html
- [Oxygene][2]
[2]: https://docs.elementscompiler.com/Oxygene/Members/Properties/
- [APPLE extension for Objective-C][3]
[3]: https://github.com/llvm/llvm-project/blob/main/llvm/docs/SourceLevelDebugging.rst#debugging-information-format-1