METHOD run.
me->ref_test( ).
WRITE me->class_ref->*. "Crashes
"me->class_ref is not initial but points to freed
"stack memory and is therefore not usable
ENDMETHOD.
METHOD ref_test.
DATA(my_local_var) = 42.
GET REFERENCE OF my_local_var INTO DATA(my_ref).
me->class_ref = my_ref.
WRITE me->class_ref->*. "Works
ENDMETHOD.
METHOD run.
me->ref_test( ).
WRITE me->class_ref->*. "Works, as the referenced variable
"lies on the heap and wasn't cleared
ENDMETHOD.
METHOD ref_test.
DATA my_ref TYPE REF TO i.
CREATE DATA my_ref. "ABAP allocates heap memory for an int and
"returns the pointer into my_ref
me->class_ref = my_ref.
WRITE me->class_ref->*. "Works
ENDMETHOD.
VALUE()
-syntax in the method header).METHOD run.
DATA(my_line_ref) = me->get_line( me->huge_table ).
WRITE my_line_ref->*. "Works
"The ref points to some line inside of
"me->huge_table and therefore to the heap
ENDMETHOD:
" --> table TYPE TABLE OF i
" <<< line_ref TYPE REF TO i
METHOD get_line.
READ TABLE table INDEX 1337 REFERENCE INTO line_ref.
ENDMETHOD.
get_line
, we're passing a huge table. But as parameters are passed by ref, the table isn't copied and just a pointer is passed to the method. That means, table
points to the same memory area me->huge_table
does.me->huge_table
.SYSTEM_DATA_ALREADY_FREE
. That error occurs, when you try to dereference a pointer which points to freed/cleared/invalidated stack memory like in the first example. But I knew that I haven't grabbed pointers to any stack variable as I'm paying attention to create all data, I want to share, directly inside the heap.METHOD run.
DATA(line_ref) = me->get_line( ).
"Do something with line_ref
ENDMETHOD.
" <<< line_ref TYPE REF TO gtype_line
METHOD get_line.
line_ref = me->read_line( me->get_some_object( )->some_table ).
ENDMETHOD.
" --> table TYPE TABLE OF gtype_line
" <<< line_ref TYPE REF TO gtype_line
METHOD read_line.
READ TABLE table INDEX 1337 REFERENCE INTO line_ref.
ENDMETHOD.
get_line
only combines read_line
with an attribute of some object. The object is obtained by calling me->get_some_object( )
and from that object, the attribute some_table
is accessed inline and passed as parameter to read_line
. As parameters are passed by ref, I don't have to worry about the ref returned by read_line
as I passed a variable from heap and therefore the ref also points to the heap.line_ref
in the run
method. So I jumped into the debugger to look what's going on there. It turned out, that the ref returned by get_line
wasn't bound and pointed to freed memory. Although not a single stack variable was involved!read_line
method returns.read_line
uses a variable from heap to grab a reference! Does the garbage collector free heap memory even if there are references?" <<< line_ref TYPE REF TO gtype_line
METHOD get_line.
DATA(some_object) = me->get_some_object( ).
line_ref = me->read_line( some_object->some_table ).
ENDMETHOD.
get_some_object
.read_line
). But that would break the fundamental rule of ABAP method calling! Method parameters aren't passed by val! They aren't copied into the stack! And why does this behaviour only appear, when directly accessing attributes from returned objects inline, but not when accessing them through a variable?METHOD get_line.
DATA(some_object) = me->get_some_object( ).
me->read_line( some_object->some_table ).
me->read_line( me->get_some_object( )->some_table ).
ENDMETHOD.
" --> table TYPE TABLE OF gtype_line
METHOD read_line.
DATA(obj) = me->get_some_object( ).
INSERT VALUE( ... ) INTO TABLE obj->some_table.
ENDMETHOD.
read_line
, we modify the table that was passed to us. But we don't modify it through the parameter table
, but through the object obtained by me->get_some_object( )
. As table
was passed by ref, it points to the same memory area. That means, when modifying obj->some_table
, we should see the change also in table
.some_object->some_table
was passed confirmed, that our test was working. The debugger showed that the table behind both variables table
and obj->some_table
growed as the table was passed by ref and it's one single table which is referenced by both variables.me->get_some_object( )->some_table
and we waited for the debugger to again display the change of the table behind the both variables.obj->some_table
growed, table
kept the state the table had before modifying it. And that means: table
MUST be a copy of the original table! ABAP must have copied the memory area of the table! That also explained, why the reference becomes invalid as soon as the method is left. As the table table
was copied, it's now lying in the stack of the method, the pointer grabbed with READ TABLE
also points to the stack. To a stack area, which gets cleared when the method returns to its caller.If an attribute is passed to a method in whose access-chain the return value of a method is used, the content of that attribute is passed by val instead of by ref
RETURNING VALUE(...)
in ABAP as it's just not possible to internally return a pointer like for the IMPORTING
-parameters. Other programming languages don't have such a VALUE(...)
syntax as they always copy both method parameters and return values. So, when using return-values directly as method parameters, it's not noticed, whether the value is copied one time or two times. It only matters, if the method returns a pointer and that pointer is dereferenced directly. The pointer itself was the return value and therefore copied, but the data behind the pointer stayed the same. Even if the pointer was copied, it still pointed to the same memory area.CLASS my_class DEFINITION.
PUBLIC SECTION.
DATA:
table TYPE string_table,
self TYPE REF TO my_class.
METHODS:
get_me
RETURNING VALUE(obj1) TYPE REF TO my_class,
get_line
IMPORTING tab TYPE string_table
RETURNING VALUE(ref1) TYPE REF TO string.
ENDCLASS.
CLASS my_class IMPLEMENTATION.
" <<< obj1 TYPE REF TO my_class
METHOD get_me.
obj1 = me.
ENDMETHOD.
" --> tab TYPE string_table
" <<< ref1 TYPE REF TO string
METHOD get_line.
"This line should modify both me->table and tab
INSERT `new line` INTO TABLE me->table.
READ TABLE tab INDEX 1 REFERENCE INTO ref1.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA:
ref1 TYPE REF TO string,
obj1 TYPE REF TO my_class.
CREATE OBJECT obj1.
INSERT `line1` INTO TABLE obj1->table.
ref1 = obj1->get_line( obj1->table ).
"ref1 points to a string as expected
"Yes, there is no sense in calling a method like 'get_me'.
"it's just to have a method call in the access-chain to
"trigger the behaviour
ref1 = obj1->get_line( obj1->get_me( )->table ).
"As soon as the return value of a method is used in the access-chain of
"the parameter which is to be passed, its value will be copied to the stack.
"ref1 is a freed reference and cannot be dereferenced anymore
EXIT.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
6 | |
5 | |
3 | |
3 | |
2 | |
2 | |
2 | |
2 | |
1 | |
1 |