An interesting edge-case in ABAP.

READ-ONLY Addition

Whereas access to attributes of a class is more conventionally controlled by member visibility (public/protected/private), ABAP has another option: the READ-ONLY addition. This addition allows an attribute to be read from outside the class, but not altered, somewhat like if there was a getter for the attribute but no setter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
CLASS class DEFINITION.

  PUBLIC SECTION.
    CLASS-DATA read_only_attribute TYPE i VALUE 10 READ-ONLY.

ENDCLASS.

CLASS class IMPLEMENTATION.
ENDCLASS.


START-OF-SELECTION.
  " Allowed
  DATA(lv_attribute_value) = class=>read_only_attribute.

  " Produces the following compilation error:
  " "Write access to the READ-ONLY attribute "READ_ONLY_ATTRIBUTE" is not allowed
  "  outside the class/interface"
  class=>read_only_attribute = 20.

Pointers

ABAP also has pointers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
DATA lv_integer TYPE i VALUE 10.


DATA lvr_integer TYPE REF TO i.

lvr_integer = REF #( lv_integer ).
lvr_integer->* = 20.


WRITE lv_integer. " Will print 20

If you squint, you can kinda, sorta see how the pointer syntax resembles C. The likeness is move obvious when the fields of a structure are accessed via a pointer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
TYPES: BEGIN OF ty_s_structure,
         member1 TYPE string,
         member2 TYPE string,
       END OF ty_s_structure.

DATA ls_structure TYPE ty_s_structure.


DATA(lsr_structure) = REF #( ls_structure ).
lsr_structure->member1 = 'Hello,'.
lsr_structure->member2 = 'world!'.


WRITE: ls_structure-member1, ls_structure-member2. " Will print "Hello, world!"

As is typical, the pointer does not include any concept of access restriction. If you have the pointer, you can access and alter the referenced value as you please; if a method returns a pointer to a private attribute, anyone with the pointer can alter the value without restrictions. If you want to control access to a value or otherwise provide encapsulation, you create a class to wrap the value and to enforce invariants.

Combining READ-ONLY and Pointers

What if we combine the write-protected READ-ONLY attributes and pointers? Since pointers don’t in any way model access restrictions, the combination will at the very least be interesting:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CLASS class DEFINITION.

  PUBLIC SECTION.
    CLASS-DATA read_only_attribute TYPE i VALUE 10 READ-ONLY.

ENDCLASS.

CLASS class IMPLEMENTATION.
ENDCLASS.


START-OF-SELECTION.
  DATA(lvr_read_only_attribute) = REF #( class=>read_only_attribute ).

  lvr_read_only_attribute->* = 20.

The above compiles, but will produce the following rare exception when run:

Overwriting a protected field

(I especially like the Error analysis part)

This is a perfectly sane result, since I think crashing is preferable to subtly corrupting data via unintended accesses. It also highlights the fact that the type system in ABAP cannot detect this type of error statically: since read-only and read-write pointers are not separate types, all otherwise valid pointer accesses will be accepted by the compiler. At runtime, however, some pointers cannot in fact be used for writes, crashing the program.

Less of an edge-case, accessing a constant in the same manner also produces the same error:

1
2
3
4
5
CONSTANTS con_integer TYPE i VALUE 10.


DATA(lvr_integer) = REF #( con_integer ).
lvr_integer->* = 20.

Which is more or less how the same would work in C (though here we knowingly cast the pointer to discard the const qualifier):

1
2
3
4
5
6
7
const int count = 10;

int main(void) {
    int *i = (int *)&count;
    
    *i = 20;
}

Which, when run, produces the informative error (or, I guess, we are lucky it produces this error. For all I know, this could be another instance of undefined behavior):

Bus error: 10

Obviously it would be preferable that the type system would catch these errors also, but it’s not the gravest sin since write-protected pointers are an exception rather than a frequent occurrence. But that, of course, makes these errors all the more cryptic when they are encountered once in a blue moon (“What even is a ‘protected field’?").