Issue 240507.1: Add support for "properties"
Author: | Martin Friebe |
---|---|
Champion: | Adrian Prantl |
Date submitted: | 2024-05-07 |
Date revised: | |
Date closed: | 2025-03-31 |
Type: | Enhancement |
Status: | Accepted |
DWARF version: | 6 |
This is the original proposal. [ 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 be read/write, read only, write only.
Properties can have one or more array like indexes (of any type).
function GetProp(AIdx:word; AIdx2: boolean): integer;
procedure SetProp(AIdx:word; AIdx2: boolean; AVal: Integer);
property MyProp[AIdx:word; AIdx2: boolean]: integer read GetProp write SetProp;
Properties can share a method, and provide an index (constant) to identify which property called the method.
function GetProp(AIndex: Integer): integer;
procedure SetProp(AIndex: Integer; AValue: integer);
property MyProp1: integer index 1 read GetProp write SetProp;
property MyProp2: integer index 2 read GetProp write SetProp;
Properties can have a "default" flag, indicating that the array [] access can omit the property name. I.e. accessing "Object[n]" is a shortcut for the default property. (default for non-array properties is being considered for future addition)
Properties can have "default" and "stored" values for streaming (constant or via function).
Properties can be elevated to a higher visibility (private/public) in inherited classes.
There may be partial overlaps with properties in Objective-C and C#.
Proposed Changes
DW_TAG_Property
or DW_TAG_Property_Pascal
This tag can occur anywhere where DW_TAG_MEMBER
can occur. It can also
occur on a global scope
.
It supports (at least) the following existing attributes:
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
andDW_AT_decl_line
.- ... others from the var/member tags, except for actual value or location of value
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 forDW_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
- take one parameter, which is the
- A setter should
- take
_this
as the first parameter (except for global properties) - the
value
as the 2nd parameter.
- take
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 givenDW_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
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
Other usage forms of DW_TAG_Property
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
).