Agent Calls
Work in progress!
Contents
Terminology
In the following text the place where an agent is created (with the agent keyword or the tilde operator) is called the agent creation. The feature to which the agent points is the agent callee and the place (or places) where the agent is called is denoted agent call.
class TERMINOLOGY feature f1 local p: PROCEDURE [ANY, TUPLE] do p := agent target -- agent creation end f2 (p: PROCEDURE [ANY, TUPLE]) do p.call ([]) -- agent call end target -- callee do end end
Wrapper generation
Frozen code
Lets look first at frozen code. Class C is used throughout the following examples.
class C feature f (p1, p2: INTEGER; p3: STRING): STRING do ... end end
For everly agent creation a c-function is generated. This wrapper function does the following:
- Reorder the closed and open arguments.
- Calculate the proper agent callee based on the dynamic type of the target.
- Call the agent callee
For the agent creation:
agent c.f (?, 1, ?)
the following wrapper function is generated:
EIF_REFERENCE _fAaatpmf_2_4 (EIF_REFERENCE (*f_ptr)(EIF_REFERENCE, EIF_INTEGER_32, EIF_INTEGER_32, EIF_REFERENCE), EIF_TYPED_ELEMENT* closed, EIF_TYPED_ELEMENT* open) { return (FUNCTION_CAST(EIF_REFERENCE, (EIF_REFERENCE, EIF_INTEGER_32, EIF_INTEGER_32, EIF_REFERENCE)) RTVF(350, 30, "f", closed [1].element.rarg))( closed [1].element.rarg, open [1].element.i32arg, closed [2].element.i32arg, open [2].element.rarg); }
And for this agent creation:
agent {C}.f (1, ?, "hello")
The wrapper function looks like:
EIF_REFERENCE _fAaatpmf_1_3 (EIF_REFERENCE (*f_ptr)(EIF_REFERENCE, EIF_INTEGER_32, EIF_INTEGER_32, EIF_REFERENCE), EIF_TYPED_ELEMENT* closed, EIF_TYPED_ELEMENT* open) { return (FUNCTION_CAST(EIF_REFERENCE, (EIF_REFERENCE, EIF_INTEGER_32, EIF_INTEGER_32, EIF_REFERENCE)) RTVF(350, 30, "f", open [1].element.rarg))( open [1].element.rarg, closed [1].element.i32arg, open [2].element.i32arg, closed [2].element.rarg); }
Finalized code
In finalized code things are only slightly different.
Whenever an agent is created with a closed target, the actual agent callee is calculated at agent creation time. Moreover, two wrappers are generated:
- one which expects the closed arguments as a tuple (encapsulated). The only difference to the frozen wrapper is how the agent callee is calculated.
- and one that expects them as separate parameters. We will later see how this wrapper is used to optimize some special agent calls.
For the agent creation:
agent c.f (?, 1, ?)
the following wrapper functions are generated:
EIF_REFERENCE _fAaatpmf_2_4 (EIF_REFERENCE (*f_ptr)(EIF_REFERENCE, EIF_INTEGER_32, EIF_INTEGER_32, EIF_REFERENCE), EIF_TYPED_ELEMENT* closed, EIF_TYPED_ELEMENT* open) { return f_ptr (closed [1].element.rarg, open [1].element.i32arg, closed [2].element.i32arg, open [2].element.rarg); } EIF_REFERENCE __fAaatpmf_2_4 (EIF_REFERENCE (*f_ptr)(EIF_REFERENCE, EIF_INTEGER_32, EIF_INTEGER_32, EIF_REFERENCE), EIF_TYPED_ELEMENT* closed, EIF_INTEGER_32 op_2, EIF_REFERENCE op_4) { return f_ptr (closed [1].element.rarg, op_2, closed [2].element.i32arg, op_4); }
The name of the wrapper for the encapsulated closed arguments allways starts with one '_', whereas the other one starts with two underlines.
Melted agent creation
Of course it is possible that the agent creation is melted and hence there is no wrapper. For this situation the Eiffel runtime provides two generic wrapper functions:
RT_LNK void rout_obj_call_procedure_dynamic ( int stype_id, int feature_id, int is_precompiled, int is_basic_type, int is_inline_agent, EIF_TYPED_ELEMENT* closed_args, int closed_count, EIF_TYPED_ELEMENT* open_args, int open_count, EIF_REFERENCE open_map); RT_LNK void rout_obj_call_function_dynamic ( int stype_id, int feature_id, int is_precompiled, int is_basic_type, int is_inline_agent, EIF_TYPED_ELEMENT* closed_args, int closed_count, EIF_TYPED_ELEMENT* open_args, int open_count, EIF_REFERENCE open_map, void* res);
These functions calculate the agent callee with the static type id and a feature id. Furthermore the reordering of open and closed arguments has to be done at agent call time with help of the open_map.
Agent call
fast_item (a_rout_disp, a_calc_rout_addr: POINTER a_closed_operands: POINTER; a_operands: POINTER a_class_id, a_feature_id: INTEGER; a_is_precompiled, a_is_basic, a_is_inline_agent: BOOLEAN a_closed_count, a_open_count: INTEGER; a_open_map: POINTER): RESULT_TYPE external "C inline use %"eif_rout_obj.h%"" alias "[ #ifdef WORKBENCH $$_result_type result; if ($a_rout_disp != 0) { return (FUNCTION_CAST($$_result_type, (EIF_POINTER, EIF_REFERENCE, EIF_REFERENCE)) $a_rout_disp)( $a_calc_rout_addr, $a_closed_operands, $a_operands); } else { rout_obj_call_function_dynamic ( $a_class_id, $a_feature_id, $a_is_precompiled, $a_is_basic, $a_is_inline_agent, $a_closed_operands, $a_closed_count, $a_operands, $a_open_count, $a_open_map, &result); return result; } #else return (FUNCTION_CAST($$_result_type, (EIF_POINTER, EIF_REFERENCE, EIF_REFERENCE)) $a_rout_disp)( $a_calc_rout_addr, $a_closed_operands, $a_operands); #endif ]" end