%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(ic_cclient).
%% This module implements generation of C client code, where the
%% client acts as an Erlang C-node, and where the communication thus
%% is according to the Erlang distribution protocol.
%%
-export([do_gen/3]).
%%------------------------------------------------------------
%% IMPLEMENTATION CONVENTIONS
%%------------------------------------------------------------
%% Functions:
%%
%% mk_* returns things to be used. No side effects.
%% emit_* Writes to file. Has Fd in arguments.
%% gen_* Same, but has no Fd. Usually for larger things.
%%
%% Terminology for generating C:
%%
%% par_list list of identifiers with types, types only, or with
%% parameters (arguments) only.
%% arg_list list of identifiers only (for function calls)
%%
%%------------------------------------------------------------
%% Internal stuff
%%------------------------------------------------------------
-import(lists, [foreach/2, foldl/3, foldr/3]).
-import(ic_codegen, [emit/2, emit/3, emit/4, emit_c_enc_rpt/4, emit_c_dec_rpt/4]).
-include("icforms.hrl").
-include("ic.hrl").
-include_lib("stdlib/include/erl_compile.hrl").
-define(IC_HEADER, "ic.h").
-define(ERL_INTERFACEHEADER, "erl_interface.h").
-define(EICONVHEADER, "ei.h").
-define(ERLANGATOMLENGTH, "256").
%%------------------------------------------------------------
%% ENTRY POINT
%%------------------------------------------------------------
do_gen(G, File, Form) ->
OeName = ic_util:mk_oe_name(G, remove_ext(ic_util:to_list(File))),
G2 = ic_file:filename_push(G, [], OeName, c),
gen_headers(G2, [], Form),
R = gen(G2, [], Form),
ic_file:filename_pop(G2, c),
R.
remove_ext(File) ->
filename:rootname(filename:basename(File)).
%%------------------------------------------------------------
%%
%% Generate client side C stubs.
%%
%% - each module definition results in a separate file.
%% - each interface definition results in a separate file.
%%
%% G = record(genobj) (see ic.hrl)
%% N = scoped names in reverse
%% X = current form to consider.
%%------------------------------------------------------------
gen(G, N, [X| Xs]) when is_record(X, preproc) ->
G1 = change_file_stack(G, N, X),
gen(G1, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, module) ->
CD = ic_code:codeDirective(G, X),
G2 = ic_file:filename_push(G, N, X, CD),
N2 = [ic_forms:get_id2(X)| N],
gen_headers(G2, N2, X),
gen(G2, N2, ic_forms:get_body(X)),
G3 = ic_file:filename_pop(G2, CD),
gen(G3, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, interface) ->
G2 = ic_file:filename_push(G, N, X, c),
N2 = [ic_forms:get_id2(X)| N],
%% Sets the temporary variable counter.
put(op_variable_count, 0),
put(tmp_declarations, []),
gen_headers(G2, N2, X),
gen(G2, N2, ic_forms:get_body(X)),
lists:foreach(
fun({_Name, Body}) ->
gen(G2, N2, Body) end,
X#interface.inherit_body),
%% Generate Prototypes
gen_prototypes(G2, N2, X),
%% Generate generic preparation for decoding
gen_receive_info(G2, N2, X),
G3 = ic_file:filename_pop(G2, c),
gen(G3, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, const) ->
emit_constant(G, N, X),
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, op) ->
{OpName, ArgNames, RetParTypes} = ic_cbe:extract_info(G, N, X),
%% XXX Note: N is the list of scoped ids of the *interface*.
gen_operation(G, N, X, OpName, ArgNames, RetParTypes),
gen_encoder(G, N, X, OpName, ArgNames, RetParTypes),
gen_decoder(G, N, X, OpName, ArgNames, RetParTypes),
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, attr) ->
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, except) ->
icstruct:except_gen(G, N, X, c),
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, enum) ->
icenum:enum_gen(G, N, X, c),
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, typedef) ->
icstruct:struct_gen(G, N, X, c),
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, struct) ->
icstruct:struct_gen(G, N, X, c),
gen(G, N, Xs);
gen(G, N, [X| Xs]) when is_record(X, union) ->
icstruct:struct_gen(G, N, X, c),
gen(G, N, Xs);
gen(G, N, [_X| Xs]) ->
%% XXX Should have debug message here.
gen(G, N, Xs);
gen(_G, _N, []) ->
ok.
%%------------------------------------------------------------
%% Change file stack
%%------------------------------------------------------------
change_file_stack(G, _N, X) when X#preproc.cat == line_nr ->
Id = ic_forms:get_id2(X),
Flags = X#preproc.aux,
case Flags of
[] ->
ic_genobj:push_file(G, Id);
_ ->
foldr(
fun({_, _, "1"}, G1) ->
ic_genobj:push_file(G1, Id);
({_, _, "2"}, G1) ->
ic_genobj:pop_file(G1, Id);
({_, _, "3"}, G1) ->
ic_genobj:sys_file(G1, Id)
end, G, Flags)
end;
change_file_stack(G, _N, _X) ->
G.
%%------------------------------------------------------------
%% Generate headers in stubfiles and header files
%%------------------------------------------------------------
gen_headers(G, N, X) when is_record(X, interface) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
%% Set the temporary variable counter
put(op_variable_count, 0),
put(tmp_declarations, []),
HFd = ic_genobj:hrlfiled(G),
IncludeFileStack = ic_genobj:include_file_stack(G),
L = length(N),
Filename =
if
L < 2 ->
lists:nth(L + 1, IncludeFileStack);
true ->
lists:nth(2, IncludeFileStack)
end,
emit(HFd, "#include \"~s\"\n", [filename:basename(Filename)]),
ic_code:gen_includes(HFd, G, X, c_client),
IfName = ic_util:to_undersc(N),
IfNameUC = ic_util:to_uppercase(IfName),
emit(HFd, "\n#ifndef __~s__\n", [IfNameUC]),
emit(HFd, "#define __~s__\n", [IfNameUC]),
LCmt = io_lib:format("Interface object definition: ~s", [IfName]),
ic_codegen:mcomment_light(HFd, [LCmt], c),
case get_c_timeout(G, "") of
"" ->
ok;
{SendTmo, RecvTmo} ->
emit(HFd, "#define OE_~s_SEND_TIMEOUT ~s\n",
[IfNameUC, SendTmo]),
emit(HFd, "#define OE_~s_RECV_TIMEOUT ~s\n",
[IfNameUC, RecvTmo]),
emit(HFd, "#ifndef EI_HAVE_TIMEOUT\n"),
emit(HFd, "#error Functions for send and receive with "
"timeout not defined in erl_interface\n"),
emit(HFd, "#endif\n\n")
end,
emit(HFd, "typedef CORBA_Object ~s;\n", [IfName]),
emit(HFd, "#endif\n\n");
false -> ok
end,
case ic_genobj:is_stubfile_open(G) of
true ->
Fd = ic_genobj:stubfiled(G),
ic_codegen:nl(Fd),
emit(Fd, "#include <stdlib.h>\n"),
emit(Fd, "#include <string.h>\n"),
case ic_options:get_opt(G, c_report) of
true ->
emit(Fd, "#ifndef OE_C_REPORT\n"),
emit(Fd, "#define OE_C_REPORT\n"),
emit(Fd, "#include <stdio.h>\n"),
emit(Fd, "#endif\n");
_ ->
ok
end,
emit(Fd, "#include \"~s\"\n", [?IC_HEADER]),
emit(Fd, "#include \"~s\"\n", [?ERL_INTERFACEHEADER]),
emit(Fd, "#include \"~s\"\n", [?EICONVHEADER]),
emit(Fd, "#include \"~s\"\n",
[filename:basename(ic_genobj:include_file(G))]),
ic_codegen:nl(Fd), ic_codegen:nl(Fd),
Fd; % XXX ??
false ->
ok
end;
%% Some items have extra includes
gen_headers(G, N, X) when is_record(X, module) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
HFd = ic_genobj:hrlfiled(G),
IncludeFileStack = ic_genobj:include_file_stack(G),
Filename = lists:nth(length(N) + 1, IncludeFileStack),
emit(HFd, "#include \"~s\"\n", [filename:basename(Filename)]),
ic_code:gen_includes(HFd, G, X, c_client);
false -> ok
end;
gen_headers(G, [], _X) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
HFd = ic_genobj:hrlfiled(G),
case ic_options:get_opt(G, c_report) of
true ->
emit(HFd, "#ifndef OE_C_REPORT\n"),
emit(HFd, "#define OE_C_REPORT\n"),
emit(HFd, "#include <stdio.h>\n"),
emit(HFd, "#endif\n");
_ ->
ok
end,
emit(HFd, "#include \"~s\"\n", [?IC_HEADER]),
emit(HFd, "#include \"~s\"\n", [?ERL_INTERFACEHEADER]),
emit(HFd, "#include \"~s\"\n", [?EICONVHEADER]),
ic_code:gen_includes(HFd, G, c_client);
false -> ok
end;
gen_headers(_G, _N, _X) ->
ok.
%%------------------------------------------------------------
%% Generate all prototypes (for interface)
%%------------------------------------------------------------
gen_prototypes(G, N, X) ->
case ic_genobj:is_hrlfile_open(G) of
false ->
ok;
true ->
HFd = ic_genobj:hrlfiled(G),
IfName = ic_util:to_undersc(N),
%% Emit generated function prototypes
emit(HFd, "\n/* Operation functions */\n"),
lists:foreach(fun({_Name, Body}) ->
emit_operation_prototypes(G, HFd, N, Body)
end, [{x, ic_forms:get_body(X)}|
X#interface.inherit_body]),
UserProto = get_user_proto(G, false),
%% Emit generic function prototypes
case UserProto of
false ->
ok;
UserProto ->
emit(HFd,
"\n/* Generic user defined encoders */\n"),
emit(HFd,
"int ~s_prepare_notification_encoding("
"CORBA_Environment*);"
"\n", [UserProto]),
emit(HFd,
"int ~s_prepare_request_encoding(CORBA_Environment*);"
"\n", [UserProto])
end,
%% Emit encoding function prototypes
emit(HFd, "\n/* Input encoders */\n"),
lists:foreach(fun({_Name, Body}) ->
emit_encoder_prototypes(G, HFd, N, Body)
end,
[{x, ic_forms:get_body(X)}|
X#interface.inherit_body]),
%% Emit generic function prototypes
emit(HFd, "\n/* Generic decoders */\n"),
emit(HFd, "int ~s__receive_info(~s, CORBA_Environment*);\n",
[IfName, IfName]),
case UserProto of
false ->
ok;
UserProto ->
emit(HFd, "\n/* Generic user defined decoders */\n"),
emit(HFd,
"int ~s_prepare_reply_decoding(CORBA_Environment*);"
"\n", [UserProto])
end,
%% Emit decode function prototypes
emit(HFd, "\n/* Result decoders */\n"),
lists:foreach(fun({_Name, Body}) ->
emit_decoder_prototypes(G, HFd, N, Body)
end, [{x, ic_forms:get_body(X)}|
X#interface.inherit_body]),
case UserProto of
false ->
ok;
UserProto ->
%% Emit generic send and receive_prototypes
{Sfx, TmoType} = case get_c_timeout(G, "") of
"" ->
{"", ""};
_ ->
{"_tmo", ", unsigned int"}
end,
emit(HFd,
"\n/* Generic user defined send and receive "
"functions */\n"),
emit(HFd,
"int ~s_send_notification~s(CORBA_Environment*~s);\n",
[UserProto, Sfx, TmoType]),
emit(HFd,
"int ~s_send_request_and_receive_reply~s("
"CORBA_Environment*~s~s);\n",
[UserProto, Sfx, TmoType, TmoType])
end
end.
%%------------------------------------------------------------
%% Generate receive_info() (generic part for message reception)
%% (for interface). For backward compatibility only.
%%------------------------------------------------------------
gen_receive_info(G, N, _X) ->
case ic_genobj:is_stubfile_open(G) of
false ->
ok;
true ->
Fd = ic_genobj:stubfiled(G),
IfName = ic_util:to_undersc(N),
UserProto = get_user_proto(G, oe),
Code =
"
/*
* Generic function, used to return received message information.
* Not used by oneways. Always generated. For backward compatibility only.
*/
int ~s__receive_info(~s oe_obj, CORBA_Environment *oe_env)
{
return ~s_prepare_reply_decoding(oe_env);
}\n",
emit(Fd, Code, [IfName, IfName, UserProto])
end.
%%------------------------------------------------------------
%% Emit constant
%%------------------------------------------------------------
emit_constant(G, N, ConstRecord) ->
case ic_genobj:is_hrlfile_open(G) of
false -> ok;
true ->
Fd = ic_genobj:hrlfiled(G),
CName = ic_util:to_undersc(
[ic_forms:get_id(ConstRecord#const.id)| N]),
UCName = ic_util:to_uppercase(CName),
emit(Fd, "\n#ifndef __~s__\n", [UCName]),
emit(Fd, "#define __~s__\n", [UCName]),
emit(Fd, "/* Constant: ~s */\n", [CName]),
if is_record(ConstRecord#const.type, wstring) ->
%% If wstring, add 'L'
emit(Fd, "#define ~s L~p\n",
[CName, ConstRecord#const.val]);
true ->
emit(Fd, "#define ~s ~p\n",
[CName, ConstRecord#const.val])
end,
emit(Fd, "#endif\n\n")
end.
%%------------------------------------------------------------
%% Generate operation (for interface)
%%------------------------------------------------------------
%% N is the list of scoped ids of the *interface*.
%% X is the operation
gen_operation(G, N, X, OpName, ArgNames, RetParTypes) ->
case ic_genobj:is_stubfile_open(G) of
true ->
do_gen_operation(G, N, X, OpName, ArgNames, RetParTypes);
false ->
ok
end.
do_gen_operation(G, N, X, OpName, ArgNames, RetParTypes) ->
Fd = ic_genobj:stubfiled(G),
IfName = ic_util:to_undersc(N),
IfNameUC = ic_util:to_uppercase(IfName),
{R, ParTypes, _} = RetParTypes,
IsOneway = ic_forms:is_oneway(X),
emit(Fd, "\n"
"/***\n"
" *** Operation function \"~s\" ~s\n"
" ***/\n\n",
[OpName, ifelse(IsOneway, "(oneway)", "")]),
RV = element(1, R),
Ret = case IsOneway of
false ->
if RV /= void ->
mk_ret_type(G, N, R);
true ->
"void"
end;
true ->
"void"
end,
ParListStr = ic_util:chain(mk_par_type_list(G, N, X, [in, out],
[types, args],
ParTypes, ArgNames), ", "),
emit(Fd,
"~s ~s(~s, ~sCORBA_Environment *oe_env)\n{\n",
[Ret, OpName, [IfName, " ", "oe_obj"], ParListStr]),
case IsOneway of
true ->
ok;
false ->
case ictype:isArray(G, N, R) of
true ->
emit(Fd, " ~s oe_return = NULL;\n\n",
[mk_ret_type(G, N, R)]);
false ->
if RV /= void ->
emit(Fd, " ~s oe_return;\n\n",
[Ret]);
true ->
ok
end
end,
emit(Fd,
" /* Initiating the message reference */\n"
" ic_init_ref(oe_env, &oe_env->_unique);\n")
end,
emit(Fd,
" /* Initiating exception indicator */ \n"
" oe_env->_major = CORBA_NO_EXCEPTION;\n"),
%% XXX Add pointer checks: checks of in-parameter
%% pointers, and non-variable out-parameter pointers.
emit(Fd," /* Creating ~s message */ \n",
[ifelse(IsOneway, "cast", "call")]),
EncParListStr = ic_util:chain(mk_arg_list_for_encoder(G, N, X,
ParTypes, ArgNames),
", "),
emit(Fd,
" if (~s__client_enc(oe_obj, ~s""oe_env) < 0) {\n",
[OpName, EncParListStr]),
emit(Fd,
" CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, "
"DATA_CONVERSION, \"Cannot encode message\");\n"),
RetVar = ifelse(RV /= void, " oe_return", ""),
emit_c_enc_rpt(Fd, " ", "client operation ~s\\n====\\n", [OpName]),
emit(Fd, " return~s;\n }\n", [RetVar]),
emit(Fd," /* Sending ~s message */ \n",
[ifelse(IsOneway, "cast", "call")]),
UserProto = get_user_proto(G, oe),
{Sfx, SendTmo, RecvTmo} = case get_c_timeout(G, "") of
"" ->
{"", "", ""};
_ ->
{"_tmo",
[", OE_", IfNameUC, "_SEND_TIMEOUT"],
[", OE_", IfNameUC, "_RECV_TIMEOUT"]}
end,
case IsOneway of
true ->
emit(Fd,
" if (~s_send_notification~s(oe_env~s) < 0)\n"
" return~s;\n", [UserProto, Sfx, SendTmo, RetVar]);
false ->
emit(Fd,
" if (~s_send_request_and_receive_reply~s(oe_env~s~s) < 0)\n"
" return~s;\n",
[UserProto, Sfx, SendTmo, RecvTmo, RetVar]),
DecParList0 = mk_arg_list_for_decoder(G, N, X,
ParTypes, ArgNames),
DecParList1 = case mk_ret_type(G, N, R) of
"void" ->
DecParList0;
_ ->
["&oe_return"| DecParList0]
end,
DecParListStr = ic_util:chain(DecParList1, ", "),
%% YYY Extracting results
emit(Fd,
" /* Extracting result value(s) */ \n"
" if (~s__client_dec(oe_obj, ~s""oe_env) < 0) {\n",
[OpName, DecParListStr]),
emit(Fd,
" CORBA_exc_set(oe_env, "
"CORBA_SYSTEM_EXCEPTION, DATA_CONVERSION, "
"\"Bad result value(s)\");\n"),
emit_c_dec_rpt(Fd, " ", "client operation ~s\\n=====\\n", [OpName]),
emit(Fd,
" return~s;\n"
" }\n", [RetVar])
end,
emit(Fd, " return~s;\n", [RetVar]),
emit(Fd, "}\n\n\n").
%%------------------------------------------------------------
%% Generate encoder
%%------------------------------------------------------------
%% N is the list of scoped ids of the *interface*.
%% X is the operation
gen_encoder(G, N, X, OpName, ArgNames, RetParTypes)->
case ic_genobj:is_stubfile_open(G) of
true ->
Fd = ic_genobj:stubfiled(G),
IfName = ic_util:to_undersc(N),
{_R, ParTypes, _} = RetParTypes,
TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames),
emit(Fd, "/*\n * Encode operation input for \"~s\"\n */\n\n",
[OpName]),
ParList = ic_util:chain(
mk_par_type_list(G, N, X, [in], [types, args],
ParTypes, ArgNames), ", "),
emit(Fd,
"int ~s__client_enc(~s oe_obj, ~s"
"CORBA_Environment *oe_env)\n{\n",
[OpName, IfName, ParList]),
InTypeAttrArgs = lists:filter(fun({_, in, _}) -> true;
({_, _, _}) -> false
end, TypeAttrArgs),
case InTypeAttrArgs of
[] ->
ok;
_ ->
emit(Fd,
" int oe_error_code = 0;\n\n")
end,
emit_encodings(G, N, Fd, X, InTypeAttrArgs,
ic_forms:is_oneway(X)),
emit(Fd, " return 0;\n}\n\n"),
ok;
false ->
ok
end.
%%------------------------------------------------------------
%% Generate decoder
%%------------------------------------------------------------
%% N is the list of scoped ids of the *interface*.
%% X is the operation
gen_decoder(G, N, X, OpName, ArgNames, RetParTypes)->
case ic_forms:is_oneway(X) of
true ->
ok;
false ->
case ic_genobj:is_stubfile_open(G) of
true ->
Fd = ic_genobj:stubfiled(G),
IfName = ic_util:to_undersc(N),
{R, ParTypes, _} = RetParTypes,
TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames),
emit(Fd, "/*\n * Decode operation results for "
"\"~s\"\n */\n\n", [OpName]),
ParList0 = mk_par_type_list(G, N, X, [out],
[types, args],
ParTypes, ArgNames),
PARLIST = case mk_ret_type(G, N, R) of
"void" ->
ParList0;
Else ->
[Else ++ "* oe_return"| ParList0]
end,
PLFCD = ic_util:chain(PARLIST, ", "),
emit(Fd,
"int ~s__client_dec(~s oe_obj, ~s"
"CORBA_Environment *oe_env)\n{\n",
[OpName, IfName, PLFCD]),
emit(Fd, " int oe_error_code = 0;\n"),
OutTypeAttrArgs = lists:filter(fun({_, out, _}) -> true;
({_, _, _}) -> false
end, TypeAttrArgs),
emit_decodings(G, N, Fd, R, OutTypeAttrArgs),
emit(Fd, " return 0;\n}\n\n"),
ok;
false ->
ok
end
end.
%%------------------------------------------------------------
%% EMIT ENCODINGS/DECODINGS
%%------------------------------------------------------------
%%------------------------------------------------------------
%% Emit encodings
%%------------------------------------------------------------
%% N is the list of scoped ids of the *interface*.
%% X is the operation
%% emit_encodings(G, N, Fd, X, TypeAttrArgs, IsOneWay)
%%
emit_encodings(G, N, Fd, X, TypeAttrArgs, true) ->
%% Cast
UserProto = get_user_proto(G, oe),
emit(Fd,
" if (~s_prepare_notification_encoding(oe_env) < 0)\n"
" return -1;\n", [UserProto]),
emit_encodings_1(G, N, Fd, X, TypeAttrArgs);
emit_encodings(G, N, Fd, X, TypeAttrArgs, false) ->
%% Call
UserProto = get_user_proto(G, oe),
emit(Fd,
" if (~s_prepare_request_encoding(oe_env) < 0)\n"
" return -1;\n", [UserProto]),
emit_encodings_1(G, N, Fd, X, TypeAttrArgs).
emit_encodings_1(G, N, Fd, X, TypeAttrArgs) ->
{ScopedName, _, _} = ic_cbe:extract_info(G, N, X),
Name = case ic_options:get_opt(G, scoped_op_calls) of
true ->
ScopedName;
false ->
ic_forms:get_id2(X)
end,
if
TypeAttrArgs /= [] ->
emit(Fd, " if (oe_ei_encode_tuple_header(oe_env, ~p) < 0) {\n",
[length(TypeAttrArgs) + 1]),
emit_c_enc_rpt(Fd, " ", "ei_encode_tuple_header", []),
emit(Fd, " return -1;\n }\n");
true ->
ok
end,
emit(Fd, " if (oe_ei_encode_atom(oe_env, ~p) < 0) {\n", [Name]),
emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []),
emit(Fd, " return -1;\n }\n"),
foreach(fun({{'void', _}, _, _}) ->
ok;
({T1, A1, N1}) ->
IndOp = mk_ind_op(A1),
emit_coding_comment(G, N, Fd, "Encode", IndOp,
T1, N1),
ic_cbe:emit_encoding_stmt(G, N, X, Fd, T1, IndOp ++ N1,
"oe_env->_outbuf")
end, TypeAttrArgs),
ok.
%%------------------------------------------------------------
%% Emit dedodings
%%------------------------------------------------------------
%% XXX Unfortunately we have to retain the silly `oe_first' variable,
%% since its name is hardcoded in other modules (icstruct, icunion,
%% etc).
%% N is the list of scoped ids of the *interface*.
%% X is the operation
emit_decodings(G, N, Fd, RetType, TypeAttrArgs) ->
if
TypeAttrArgs /= [] ->
%% Only if there are out parameters
emit(Fd, " if ((oe_error_code = ei_decode_tuple_header("
"oe_env->_inbuf, &oe_env->_iin, "
"&oe_env->_received)) < 0) {\n"),
emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []),
emit(Fd, " return oe_error_code;\n }\n"),
Len = length(TypeAttrArgs) + 1,
emit(Fd, " if (oe_env->_received != ~p) {\n", [Len]),
emit_c_dec_rpt(Fd, " ", "tuple header size != ~p", [Len]),
emit(Fd, " return -1;\n }\n");
true ->
ok
end,
%% Fetch the return value
emit_coding_comment(G, N, Fd, "Decode return value", "*", RetType, "oe_return"),
APars =
case ic_cbe:is_variable_size(G, N, RetType) of
true ->
emit(Fd,
" {\n"
" int oe_size_count_index = oe_env->_iin;\n"
" int oe_malloc_size = 0;\n"
" void *oe_first = NULL;\n"),
ic_cbe:emit_malloc_size_stmt(G, N, Fd, RetType,
"oe_env->_inbuf",
1, caller),
%% XXX Add malloc prefix from option
emit(Fd,
" OE_MALLOC_SIZE_CHECK(oe_env, oe_malloc_size);\n"
" if ((*oe_return = oe_first = "
"malloc(oe_malloc_size)) == NULL) {\n"
" CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, "
"NO_MEMORY, \"Cannot malloc\");\n"
" return -1;\n"
" }\n"),
Pars = ["*oe_return"],
DecType = case ictype:isArray(G, N, RetType) of
true -> array_dyn;
false -> caller_dyn
end,
ic_cbe:emit_decoding_stmt(G, N, Fd, RetType,
"(*oe_return)",
"", "oe_env->_inbuf", 1,
"&oe_outindex", DecType,
Pars),
emit(Fd, " }\n"),
Pars;
false ->
case ictype:isArray(G, N, RetType) of
true ->
Pars = ["*oe_return"],
emit(Fd,
" {\n"
" int oe_size_count_index = oe_env->_iin;\n"
" int oe_malloc_size = 0;\n"
" void *oe_first = NULL;\n"),
ic_cbe:emit_malloc_size_stmt(G, N, Fd, RetType,
"oe_env->_inbuf",
1, caller),
%% XXX Add malloc prefix from option
emit(Fd,
" OE_MALLOC_SIZE_CHECK(oe_env, "
"oe_malloc_size);\n"
" if ((*oe_return = oe_first = "
"malloc(oe_malloc_size)) == NULL) {\n"
" CORBA_exc_set(oe_env, "
"CORBA_SYSTEM_EXCEPTION, NO_MEMORY, "
"\"Cannot malloc\");\n"
" return -1;"
" }\n"),
ic_cbe:emit_decoding_stmt(G, N, Fd, RetType,
"oe_return", "",
"oe_env->_inbuf", 1,
"&oe_outindex",
array_fix_ret,
Pars),
emit(Fd, " }\n"),
Pars;
false ->
Pars = [],
%% The last parameter "oe_outindex" is not interesting
%% in the static case.
ic_cbe:emit_decoding_stmt(G, N, Fd, RetType,
"oe_return", "",
"oe_env->_inbuf", 1,
"&oe_outindex",
caller, Pars),
ic_codegen:nl(Fd),
Pars
end
end,
foldl(fun({{'void', _}, _, _}, Acc) ->
Acc;
({T, A, N1}, Acc) ->
emit_one_decoding(G, N, Fd, T, A, N1, Acc)
end, APars, TypeAttrArgs),
ok.
emit_one_decoding(G, N, Fd, T, A, N1, Acc) ->
IndOp = mk_ind_op(A),
case ic_cbe:is_variable_size(G, N, T) of
true ->
emit_coding_comment(G, N, Fd, "Decode", IndOp,
T, N1),
emit(Fd,
" {\n"
" int oe_size_count_index = oe_env->_iin;\n"
" int oe_malloc_size = 0;\n"
" void *oe_first = NULL;\n"),
ic_cbe:emit_malloc_size_stmt(G, N, Fd, T,
"oe_env->_inbuf",
1, caller),
%% XXX Add malloc prefix from option
emit(Fd,
" OE_MALLOC_SIZE_CHECK(oe_env, oe_malloc_size);\n"
" if ((~s~s = oe_first = "
"malloc(oe_malloc_size)) == NULL) {\n", [IndOp, N1]),
ic_cbe:emit_dealloc_stmts(Fd, " ", Acc),
emit(Fd,
" CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, "
"NO_MEMORY, \"Cannot malloc\");\n"
" return -1;\n"
" }\n"),
NAcc = [IndOp ++ N1| Acc],
DecType = case ictype:isArray(G, N, T) of
true ->
array_dyn;
false ->
caller_dyn
end,
ic_cbe:emit_decoding_stmt(G, N, Fd, T,
"(" ++ IndOp
++ N1 ++ ")", "",
"oe_env->_inbuf", 1,
"&oe_outindex",
DecType, NAcc),
emit(Fd, " }\n"),
NAcc;
false ->
case ictype:isArray(G, N, T) of
true ->
emit_coding_comment(G, N, Fd, "Decode", "",
T, N1),
ic_cbe:emit_decoding_stmt(G, N, Fd, T, N1,
"", "oe_env->_inbuf",
1, "&oe_outindex",
array_fix_out, Acc),
ic_codegen:nl(Fd),
[N1| Acc];
false ->
%% The last parameter "oe_outindex" is
%% not interesting in the static case, but
%% must be present anyhow.
emit_coding_comment(G, N, Fd, "Decode",
IndOp, T, N1),
ic_cbe:emit_decoding_stmt(G, N, Fd, T, N1,
"", "oe_env->_inbuf",
1, "&oe_outindex",
caller, Acc),
ic_codegen:nl(Fd),
Acc
end
end.
%%------------------------------------------------------------
%% GENERATE PROTOTYPES
%%------------------------------------------------------------
%%------------------------------------------------------------
%% Generate operation prototypes
%%------------------------------------------------------------
emit_operation_prototypes(G, Fd, N, Xs) ->
lists:foreach(
fun(X) when is_record(X, op) ->
{ScopedName, ArgNames, RetParTypes} =
ic_cbe:extract_info(G, N, X),
{R, ParTypes, _} = RetParTypes,
IfName = ic_util:to_undersc(N),
RT = mk_ret_type(G, N, R),
ParList =
ic_util:chain(
mk_par_type_list(G, N, X, [in, out], [types],
ParTypes, ArgNames),
", "),
emit(Fd, "~s ~s(~s, ~sCORBA_Environment*);\n",
[RT, ScopedName, IfName, ParList]);
(_) ->
ok
end, Xs).
%%------------------------------------------------------------
%% Generate encoder prototypes
%%------------------------------------------------------------
emit_encoder_prototypes(G, Fd, N, Xs) ->
lists:foreach(
fun(X) when is_record(X, op) ->
{ScopedName, ArgNames, RetParTypes} =
ic_cbe:extract_info(G, N, X),
{_R, ParTypes, _} = RetParTypes,
IfName = ic_util:to_undersc(N),
ParList = ic_util:chain(
mk_par_type_list(G, N, X, [in], [types],
ParTypes, ArgNames),
", "),
emit(Fd, "int ~s__client_enc(~s, ~sCORBA_Environment*);\n",
[ScopedName, IfName, ParList]);
(_) ->
ok
end, Xs).
%%------------------------------------------------------------
%% Generate decoder prototypes
%%------------------------------------------------------------
emit_decoder_prototypes(G, Fd, N, Xs) ->
lists:foreach(
fun(X) when is_record(X, op) ->
case ic_forms:is_oneway(X) of
true ->
true;
false ->
IfName = ic_util:to_undersc(N),
{ScopedName, ArgNames, RetParTypes} =
ic_cbe:extract_info(G, N, X),
{R, ParTypes, _} = RetParTypes,
ParList0 =
mk_par_type_list(G, N, X, [out], [types],
ParTypes, ArgNames),
PARLIST = case mk_ret_type(G, N, R) of
"void" ->
ParList0;
Else ->
[Else ++ "*"| ParList0]
end,
ParList = ic_util:chain(PARLIST, ", "),
emit(Fd, "int ~s__client_dec(~s, ~s"
"CORBA_Environment*);\n",
[ScopedName, IfName, ParList])
end;
(_) ->
ok
end, Xs).
%%------------------------------------------------------------
%% PARAMETER TYPE LISTS
%%------------------------------------------------------------
%%------------------------------------------------------------
%% Make parameter type list
%%
%% InOrOut = in | out | [in | out]
%% TypesOrArgs = types | args | [types | args]
%%------------------------------------------------------------
mk_par_type_list(G, N, X, InOrOut, TypesOrArgs, Types, Args) ->
TypeAttrArgs =
filterzip(
fun(_, {inout, Arg}) ->
ic_error:error(G, {inout_spec_for_c, X, Arg}),
false;
(Type, {Attr, Arg}) ->
case lists:member(Attr, InOrOut) of
true ->
{true, {Type, Attr, Arg}};
false ->
false
end
end, Types, Args),
lists:map(
fun({Type, Attr, Arg}) ->
Ctype = ic_cbe:mk_c_type(G, N, Type),
IsArray = ictype:isArray(G, N, Type),
IsStruct = ictype:isStruct(G, N, Type),
IsUnion = ictype:isUnion(G, N, Type),
Dyn =
case ic_cbe:is_variable_size(G, N, Type) of
true ->
if
is_record(Type, string) -> "";
Ctype == "CORBA_char *" -> "";
is_record(Type, wstring) -> "";
Ctype == "CORBA_wchar *" -> "";
true ->
case IsArray of
true ->
"_slice*";
false ->
"*"
end
end;
false ->
if
Attr == in, Ctype == "erlang_pid" ->
"*";
Attr == in, Ctype == "erlang_port" ->
"*";
Attr == in, Ctype == "erlang_ref" ->
"*";
Attr == in, IsStruct == true ->
"*";
Attr == in, IsUnion == true ->
"*";
Attr == in, IsArray == true ->
"_slice*";
Attr == out, IsArray == true ->
"_slice";
true ->
""
end
end,
IndOp = mk_ind_op(Attr),
case {lists:member(types, TypesOrArgs),
lists:member(args, TypesOrArgs)} of
{true, true} ->
Ctype ++ Dyn ++ IndOp ++ " " ++ Arg;
{true, false} ->
Ctype ++ Dyn ++ IndOp;
{false, true} ->
Arg;
{false, false} ->
""
end
end, TypeAttrArgs).
%%------------------------------------------------------------
%% ENCODER ARG LIST
%%------------------------------------------------------------
%%------------------------------------------------------------
%% Make encoder argument list XXX
%%------------------------------------------------------------
mk_arg_list_for_encoder(G, _N, X, Types, Args) ->
filterzip(
fun(_, {out, _}) ->
false;
(_, {inout, Arg}) ->
ic_error:error(G, {inout_spec_for_c, X, Arg}),
false;
(_Type, {in, Arg}) ->
{true, Arg}
end, Types, Args).
%%------------------------------------------------------------
%% DECODER ARG LIST
%%------------------------------------------------------------
%%------------------------------------------------------------
%% Make decoder argument list XXX
%%------------------------------------------------------------
mk_arg_list_for_decoder(G, _N, X, Types, Args) ->
filterzip(fun(_, {in, _}) ->
false;
(_, {inout, Arg}) ->
ic_error:error(G, {inout_spec_for_c, X, Arg}),
false;
(_, {out, Arg}) ->
{true, Arg}
end, Types, Args).
%%------------------------------------------------------------
%% MISC
%%------------------------------------------------------------
%%------------------------------------------------------------
%% Make list of {Type, Attr, Arg}
%%------------------------------------------------------------
mk_type_attr_arg_list(Types, Args) ->
filterzip(fun(Type, {Attr, Arg}) ->
{true, {Type, Attr, Arg}}
end, Types, Args).
%%------------------------------------------------------------
%% Make return type
%%------------------------------------------------------------
mk_ret_type(G, N, Type) ->
Ctype = ic_cbe:mk_c_type(G, N, Type),
Dyn = case ic_cbe:is_variable_size(G, N, Type) of
true ->
if
is_record(Type, string) ->
"";
Ctype == "CORBA_char *" ->
"";
is_record(Type, wstring) ->
"";
Ctype == "CORBA_wchar *" ->
"";
true ->
case ictype:isArray(G, N, Type) of
true ->
"_slice*";
false ->
"*"
end
end;
false ->
case ictype:isArray(G, N, Type) of
true ->
"_slice*";
false ->
""
end
end,
Ctype ++ Dyn.
%%------------------------------------------------------------
%% Make indirection operator (to "*" or not to "*").
%%------------------------------------------------------------
mk_ind_op(in) ->
"";
mk_ind_op(inout) ->
error;
mk_ind_op(out) ->
"*".
%%------------------------------------------------------------
%% Emit encoding/decoding comment
%%------------------------------------------------------------
emit_coding_comment(G, N, Fd, String, RefOrVal, Type, Name) ->
emit(Fd, " /* ~s parameter: ~s~s ~s */\n",
[String, ic_cbe:mk_c_type(G, N, Type), RefOrVal, Name]).
%%------------------------------------------------------------
%% User protocol prefix for generic functions
%%------------------------------------------------------------
get_user_proto(G, Default) ->
case ic_options:get_opt(G, user_protocol) of
false ->
Default;
Pfx ->
Pfx
end.
%%------------------------------------------------------------
%% Timeout. Returns a string (or Default).
%%------------------------------------------------------------
get_c_timeout(G, Default) ->
case ic_options:get_opt(G, c_timeout) of
Tmo when is_integer(Tmo) ->
TmoStr = integer_to_list(Tmo),
{TmoStr, TmoStr};
{SendTmo, RecvTmo} when is_integer(SendTmo) andalso is_integer(RecvTmo) ->
{integer_to_list(SendTmo), integer_to_list(RecvTmo)};
false ->
Default
end.
%%------------------------------------------------------------
%% ZIPPERS (merging of successive elements of two lists).
%%------------------------------------------------------------
%% zip([H1| T1], [H2| T2]) ->
%% [{H1, H2}| zip(T1, T2)];
%% zip([], []) ->
%% [].
filterzip(F, [H1| T1], [H2| T2]) ->
case F(H1, H2) of
false ->
filterzip(F, T1, T2);
{true, Val} ->
[Val| filterzip(F, T1, T2)]
end;
filterzip(_, [], []) ->
[].
ifelse(true, A, _) ->
A;
ifelse(false, _, B) ->
B.