diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/ic/src | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/ic/src')
44 files changed, 30155 insertions, 0 deletions
diff --git a/lib/ic/src/Makefile b/lib/ic/src/Makefile new file mode 100644 index 0000000000..5dac304e32 --- /dev/null +++ b/lib/ic/src/Makefile @@ -0,0 +1,218 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1998-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk + +ifeq ($(TYPE),debug) +ERL_COMPILE_FLAGS += -Ddebug -W +endif + +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(IC_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/ic-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + ic \ + ic_erlbe \ + ic_cbe \ + icscan \ + icparse \ + iceval \ + ictype \ + ictk \ + icstruct \ + icenum \ + icpreproc \ + icunion \ + ic_pp \ + ic_pragma \ + ic_noc \ + ic_plainbe \ + ic_cclient \ + ic_cserver \ + ic_fetch \ + ic_code \ + ic_codegen \ + ic_error \ + ic_file \ + ic_forms \ + ic_genobj \ + ic_options \ + ic_symtab \ + ic_util \ + ic_jbe \ + ic_struct_java \ + ic_union_java \ + ic_enum_java \ + ic_constant_java \ + ic_sequence_java \ + ic_array_java \ + ic_attribute_java \ + ic_java_type \ + ic_erl_template + + +CCL_EX_FILES = \ + ../examples/c-client/ReadMe \ + ../examples/c-client/Makefile \ + ../examples/c-client/client.c \ + ../examples/c-client/random.idl \ + ../examples/c-client/rmod_random_impl.erl \ + ../examples/c-client/test.erl + +CSRV_EX_FILES = \ + ../examples/c-server/ReadMe \ + ../examples/c-server/Makefile \ + ../examples/c-server/client.c \ + ../examples/c-server/client.erl \ + ../examples/c-server/server.c \ + ../examples/c-server/callbacks.c \ + ../examples/c-server/random.idl + +EPL_EX_FILES = \ + ../examples/erl-plain/ReadMe \ + ../examples/erl-plain/rmod_random_impl.erl \ + ../examples/erl-plain/random.idl + + +ESRV_EX_FILES = \ + ../examples/erl-genserv/ReadMe \ + ../examples/erl-genserv/rmod_random_impl.erl \ + ../examples/erl-genserv/random.idl + +JAVA_EX_FILES = \ + ../examples/java-client-server/ReadMe \ + ../examples/java-client-server/client.java \ + ../examples/java-client-server/server.java \ + ../examples/java-client-server/serverImpl.java \ + ../examples/java-client-server/random.idl + +MIXED_EX_FILES = \ + ../examples/all-against-all/ReadMe \ + ../examples/all-against-all/Makefile \ + ../examples/all-against-all/client.erl \ + ../examples/all-against-all/server.erl \ + ../examples/all-against-all/client.c \ + ../examples/all-against-all/server.c \ + ../examples/all-against-all/callbacks.c \ + ../examples/all-against-all/client.java \ + ../examples/all-against-all/server.java \ + ../examples/all-against-all/serverImpl.java \ + ../examples/all-against-all/random.idl + + +EXTERNAL_HRL_FILES= + +INTERNAL_HRL_FILES = \ + ic.hrl \ + ic_debug.hrl \ + icforms.hrl + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +YRL_FILE = icparse.yrl + +GEN_FILES = icparse.erl + +APP_FILE = ic.app +APP_SRC = $(APP_FILE).src +APP_TARGET = $(EBIN)/$(APP_FILE) + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_LOCAL_FLAGS += -pa ../../ic/ebin +# The -pa option is just used temporary until erlc can handle +# includes from other directories than ../include . +ERL_COMPILE_FLAGS += \ + $(ERL_LOCAL_FLAGS) \ + +'{parse_transform,sys_pre_attributes}' \ + +'{attribute,insert,app_vsn,"ic_$(VSN)"}' \ + -D'COMPILERVSN="$(VSN)"' +YRL_FLAGS = -Iicyeccpre.hrl + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +debug: + @${MAKE} TYPE=debug opt + +opt: $(TARGET_FILES) $(APP_TARGET) + +clean: + rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) + rm -f errs core *~ + +$(APP_TARGET): $(APP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ + +docs: + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- +../ebin/icparse.beam: icparse.erl + $(ERLC) $(ERL_COMPILE_FLAGS) +nowarn_unused_vars +nowarn_unused_function -o$(EBIN) +pj $< + +icparse.erl: icparse.yrl icyeccpre.hrl + +### $(ERLC) $(YRL_FLAGS) $< + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/examples + $(INSTALL_DIR) $(RELSYSDIR)/examples/c-client + $(INSTALL_DATA) $(CCL_EX_FILES) $(RELSYSDIR)/examples/c-client + $(INSTALL_DIR) $(RELSYSDIR)/examples/c-server + $(INSTALL_DATA) $(CSRV_EX_FILES) $(RELSYSDIR)/examples/c-server + $(INSTALL_DIR) $(RELSYSDIR)/examples/erl-plain + $(INSTALL_DATA) $(EPL_EX_FILES) $(RELSYSDIR)/examples/erl-plain + $(INSTALL_DIR) $(RELSYSDIR)/examples/erl-genserv + $(INSTALL_DATA) $(ESRV_EX_FILES) $(RELSYSDIR)/examples/erl-genserv + $(INSTALL_DIR) $(RELSYSDIR)/examples/java-client-server + $(INSTALL_DATA) $(JAVA_EX_FILES) $(RELSYSDIR)/examples/java-client-server + $(INSTALL_DIR) $(RELSYSDIR)/examples/all-against-all + $(INSTALL_DATA) $(MIXED_EX_FILES) $(RELSYSDIR)/examples/all-against-all + +release_docs_spec: + diff --git a/lib/ic/src/ic.app.src b/lib/ic/src/ic.app.src new file mode 100644 index 0000000000..29aa6def00 --- /dev/null +++ b/lib/ic/src/ic.app.src @@ -0,0 +1,52 @@ +{application, ic, + [{description, "The IDL Compiler"}, + {vsn, "%VSN%"}, + {modules, + [ + ic, + ic_cclient, + ic_cbe, + ic_cserver, + ic_erlbe, + ic_fetch, + ic_noc, + ic_plainbe, + ic_pp, + ic_pragma, + icenum, + iceval, + icparse, + icpreproc, + icscan, + icstruct, + ictk, + ictype, + ic_array_java, + ic_attribute_java, + ic_code, + ic_codegen, + ic_constant_java, + ic_enum_java, + ic_error, + ic_file, + ic_forms, + ic_genobj, + ic_java_type, + ic_jbe, + ic_options, + ic_sequence_java, + ic_struct_java, + ic_symtab, + ic_union_java, + ic_util, + icunion, + ic_erl_template + ] + }, + {registered, []}, + {applications, [stdlib, kernel]}, + {env, []}, + {mod, {ic, []}} +]}. + + diff --git a/lib/ic/src/ic.erl b/lib/ic/src/ic.erl new file mode 100644 index 0000000000..3c6ce3d9d6 --- /dev/null +++ b/lib/ic/src/ic.erl @@ -0,0 +1,414 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic). + + +-export([sgen/1, gen/1, gen/2, help/0, compile/3]). + + +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ + +-export([filter_params/2, handle_preproc/4, do_gen/4]). + +-import(lists, [foldr/3]). + + +-include("icforms.hrl"). +-include("ic.hrl"). + +-include_lib("stdlib/include/erl_compile.hrl"). + +-export([make_erl_options/1]). % For erlc + +-export([main/3, do_scan/1, do_parse/2, do_type/2]). + + +%%------------------------------------------------------------ +%% +%% Entry point +%% +%%------------------------------------------------------------ + +%% compile(AbsFileName, Outfile, Options) +%% Compile entry point for erl_compile. + +compile(File, _OutFile, Options) -> + case gen(File, make_erl_options(Options)) of + ok -> ok; + Other -> Other + end. + + +%% Entry for the -s switch +sgen(ArgList) -> +%%% io:format("sgen called w ~p~n", [ArgList]), + apply(?MODULE, gen, ArgList). + + +gen(File) -> + gen(File, []). + +gen(File, Opts) -> + G = ic_genobj:new(Opts), + IdlFile = ic_file:add_dot_idl(File), + case ic_options:get_opt(G, show_opts) of + true -> + io:format("Opts: ~p~n", [ic_options:which_opts(G)]); + _ -> ok + end, + ic_genobj:set_idlfile(G, IdlFile), + case catch gen2(G, File, Opts) of + {_, {'EXIT', R}} -> + ic_genobj:free_table_space(G), %% Free space for all ETS tables + io:format("Fatal error : ~p~n",[R]), + error; + {_, {'EXIT', _, R}} -> + ic_genobj:free_table_space(G), %% Free space for all ETS tables + io:format("Fatal error : ~p~n",[R]), + error; + {'EXIT', R} -> + ic_genobj:free_table_space(G), %% Free space for all ETS tables + io:format("Fatal error : ~p~n",[R]), + error; + {'EXIT', _, R} -> + ic_genobj:free_table_space(G), %% Free space for all ETS tables + io:format("Fatal error : ~p~n",[R]), + error; + %% In this case, the pragma registration + %% found errors so this should return error. + error -> + ic_genobj:free_table_space(G), %% Free space for all ETS tables + error; + _ -> + X = ic_error:return(G), + ic_genobj:free_table_space(G), %% Free space for all ETS tables + X + end. + + +gen2(G, File, Opts) -> + case ic_options:get_opt(G, time) of + true -> + time("TOTAL ", ic, main, [G, File, Opts]); + _ -> + case main(G, File, Opts) of + error -> + error; + _ -> + ok + end + end. + + + +do_gen(erl_corba, G, File, T) -> + ic_erlbe:do_gen(G, File, T); +do_gen(erl_template, G, File, T) -> + ic_erl_template:do_gen(G, File, T); +do_gen(erl_genserv, G, File, T) -> + ic_erlbe:do_gen(G, File, T); +do_gen(c_genserv, G, File, T) -> + ic_cclient:do_gen(G, File, T); +do_gen(noc, G, File, T) -> + ic_noc:do_gen(G, File, T); +do_gen(erl_plain, G, File, T) -> + ic_plainbe:do_gen(G, File, T); +do_gen(c_server, G, File, T) -> + ic_cserver:do_gen(G, File, T); +do_gen(c_client, G, File, T) -> + ic_cclient:do_gen(G, File, T); +%% Java backend +do_gen(java, G, File, T) -> + ic_jbe:do_gen(G, File, T); +%% No language choice +do_gen(_,_,_,_) -> + ok. + +do_scan(G) -> + icscan:scan(G, ic_genobj:idlfile(G)). + + +do_parse(G, Tokens) -> + case icparse:parse(Tokens) of + {ok, L} -> L; + X when element(1, X) == error -> + Err = element(2, X), + ic_error:fatal_error(G, {parse_error, element(1, Err), + element(3, Err)}); + X -> exit(X) + end. + + +do_type(G, Form) -> + ictype:type_check(G, Form). + +time(STR,M,F,A) -> + case timer:tc(M, F, A) of + {_, {'EXIT', R}} -> exit(R); + {_, {'EXIT', _, R}} -> exit(R); + {_, _X} when element(1, _X)==error -> throw(_X); + {_T, _R} -> + io:format("Time for ~s: ~10.2f~n", [STR, _T/1000000]), + _R + end. + + + +%% Filters parameters so that only those with certain attributes are +%% seen. The filter parameter is a list of attributes that will be +%% seen, ex. [in] or [inout, out] +filter_params(Filter, Params) -> + lists:filter(fun(P) -> + lists:member(get_param_attr(P#param.inout), Filter) end, + Params). + + +%% Access primitive to get the attribute name (and discard the line +%% number). +get_param_attr({A, _N}) -> A. + + +%% +%% Fixing the preproc directives +%% +handle_preproc(G, _N, line_nr, X) -> + Id = ic_forms:get_id2(X), + Flags = X#preproc.aux, + case Flags of + [] -> ic_genobj:push_file(G, Id); + _ -> + foldr(fun({_, _, "1"}, Gprim) -> ic_genobj:push_file(Gprim, Id); + ({_, _, "2"}, Gprim) -> ic_genobj:pop_file(Gprim, Id); + ({_, _, "3"}, Gprim) -> ic_genobj:sys_file(Gprim, Id) end, + G, Flags) + end; +handle_preproc(G, _N, _Other, _X) -> + G. + + + +%%------------------------------------------------------------ +%% +%% The help department +%% +%% +%% +%%------------------------------------------------------------ + +help() -> + io:format("No help available at the moment~n", []), + ok. + +print_version_str(G) -> + case {ic_options:get_opt(G, silent), ic_options:get_opt(G, silent2)} of + {true, _} -> ok; + {_, true} -> ok; + _ -> + io:format("Erlang IDL compiler version ~s~n", [?COMPILERVSN]) + end. + + + +%% +%% Converts generic compiler options to specific options. +%% +%% Used by erlc +%% + +make_erl_options(Opts) -> + + %% This way of extracting will work even if the record passed + %% has more fields than known during compilation. + + Includes1 = Opts#options.includes, + Defines = Opts#options.defines, + Outdir = Opts#options.outdir, + Warning = Opts#options.warning, + Verbose = Opts#options.verbose, + Specific = Opts#options.specific, + Optimize = Opts#options.optimize, + PreProc = + lists:flatten( + lists:map(fun(D) -> io_lib:format("-I~s ", [ic_util:to_list(D)]) end, + Includes1)++ + lists:map( + fun ({Name, Value}) -> + io_lib:format("-D~s=~s ", [ic_util:to_list(Name), ic_util:to_list(Value)]); + (Name) -> + io_lib:format("-D~s ", [ic_util:to_list(Name)]) + end, + Defines)), + Options = + case Verbose of + true -> []; + false -> [] + end ++ + case Warning of + 0 -> [nowarn]; + _ -> ['Wall'] + end ++ + case Optimize of + 0 -> []; + _ -> [] + end, + + Options++[{outdir, Outdir}, {preproc_flags, PreProc}]++Specific. + + +%%% +%%% NEW main, avoids memory fragmentation +%%% +main(G, File, _Opts) -> + print_version_str(G), + ?ifopt(G, time, io:format("File ~p compilation started : ~p/~p/~p ~p:~2.2.0p~n", + [ic_genobj:idlfile(G), + element(1,date()), + element(2, date()), + element(3, date()), + element(1, time()), + element(2, time())])), + + case ic_options:get_opt(G, help) of + true -> help(); + + _ -> + scanning(G, File) + end. + + + +scanning(G, File) -> + S = ?ifopt2(G, time, + time("input file scanning ", ic, do_scan, [G]), + ic:do_scan(G)), + ?ifopt2(G, tokens, io:format("TOKENS: ~p~n", [S]), + parsing(G, File, S)). + +parsing(G, File, S) -> + T = ?ifopt2(G, + time, + time("input file parsing ", ic, do_parse, [G,S]), + ic:do_parse(G,S)), + ?ifopt2(G, form, io:format("PARSE FORM: ~p~n", [T]), + pragma(G, File, T)). + + + +pragma(G, File, T) -> + case ?ifopt2(G, + time, + time("pragma registration ", ic_pragma, pragma_reg, [G,T]), + ic_pragma:pragma_reg(G,T)) of + %% All pragmas were succesfully applied + {ok,Clean} -> + typing(G, File, Clean); + + error -> + error + end. + + +typing(G, File, Clean) -> + case catch ?ifopt2(G, + time, + time("type code appliance ", ic, do_type, [G,Clean]), + ic:do_type(G,Clean)) of + {'EXIT',Reason} -> + io:format("Error under type appliance : ~p~n",[Reason]), + error; + + T2 -> + ?ifopt2(G, tform, io:format("TYPE FORM: ~p~n", [T2]), + generation(G, File, T2)) + end. + + + +generation(G, File, T2) -> + case ic_options:get_opt(G, multiple_be) of + false -> + single_generation(G, File, T2); + List -> + OutDir = + case ic_options:get_opt(G, outdir) of + false -> + []; + Dir -> + Dir + end, + + case ic_options:get_opt(G, be) of + false -> + ok; + Be -> + %% Generate this first + ic_options:add_opt(G,[{outdir,OutDir++atom_to_list(Be)}],true), + single_generation(G, File, T2) + end, + multiple_generation(G, File, T2, OutDir, List) + end. + +multiple_generation(_G, _File, _T2, _RootDir, []) -> + ok; +multiple_generation(G, File, T2, RootDir, [Be|Bes]) -> + ic_options:add_opt(G,[{outdir,RootDir++atom_to_list(Be)}],true), + ic_options:add_opt(G,[{be,Be}],true), + single_generation(G, File, T2), + + case ic_error:get_error_count(G) of + 0 -> + multiple_generation(G,File,T2,RootDir,Bes); + _ -> + %% Errors reported, abort + ok + end. + + +single_generation(G, File, T2) -> + case ic_error:get_error_count(G) of + 0 -> + %% Check if user has sett backend option + case ic_options:get_opt(G, be) of + false -> + %% Use default backend option + DefaultBe = ic_options:defaultBe(), + ic_options:add_opt(G,[{be,DefaultBe}],true), + + ?ifopt2(G, + time, + time("code generation ", ic, do_gen, [DefaultBe, G, File, T2]), + ic:do_gen(DefaultBe, G, File, T2)); + Be -> + %% Use user defined backend + ?ifopt2(G, + time, + time("code generation ", ic, do_gen, [Be, G, File, T2]), + ic:do_gen(Be, G, File, T2)) + end; + _ -> + ok %% Does not matter + end. + + + diff --git a/lib/ic/src/ic.hrl b/lib/ic/src/ic.hrl new file mode 100644 index 0000000000..974e6088f4 --- /dev/null +++ b/lib/ic/src/ic.hrl @@ -0,0 +1,158 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + + +%%------------------------------------------------------------ +%% Configuration macros +-define(CORBAMOD, corba). +-define(ORBNAME, orber). +-define(CORBAHRL, "corba.hrl"). +-define(CALL, "call"). +-define(CAST, "cast"). +-define(IFRREGID, "register"). +-define(IFRTYPESHRL, "ifr_types.hrl"). + +-define(GENSERVMOD, gen_server). + +%%------------------------------------------------------------ +%% Flags. NOTE! Once assigned value may NOT be changed. Deprecate ok. +%% Default flags. Can be changed if we change the default behavior. +-define(IC_FLAG_TEMPLATE_1, 16#01). +-define(IC_FLAG_TEMPLATE_2, 16#02). + +-define(IC_INIT_FLAGS, 16#00). + +%% Flag operations +%% USAGE: Boolean = ?IC_FLAG_TEST(Flags, ?IC_ATTRIBUTE) +-define(IC_FLAG_TEST(_F1, _I1), ((_F1 band _I1) == _I1)). + +%% USAGE: NewFlags = ?IC_SET_TRUE(Flags, ?IC_ATTRIBUTE) +-define(IC_SET_TRUE(_F2, _I2), (_I2 bor _F2)). + +%% USAGE: NewFlags = ?IC_SET_FALSE(Flags, ?IC_ATTRIBUTE) +-define(IC_SET_FALSE(_F3, _I3), ((_I3 bxor 16#ff) band _F3)). + +%% USAGE: NewFlags = ?IC_SET_FALSE_LIST(Flags, [?IC_SEC_ATTRIBUTE, ?IC_SOME]) +-define(IC_SET_FALSE_LIST(_F4, _IList1), + lists:foldl(fun(_I4, _F5) -> + ((_I4 bxor 16#ff) band _F5) + end, + _F4, _IList1)). + +%% USAGE: NewFlags = ?IC_SET_TRUE_LIST(Flags, [?IC_ATTRIBUTE, ?IC_SOME]) +-define(IC_SET_TRUE_LIST(_F6, _IList2), + lists:foldl(fun(_I6, _F7) -> + (_I6 bor _F7) + end, + _F6, _IList2)). + +%% USAGE: Boolean = ?IC_FLAG_TEST_LIST(Flags, [?IC_CONTEXT, ?IC_THING]) +-define(IC_FLAG_TEST_LIST(_F8, _IList3), + lists:all(fun(_I7) -> + ((_F8 band _I7) == _I7) + end, + _IList3)). + + +%%------------------------------------------------------------ +%% Usefull macros + +-define(ifthen(P,ACTION), if P -> ACTION; true->true end). + + +%%------------------------------------------------------------ +%% Option macros + +-define(ifopt(G,OPT,ACTION), + case ic_options:get_opt(G,OPT) of true -> ACTION; _ -> ok end). + +-define(ifopt2(G,OPT,ACT1,ACT2), + case ic_options:get_opt(G,OPT) of true -> ACT1; _ -> ACT2 end). + +-define(ifnopt(G,OPT,ACTION), + case ic_options:get_opt(G,OPT) of false -> ACTION; _ -> ok end). + + +%% Internal record +-record(id_of, {id, type, tk}). + +%%-------------------------------------------------------------------- +%% The generator object definition + +-record(genobj, {symtab, impl, options, warnings, auxtab, + tktab, pragmatab, c_typedeftab, + skelfile=[], skelfiled=[], skelscope=[], + stubfile=[], stubfiled=[], stubscope=[], + includefile=[], includefiled=[], + interfacefile=[],interfacefiled=[], + helperfile=[],helperfiled=[], + holderfile=[],holderfiled=[], + filestack=0, do_gen=true, sysfile=false}). + +%%-------------------------------------------------------------------- +%% The scooped id definition +-record(scoped_id, {type=local, line=-1, id=""}). + + + + + + + + +%%-------------------------------------------------------------------- +%% Secret macros +%% +%% NOTE these macros are not general, they cannot be used +%% everywhere. +%% +-define(lookup(T,K), case ets:lookup(T, K) of [{_X, _Y}] -> _Y; _->[] end). +-define(insert(T,K,V), ets:insert(T, {K, V})). + + +%%--------------------------------------------------------------------- +%% +%% Java specific macros +%% +%% +-define(ERLANGPACKAGE,"com.ericsson.otp.erlang."). +-define(ICPACKAGE,"com.ericsson.otp.ic."). + + +%% +%% Macros for reporting encode/decode errors in C back-ends. +%% +%% + +-define(emit_c_enc_rpt(Fd, Fill, Fmt, Vals), + begin + CType = ic_cbe:mk_c_type2(G, N, T), + ic_codegen:emit_c_enc_rpt(Fd, Fill, "~s : " ++ Fmt, [CType| Vals]) + end). +-define(emit_c_dec_rpt(Fd, Fill, Fmt, Vals), + begin + CType = ic_cbe:mk_c_type2(G, N, T), + ic_codegen:emit_c_dec_rpt(Fd, Fill, "~s : " ++ Fmt, [CType| Vals]) + end). + + + + + diff --git a/lib/ic/src/ic_array_java.erl b/lib/ic/src/ic_array_java.erl new file mode 100644 index 0000000000..e21d646bf5 --- /dev/null +++ b/lib/ic/src/ic_array_java.erl @@ -0,0 +1,295 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_array_java). + +-export([gen/4]). + +-include("ic.hrl"). +-include("icforms.hrl"). + + +gen(G, N, X, Array) when is_record(X, member) -> + ArrayName = ic_forms:get_java_id(Array), + ArrayElement = ic_forms:get_type(X), + emit_holder_class(G, N, X, Array, ArrayName, ArrayElement), + emit_helper_class(G, N, X, Array, ArrayName, ArrayElement); +gen(G, N, X, Array) when is_record(X, case_dcl) -> + ArrayName = ic_forms:get_java_id(Array), + ArrayElement = ic_forms:get_type(X), + emit_holder_class(G, N, X, Array, ArrayName, ArrayElement), + emit_helper_class(G, N, X, Array, ArrayName, ArrayElement); +gen(G, N, X, Array) -> + ArrayName = ic_forms:get_java_id(Array), + ArrayElement = ic_forms:get_body(X), + emit_holder_class(G, N, X, Array, ArrayName, ArrayElement), + emit_helper_class(G, N, X, Array, ArrayName, ArrayElement). + + + +%%----------------------------------------------------------------- +%% Func: emit_holder_class/4 +%%----------------------------------------------------------------- +emit_holder_class(G, N, _X, Array, ArrayName, ArrayElement) -> + SName = string:concat(ArrayName, "Holder"), + {Fd, _}= ic_file:open_java_file(G, N, SName), + + ArrayElementName = ic_java_type:getType(G, N, ArrayElement), + EmptyDim = arrayEmptyDim(Array), + + ic_codegen:emit(Fd, "final public class ~sHolder {\n",[ArrayName]), + + ic_codegen:emit(Fd, " // instance variables\n", []), + ic_codegen:emit(Fd, " public ~s~s value;\n\n", + [ArrayElementName,EmptyDim]), + + ic_codegen:emit(Fd, " // constructors\n", []), + ic_codegen:emit(Fd, " public ~sHolder() {}\n", [ArrayName]), + ic_codegen:emit(Fd, " public ~sHolder(~s~s initial) {\n", + [ArrayName,ArrayElementName,EmptyDim]), + ic_codegen:emit(Fd, " value = initial;\n", []), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, " // methods\n", []), + + ic_codegen:emit(Fd, " public void _marshal(~sOtpOutputStream out)\n", [?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n"), + ic_codegen:emit(Fd, " ~sHelper.marshal(out, value);\n", [ArrayName]), + ic_codegen:emit(Fd, " }\n"), + ic_codegen:nl(Fd), + ic_codegen:emit(Fd, " public void _unmarshal(~sOtpInputStream in)\n", [?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n"), + ic_codegen:emit(Fd, " value = ~sHelper.unmarshal(in);\n", [ArrayName]), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, "}\n", []), + file:close(Fd). + + +%%----------------------------------------------------------------- +%% Func: emit_helper_class/4 +%%----------------------------------------------------------------- +emit_helper_class(G, N, X, Array, ArrayName, ArrayElement) -> + SName = string:concat(ArrayName, "Helper"), + {Fd, _}= ic_file:open_java_file(G, N, SName), + + ArrayElementName = ic_java_type:getType(G, N, ArrayElement), + EmptyDim = arrayEmptyDim(Array), +% Dim = arrayDim(G,N,Array), + + ic_codegen:emit(Fd, "public class ~sHelper {\n",[ArrayName]), + + ic_codegen:emit(Fd, " // constructors\n"), + ic_codegen:emit(Fd, " private ~sHelper() {}\n\n", [ArrayName]), + + ic_codegen:emit(Fd, " // methods\n"), + + ic_codegen:emit(Fd, " public static void marshal(~sOtpOutputStream _out, ~s~s _value)\n", + [?ERLANGPACKAGE,ArrayElementName,EmptyDim]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + emit_array_marshal_loop(G,N,X,Array,ArrayElement,Fd), + ic_codegen:emit(Fd, " }\n"), + ic_codegen:nl(Fd), + ic_codegen:emit(Fd, " public static ~s~s unmarshal(~sOtpInputStream _in)\n", + [ArrayElementName,EmptyDim,?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + ic_codegen:emit(Fd, " ~s~s _value = new ~s;\n\n", + [ArrayElementName,EmptyDim,ic_java_type:getFullType(G, N, X, Array)]), + emit_array_unmarshal_loop(G,N,X,Array,ArrayElement,Fd), + ic_codegen:emit(Fd, " return _value;\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static String id() {\n", []), + ic_codegen:emit(Fd, " return ~p;\n",[ictk:get_IR_ID(G, N, Array)]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static String name() {\n", []), + ic_codegen:emit(Fd, " return ~p;\n",[ArrayName]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_jbe:emit_type_function(G, N, X, Fd), + + ic_codegen:emit(Fd, " public static void insert(~sAny _any, ~s~s _this)\n", + [?ICPACKAGE,ArrayElementName,EmptyDim]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " ~sOtpOutputStream _os = \n",[?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " new ~sOtpOutputStream();\n\n",[?ERLANGPACKAGE]), + + ic_codegen:emit(Fd, " _any.type(type());\n"), + ic_codegen:emit(Fd, " marshal(_os, _this);\n"), + ic_codegen:emit(Fd, " _any.insert_Streamable(_os);\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static ~s~s extract(~sAny _any)\n", + [ArrayElementName,EmptyDim,?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " return unmarshal(_any.extract_Streamable());\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, "}\n"), + file:close(Fd). + + + + +emit_array_marshal_loop(G,N,X,Array,AEl,Fd) -> + DimList = mk_array_dim_list(G,N,Array), + emit_array_marshal_loop_1(G,N,X,Array,AEl,DimList,0,Fd). + + +emit_array_marshal_loop_1(G,N,X,Array,AEl,[D],C,Fd) -> + + DimList = mk_array_dim_list(G,N,Array), + + ic_codegen:emit(Fd, " _out.write_tuple_head(~s);\n\n",[D]), + + ic_codegen:emit(Fd, " for(int _tmp~p = 0; _tmp~p < ~s; _tmp~p++)\n",[C,C,D,C]), + + case ic_java_type:isBasicType(G, N, AEl) of + true -> + ic_codegen:emit(Fd, " _out~s(_value", + [ic_java_type:marshalFun(G, N, X, AEl)]); + false -> + ic_codegen:emit(Fd, " ~s(_out, _value", + [ic_java_type:marshalFun(G, N, X, AEl)]) + end, + + emit_array_dimensions(DimList,0,Fd), + + ic_codegen:emit(Fd, ");\n\n"); + +emit_array_marshal_loop_1(G,N,X,Array,AEl,[D|Ds],C,Fd) -> +% DimList = mk_array_dim_list(G,N,Array), + + ic_codegen:emit(Fd, " _out.write_tuple_head(~s);\n\n",[D]), + + ic_codegen:emit(Fd, " for(int _tmp~p = 0; _tmp~p < ~s; _tmp~p++) {\n",[C,C,D,C]), + + emit_array_marshal_loop_1(G,N,X,Array,AEl,Ds,C+1,Fd), + + ic_codegen:emit(Fd, " }\n\n"). + + + + + +emit_array_unmarshal_loop(G,N,X,Array,AEl,Fd) -> + DimList = mk_array_dim_list(G,N,Array), + case length(DimList) > 0 of + true -> + ic_codegen:emit(Fd, " _in.read_tuple_head();\n\n"), + + ic_codegen:emit(Fd, " for(int _tmp0 = 0; _tmp0 < ~s; _tmp0++) {\n\n",[hd(DimList)]), + emit_array_unmarshal_loop_1(G,N,X,Array,AEl,tl(DimList),1,Fd), + ic_codegen:emit(Fd, " }\n\n"); + false -> + emit_array_unmarshal_loop_1(G,N,X,Array,AEl,DimList,0,Fd) + end. + +emit_array_unmarshal_loop_1(G,N,X,_Array,AEl,[],1,Fd) -> %% One dimensional array + case ic_java_type:isBasicType(G, N, AEl) of + true -> + ic_codegen:emit(Fd, " _value[_tmp0] = _in~s;\n", + [ic_java_type:unMarshalFun(G, N, X, AEl)]); + false -> + ic_codegen:emit(Fd, " _value[_tmp0] = ~s.unmarshal(_in);\n\n", + [ic_java_type:getUnmarshalType(G, N, X, AEl)]) + end; +emit_array_unmarshal_loop_1(G,N,X,Array,AEl,[],_C,Fd) -> + DimList = mk_array_dim_list(G,N,Array), + ic_codegen:emit(Fd, " _value"), + emit_array_dimensions(DimList,0,Fd), + case ic_java_type:isBasicType(G,N,AEl) of + true -> + ic_codegen:emit(Fd, " = _in~s;\n", + [ic_java_type:unMarshalFun(G, N, X, AEl)]); + false -> + ic_codegen:emit(Fd, " = ~s.unmarshal(_in);\n", + [ic_java_type:getUnmarshalType(G, N, X, AEl)]) + end; +emit_array_unmarshal_loop_1(G,N,X,Array,AEl,[D|Ds],C,Fd) -> + ic_codegen:emit(Fd, " _in.read_tuple_head();\n\n"), + + ic_codegen:emit(Fd, " for(int _tmp~p = 0; _tmp~p < ~s; _tmp~p++) {\n\n",[C,C,D,C]), + emit_array_unmarshal_loop_1(G,N,X,Array,AEl,Ds,C+1,Fd), + ic_codegen:emit(Fd, " }\n"). + + + + + +%%--------------------------------------------------- +%% Utilities +%%--------------------------------------------------- + +mk_array_dim_list(G,N,Array) -> + mk_array_dim_list2(G,N,Array#array.size). + + +mk_array_dim_list2(_G,_N,[]) -> + []; + +mk_array_dim_list2(G,N,[D |Ds]) when is_record(D,scoped_id) -> + {FSN, _, _, _} = ic_symtab:get_full_scoped_name(G, N, D), + [ ic_util:to_dot(G,FSN) | mk_array_dim_list2(G,N,Ds)]; + +mk_array_dim_list2(G,N,[D |Ds]) -> + [ic_util:eval_java(G,N,D) | mk_array_dim_list2(G,N,Ds)]. + + + +%% Array dimension string +%arrayDim(G,N,X) -> +% arrayDim2(G,N,X#array.size). + +%arrayDim2(_G,_N,[]) -> +% ""; +%arrayDim2(G,N,[D|Ds]) when record(D,scoped_id) -> +% {FSN, _, _, _} = ic_symtab:get_full_scoped_name(G, N, D), +% "[" ++ ic_util:to_dot(G,FSN) ++ "]" ++ arrayDim2(G,N,Ds); +%arrayDim2(G,N,[D|Ds]) -> +% "[" ++ ic_util:eval_java(G,N,D) ++ "]" ++ arrayDim2(G,N,Ds). + + +%% Array Empty dimension string +arrayEmptyDim(X) -> + arrayEmptyDim2(X#array.size). + +arrayEmptyDim2([_D]) -> + "[]"; +arrayEmptyDim2([_D |Ds]) -> + "[]" ++ arrayEmptyDim2(Ds). + + +emit_array_dimensions([_D],C,Fd) -> + ic_codegen:emit(Fd, "[_tmp~p]",[C]); +emit_array_dimensions([_D|Ds],C,Fd) -> + ic_codegen:emit(Fd, "[_tmp~p]",[C]), + emit_array_dimensions(Ds,C+1,Fd). + + + + + + diff --git a/lib/ic/src/ic_attribute_java.erl b/lib/ic/src/ic_attribute_java.erl new file mode 100644 index 0000000000..6352dcf608 --- /dev/null +++ b/lib/ic/src/ic_attribute_java.erl @@ -0,0 +1,412 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_attribute_java). + +-include("icforms.hrl"). +-include("ic.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([emit_attribute_prototype/4, + emit_attribute_stub_code/4, + emit_atrribute_on_dictionary/5, + emit_attribute_switch_case/5]). + + + + + +%%%----------------------------------------------------- +%%% +%%% Generates operation in interface +%%% +%%%----------------------------------------------------- +emit_attribute_prototype(G, N, X, Fd) -> + emit_attribute_prototype(G, N, X, Fd, ic_forms:get_idlist(X)). + +emit_attribute_prototype(_G, _N, _X, _Fd, []) -> + ok; +emit_attribute_prototype(G, N, X, Fd, [V|Vs]) -> + WireAttrName = ic_forms:get_id(V), + AttrName = ic_forms:get_java_id(WireAttrName), + emit_attr_prototype(G, N, X, Fd, AttrName,WireAttrName), + emit_attribute_prototype(G, N, X, Fd, Vs). + + +emit_attr_prototype(G, N, X, Fd, OpName, WireOpName) -> + + ic_codegen:emit(Fd, "/****\n"), + ic_codegen:emit(Fd, " * Attribute ~p interface functions \n", [ic_util:to_colon([WireOpName|N])]), + ic_codegen:emit(Fd, " *\n"), + ic_codegen:emit(Fd, " */\n\n"), + + AT = ic_forms:get_type(X), + Type = ic_java_type:getType(G, N, AT), +% HolderType = ic_java_type:getHolderType(G, N, AT), + + ic_codegen:emit(Fd, " ~s ~s() throws java.lang.Exception;\n\n",[Type, OpName]), + + case X#attr.readonly of + {readonly, _} -> + ok; + _ -> + ic_codegen:emit(Fd, " void ~s(~s _value) throws java.lang.Exception;\n\n",[OpName, Type]) + end. + + + +%%%----------------------------------------------------- +%%% +%%% Generates attribute insertion in dictionary +%%% +%%%----------------------------------------------------- +emit_atrribute_on_dictionary(G, N, X, Fd, C) -> + emit_atrribute_on_dictionary(G, N, X, Fd, C, ic_forms:get_idlist(X)). + +emit_atrribute_on_dictionary(_G, _N, _X, _Fd, C, []) -> + C; +emit_atrribute_on_dictionary(G, N, X, Fd, C, [V|Vs]) -> + + WireAttrName = ic_forms:get_id(V), + + ic_codegen:emit(Fd, " _operations.put(\"_get_~s\", new java.lang.Integer(~p));\n", + [WireAttrName,C]), + + case X#attr.readonly of + {readonly, _} -> + + emit_atrribute_on_dictionary(G, N, X, Fd, C+1, Vs); + + _ -> + + ic_codegen:emit(Fd, " _operations.put(\"_set_~s\", new java.lang.Integer(~p));\n", + [WireAttrName,C+1]), + + emit_atrribute_on_dictionary(G, N, X, Fd, C+2, Vs) + end. + + + +%%%----------------------------------------------------- +%%% +%%% Generates attribute case in server switch +%%% +%%%----------------------------------------------------- +emit_attribute_switch_case(G, N, X, Fd, C) -> + Tk = ic_forms:get_tk(X), + emit_attribute_switch_case(G, N, X, Fd, Tk, C, ic_forms:get_idlist(X)). + +emit_attribute_switch_case(_G, _N, _X, _Fd, _Tk, C, []) -> + C; +emit_attribute_switch_case(G, N, X, Fd, Tk, C, [V|Vs]) -> + AttrName = ic_forms:get_java_id(V), + + emit_attribute_switch_case1(G,N,X,Fd,"_get_",AttrName,Tk,C), + + case X#attr.readonly of + {readonly, _} -> + emit_attribute_switch_case(G, N, X, Fd, Tk, C+1, Vs); + + _ -> + emit_attribute_switch_case1(G,N,X,Fd,"_set_",AttrName,Tk,C+1), + emit_attribute_switch_case(G, N, X, Fd, Tk, C+2, Vs) + end. + + +emit_attribute_switch_case1(G, N, X, Fd, "_get_", Name, _Tk, C) -> + + R = ic_forms:get_type(X), + RT = ic_java_type:getParamType(G,N,R,ret), + + ic_codegen:emit(Fd, " case ~p: { // Get operation for attribute ~s\n\n",[C,ic_util:to_dot([Name|N])]), + + ic_codegen:emit(Fd, " // Calling implementation function\n"), + ic_codegen:emit(Fd, " ~s _result = this.~s();\n\n", [RT, Name]), + + ic_codegen:emit(Fd, " // Marshalling output\n"), + ic_codegen:emit(Fd, " ~sOtpErlangRef __ref = __env.getSref();\n",[?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_ref(__ref.node(),__ref.id(),__ref.creation()); // Call reference\n"), + + case ic_java_type:isBasicType(G,N,R) of + true -> + ic_codegen:emit(Fd, " __os~s(_result); // Return value\n\n", + [ic_java_type:marshalFun(G,N,X,R)]); + false -> + ic_codegen:emit(Fd, " ~s(__os,_result); // Return value\n\n", + [ic_java_type:marshalFun(G,N,X,R)]) + end, + + ic_codegen:emit(Fd, " } break;\n\n"); + + +emit_attribute_switch_case1(G, N, X, Fd, "_set_", Name, _Tk, C) -> + ic_codegen:emit(Fd, " case ~p: { // Set operation for attribute ~s\n\n",[C,ic_util:to_dot([Name|N])]), + + Type = ic_forms:get_type(X), + + ic_codegen:emit(Fd, " // Preparing input\n"), + ic_codegen:emit(Fd, " ~sOtpInputStream __is = __env.getIs();\n",[?ERLANGPACKAGE]), + + case ic_java_type:isBasicType(G,N,Type) of + true -> + ic_codegen:emit(Fd, " ~s _value = __is~s; // In value\n\n", + [ic_java_type:getParamType(G,N,Type,in), + ic_java_type:unMarshalFun(G,N,X,Type)]); + false -> + ic_codegen:emit(Fd, " ~s _value = ~s.unmarshal(__is); // In value\n\n", + [ic_java_type:getParamType(G,N,Type,in), + ic_java_type:getUnmarshalType(G,N,X,Type)]) + end, + + + ic_codegen:emit(Fd, " // Calling implementation function\n"), + ic_codegen:emit(Fd, " this.~s(_value);\n\n", [Name]), + + ic_codegen:emit(Fd, " // Marshalling output\n"), + ic_codegen:emit(Fd, " ~sOtpErlangRef __ref = __env.getSref();\n",[?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_ref(__ref.node(),__ref.id(),__ref.creation()); // Call reference\n"), + ic_codegen:emit(Fd, " __os.write_atom(\"ok\");\n\n"), + + ic_codegen:emit(Fd, " } break;\n\n"). + + + + + + + +%%%----------------------------------------------------- +%%% +%%% Generates attribute function in stub +%%% +%%%----------------------------------------------------- +emit_attribute_stub_code(G, N, X, Fd) -> + emit_attribute_stub_code(G, N, X, Fd, ic_forms:get_idlist(X)). + +emit_attribute_stub_code(_G, _N, _X, _Fd, []) -> + ok; +emit_attribute_stub_code(G, N, X, Fd, [V|Vs]) -> + WireAttrName = ic_forms:get_id(V), + AttrName = ic_forms:get_java_id(WireAttrName), + + emit_attribute_stub_code1(G,N,X,Fd,"_get_",AttrName,WireAttrName), + + case X#attr.readonly of + {readonly, _} -> + emit_attribute_stub_code(G, N, X, Fd, Vs); + + _ -> + emit_attribute_stub_code1(G,N,X,Fd,"_set_",AttrName,WireAttrName), + emit_attribute_stub_code(G, N, X, Fd, Vs) + end. + + +emit_attribute_stub_code1(G,N,X,Fd,"_get_",Name,WireName) -> + + Type = ic_forms:get_type(X), + RT = ic_java_type:getType(G,N,Type), + + %% + %% Main get operation + %% + ic_codegen:emit(Fd, " // Attribute ~p get operation implementation\n", [ic_util:to_colon([WireName|N])]), + ic_codegen:emit(Fd, " public ~s ~s() throws java.lang.Exception {\n\n", [RT, Name]), + + %% Function marshal call + ic_codegen:emit(Fd, " // Calling the marshal function\n"), + ic_codegen:emit(Fd, " _~s_marshal(_env);\n\n", [Name]), + + %% Sending call + ic_codegen:emit(Fd, " // Message send\n"), + ic_codegen:emit(Fd, " _env.send();\n\n"), + + %% Receiving return value + ic_codegen:emit(Fd, " // Message receive\n"), + ic_codegen:emit(Fd, " _env.receive();\n\n"), + + ic_codegen:emit(Fd, " // Calling the unmarshal function\n"), + ic_codegen:emit(Fd, " return _~s_get_unmarshal(_env);\n", [Name]), + ic_codegen:emit(Fd, " }\n\n"), + + + %% + %% Marshal get operation + %% + ic_codegen:emit(Fd, " // Marshal operation for get attribute ~p\n", [Name]), + ic_codegen:emit(Fd, " public static void _~s_marshal(~sEnvironment __env)\n", + [Name, ?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Get output stream\n"), + ic_codegen:emit(Fd, " ~sOtpOutputStream __os = __env.getOs();\n\n",[?ERLANGPACKAGE]), + + %% Initiating Message header + ic_codegen:emit(Fd, " // Message header assembly\n"), + ic_codegen:emit(Fd, " __os.reset();\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(3);\n"), + ic_codegen:emit(Fd, " __os.write_atom(\"$gen_call\");\n\n"), + + + %% Creating call identity tuple + ic_codegen:emit(Fd, " // Message identity part creation\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __env.write_client_pid();\n"), + ic_codegen:emit(Fd, " __env.write_client_ref();\n\n"), + + OpCallName = case ic_options:get_opt(G, scoped_op_calls) of + true -> + ic_util:to_undersc(["_get_"++WireName|N]); + false -> + "_get_"++WireName + end, + + %% Creating operation identity + ic_codegen:emit(Fd, " // Message operation part creation\n"), + ic_codegen:emit(Fd, " __os.write_atom(~p);\n\n",[OpCallName]), + + ic_codegen:emit(Fd, " }\n\n"), + + + %% + %% Unmarshal get operation + %% + MRT = ic_java_type:getParamType(G,N,Type,ret), + + ic_codegen:emit(Fd, " // Unmarshal operation for get attribute ~p\n", [Name]), + ic_codegen:emit(Fd, " public static ~s _~s_get_unmarshal(~sEnvironment __env)\n", + [MRT, Name, ?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + + ic_codegen:emit(Fd, " // Get input stream\n"), + ic_codegen:emit(Fd, " ~sOtpInputStream __is = __env.getIs();\n\n",[?ERLANGPACKAGE]), + + ic_codegen:emit(Fd, " // Extracting return value\n"), + case ic_java_type:isBasicType(G, N, Type) of + true -> + ic_codegen:emit(Fd, " return __is~s;\n", + [ic_java_type:unMarshalFun(G, N, X, Type)]); + false -> + ic_codegen:emit(Fd, " return ~s.unmarshal(__is);\n", + [ic_java_type:getUnmarshalType(G, N, X, Type)]) + end, + + ic_codegen:emit(Fd, " }\n\n"); + + +emit_attribute_stub_code1(G,N,X,Fd,"_set_",Name,WireName) -> + + Type = ic_forms:get_type(X), + + %% + %% Main set operation + %% + IT = ic_java_type:getType(G,N,Type), + + ic_codegen:emit(Fd, " // Attribute ~p set operation implementation\n", [ic_util:to_colon([WireName|N])]), + ic_codegen:emit(Fd, " public void ~s(~s _value) throws java.lang.Exception {\n\n", [Name,IT]), + + %% Function marshal call + ic_codegen:emit(Fd, " // Calling the marshal function\n"), + ic_codegen:emit(Fd, " _~s_marshal(_env, _value);\n\n", [Name]), + + %% Sending call + ic_codegen:emit(Fd, " // Message send\n"), + ic_codegen:emit(Fd, " _env.send();\n\n"), + + %% Receiving return value + ic_codegen:emit(Fd, " // Message receive\n"), + ic_codegen:emit(Fd, " _env.receive();\n\n"), + + ic_codegen:emit(Fd, " // Calling the unmarshal function\n"), + ic_codegen:emit(Fd, " _~s_set_unmarshal(_env);\n", [Name]), + + ic_codegen:emit(Fd, " }\n\n"), + + + %% + %% Marshal set operation + %% + IP = ic_java_type:getParamType(G, N, Type, in), + OpCallName = case ic_options:get_opt(G, scoped_op_calls) of + true -> + ic_util:to_undersc(["_set_"++WireName|N]); + false -> + "_set_"++WireName + end, + + ic_codegen:emit(Fd, " // Marshal operation for set attribute ~p\n", [Name]), + ic_codegen:emit(Fd, " public static void _~s_marshal(~sEnvironment __env, ~s _value)\n", + [Name, ?ICPACKAGE, IP]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Get output stream\n"), + ic_codegen:emit(Fd, " ~sOtpOutputStream __os = __env.getOs();\n\n",[?ERLANGPACKAGE]), + + %% Initiating Message header + ic_codegen:emit(Fd, " // Message header assembly\n"), + ic_codegen:emit(Fd, " __os.reset();\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(3);\n"), + ic_codegen:emit(Fd, " __os.write_atom(\"$gen_call\");\n\n"), + + + %% Creating call identity tuple + ic_codegen:emit(Fd, " // Message identity part creation\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __env.write_client_pid();\n"), + ic_codegen:emit(Fd, " __env.write_client_ref();\n\n"), + + + %% Creating operation identity + ic_codegen:emit(Fd, " // Message operation part creation\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_atom(~p);\n",[OpCallName]), + + case ic_java_type:isBasicType(G, N, Type) of + true -> + ic_codegen:emit(Fd, " __os~s(_value);\n\n", + [ic_java_type:marshalFun(G, N, X, Type)]); + false -> + ic_codegen:emit(Fd, " ~s(__os, _value);\n\n", + [ic_java_type:marshalFun(G, N, X, Type)]) + end, + ic_codegen:emit(Fd, " }\n\n"), + + + ic_codegen:emit(Fd, " // Unmarshal operation for set attribute ~p\n", [Name]), + ic_codegen:emit(Fd, " public static void _~s_set_unmarshal(~sEnvironment __env)\n", + [Name, ?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Get input stream\n"), + ic_codegen:emit(Fd, " ~sOtpInputStream __is = __env.getIs();\n\n",[?ERLANGPACKAGE]), + + ic_codegen:emit(Fd, " __is.read_atom();\n"), + ic_codegen:emit(Fd, " }\n\n"). + + + + + + diff --git a/lib/ic/src/ic_cbe.erl b/lib/ic/src/ic_cbe.erl new file mode 100644 index 0000000000..1000e0d962 --- /dev/null +++ b/lib/ic/src/ic_cbe.erl @@ -0,0 +1,1306 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +%%------------------------------------------------------------ +%% +%% This module is a main module for generation of C code, both +%% for ic_cclient and ic_cserver. +%% +%% The former role of this module (ic_cbe) was to generate client +%% code only. +%% +-module(ic_cbe). + +-export([emit_malloc_size_stmt/7, emit_encoding_stmt/6, + emit_encoding_stmt/7, emit_decoding_stmt/10, + emit_decoding_stmt/11, emit_dealloc_stmts/3, + mk_variable_name/1, mk_c_type/3, mk_c_type/4, mk_c_type2/3, + is_variable_size/1, is_variable_size/3, mk_dim/1, + mk_slice_dim/1, emit_tmp_variables/1, store_tmp_decl/2, + extract_info/3, normalize_type/1]). + +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ + +-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"). + +%%------------------------------------------------------------ +%% ENCODING +%%------------------------------------------------------------ + +emit_encoding_stmt(G, N, Fd, T, LName, OutBuffer) when element(1, T) == scoped_id -> + case mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_pid(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_port(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n} \n"); + "erlang_ref" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ref(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + "ETERM*" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_term(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + {enum, FSN} -> + emit_encoding_stmt(G, N, Fd, FSN, LName, OutBuffer); + FSN -> + emit_encoding_stmt(G, N, Fd, FSN, LName, OutBuffer) + end; + +%% XXX T is a string +emit_encoding_stmt(G, N, Fd, T, LName, _OutBuffer) when is_list(T) -> + %% Already a fullscoped name + Type = ictype:name2type(G,T), + case ictype:isBasicType(Type) of + true -> + emit_encoding_stmt_for_basic_type(G, N, T, Fd, Type, LName); + false -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, ~s))" + " < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), % XXX list + emit(Fd, " return oe_error_code;\n }\n") + end; +emit_encoding_stmt(G, N, Fd, T, LName, _OutBuffer) when is_record(T, string) -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_encode_string(oe_env, " + " ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); +emit_encoding_stmt(G, N, Fd, T, LName, _OutBuffer) when is_record(T, wstring) -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_encode_wstring(oe_env, " + "~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); +emit_encoding_stmt(G, N, Fd, T, LName, _OutBuffer) -> + case normalize_type(T) of + {basic, Type} -> + emit_encoding_stmt_for_basic_type(G, N, T, Fd, Type, LName); + %% XXX Why only returns? + {void, _} -> + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + {sequence, _, _} -> + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + {_ArrayType, {array, _, _}} -> + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + {union, _, _, _, _} -> + %% Union as a member in struct ! + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + {struct, _, _, _} -> + %% Struct as a member in struct ! + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end. + +%% Arity = 7. +%% +emit_encoding_stmt(G, N, X, Fd, T, LName, OutBuffer) when element(1, T) == scoped_id -> + case mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_pid(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_port(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_ref" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ref(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + "ETERM*" -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_term(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + {enum, FSN} -> + emit_encoding_stmt(G, N, X, Fd, FSN, LName, OutBuffer); + FSN -> + emit_encoding_stmt(G, N, X, Fd, FSN, LName, OutBuffer) + end; + +%% XXX T is a string +emit_encoding_stmt(G, N, X, Fd, T, LName, _OutBuffer) when is_list(T) -> + %% Already a fullscoped name + case get_param_tk(LName,X) of + error -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + ParamTK -> + case is_variable_size(ParamTK) of + true -> + if is_tuple(ParamTK) -> + case element(1,ParamTK) of + tk_array -> + %% Array of dynamic data + emit(Fd, + " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, + "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, + " return " + "oe_error_code;\n }\n"); + _ -> + emit(Fd, + " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, + "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return " + "oe_error_code;\n }\n") + end; + true -> + emit(Fd, + " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n") + end; + false -> + if is_atom(ParamTK) -> + case normalize_type(ParamTK) of + {basic, Type} -> + emit_encoding_stmt_for_basic_type(G, N, T, Fd, + Type, + LName); + _ -> + %% Why only return? + ?emit_c_enc_rpt(Fd, " ", "~/slist/~s", [T, LName]), + emit(Fd, " return oe_error_code;\n }\n"), + ok + end; + true -> + case element(1,ParamTK) of + tk_enum -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + tk_array -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + tk_struct -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + tk_union -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, &~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n") + end + end + end + end; +emit_encoding_stmt(G, N, _X, Fd, T, LName, _OutBuffer) when is_record(T, string) -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_encode_string(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); +emit_encoding_stmt(G, N, _X, Fd, T, LName, _OutBuffer) when is_record(T, wstring) -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_wstring(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"); +emit_encoding_stmt(G, N, _X, Fd, T, LName, _OutBuffer) -> + case normalize_type(T) of + {basic, Type} -> + emit_encoding_stmt_for_basic_type(G, N, T, Fd, Type, LName); + {void, _} -> + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"), + ok; + {sequence, _, _} -> + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"), + ok; + {_ArrayType, {array, _, _}} -> + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"), + ok; + {struct, _, _, _} -> %% Struct as a member in struct ! + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"), + ok; + _ -> + %%io:format("2 ------------> ~p~n", [T]), + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end. + +%%------------------------------------------------------------ +emit_encoding_stmt_for_basic_type(G, N, T, Fd, Type, LName) -> + {Cast, DecType} = + case Type of + ushort -> {"(unsigned long) ", "ulong"}; + ulong -> {"", "ulong"}; + ulonglong -> {"", "ulonglong"}; + short -> {"(long) ", "long"}; + long -> {"", "long"}; + longlong -> {"", "longlong"}; + float -> {"(double) ", "double"}; + double -> {"", "double"}; + boolean -> {"", "atom"}; + char -> {"", "char"}; + wchar -> {"", "wchar"}; + octet -> {"", "char"}; + any -> {"", "long"} % Fix for any + end, + case Type of + boolean -> + %% Note prefix: oe_ei + emit(Fd, " switch(~s) {\n",[LName]), + emit(Fd, " case 0 :\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, " + "\"false\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " case 1 :\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, " + "\"true\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " default :\n"), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n\n"); + _ -> + Fmt = + " if ((oe_error_code = oe_ei_encode_~s(oe_env, ~s~s)) < 0) {\n", + emit(Fd, Fmt, [DecType, Cast, LName]), + ?emit_c_enc_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n }\n") + end. + + +%%------------------------------------------------------------ +%% MALLOC SIZE (for Decode) +%%------------------------------------------------------------ + +emit_malloc_size_stmt(G, N, Fd, T, InBuffer, + Align, CalcType) when element(1, T) == scoped_id -> + case mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + emit(Fd, " oe_malloc_size += sizeof(erlang_pid);\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_pid(~s, " + "oe_size_count_index, NULL)) < 0) {\n", [InBuffer]), + ?emit_c_dec_rpt(Fd, " ", "erlang_pid", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + emit(Fd, " oe_malloc_size += sizeof(erlang_port);\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_port(~s, " + "oe_size_count_index, NULL)) < 0) {\n", [InBuffer]), + ?emit_c_dec_rpt(Fd, " ", "erlang_port", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_ref" -> + emit(Fd, " oe_malloc_size += sizeof(erlang_ref);\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_ref(~s, " + "oe_size_count_index, NULL)) < 0) {\n", [InBuffer]), + ?emit_c_dec_rpt(Fd, " ", "erlang_ref", []), + emit(Fd, " return oe_error_code;\n }\n"); + "ETERM*" -> + emit(Fd, " oe_malloc_size += sizeof(char*);\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_term(~s, " + "oe_size_count_index, NULL)) < 0) {\n", [InBuffer]), + ?emit_c_dec_rpt(Fd, " ", "ETERM*", []), + emit(Fd, " return oe_error_code;\n }\n"); + {enum, FSN} -> + emit_malloc_size_stmt(G, N, Fd, FSN, InBuffer, Align, CalcType); + FSN -> + %% io:format("emit_malloc_size_stmt: ~p ~p~n",[FSN, + %% CalcType]), + emit_malloc_size_stmt(G, N, Fd, FSN, InBuffer, Align, CalcType) + end; + +%% XXX T is a string +emit_malloc_size_stmt(G, N, Fd, T, InBuffer, + _Align, CalcType) when is_list(T) -> + %% Already a fullscoped name + Type = ictype:name2type(G,T), + case ictype:isBasicType(Type) of + true -> + emit_malloc_size_stmt_for_basic_type(G, N, T, Fd, Type, InBuffer); + false -> + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), T]), + ?emit_c_dec_rpt(Fd, " ", "~s", [T]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "&oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), T]), + ?emit_c_dec_rpt(Fd, " ", "~s", [T]), + emit(Fd, " return oe_error_code;\n }\n") + end + end; +emit_malloc_size_stmt(G, N, Fd, T, InBuffer, _Align, + CalcType) when is_record(T, string) -> + Tname = mk_variable_name(op_variable_count), + store_tmp_decl(" int ~s = 0;\n",[Tname]), + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ei_get_type(~s, " + "oe_size_count_index, &oe_type, &~s)) < 0) {\n", + [InBuffer, Tname]); + _ -> + emit(Fd, " int oe_type = 0;\n"), + emit(Fd, " int oe_temp = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ei_get_type(~s, " + "&oe_size_count_index, &oe_type, &oe_temp)) < 0) {\n", + [InBuffer]) + end, + ?emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + if + T#string.length == 0 -> + ok; + true -> + Length = ic_util:eval_c(G, N, T#string.length), + case CalcType of + generator -> + emit(Fd, " if (~s > ~s)\n",[Tname, Length]), + emit(Fd, " return -1;\n\n"); + _ -> + emit(Fd, " if (oe_temp > ~s)\n",[Length]), + emit(Fd, " return -1;\n\n") + end + end, + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ei_decode_string(~s, " + "oe_size_count_index, NULL)) < 0) {\n", [InBuffer]); + _ -> + emit(Fd, " if ((oe_error_code = ei_decode_string(~s, " + "&oe_size_count_index, NULL)) < 0) {\n", [InBuffer]) + end, + ?emit_c_dec_rpt(Fd, " ", "ei_decode_string", []), + emit(Fd, " return oe_error_code;\n }\n"), + case CalcType of + generator -> + emit(Fd, " oe_malloc_size = ~s;\n\n", + [ic_util:mk_align("oe_malloc_size + " ++ Tname ++"+1")]); + _ -> + emit(Fd, " oe_malloc_size = ~s;\n\n", + [ic_util:mk_align("oe_malloc_size + oe_temp+1")]) + end; +emit_malloc_size_stmt(G, N, Fd, T, InBuffer, _Align, + CalcType) when is_record(T, wstring) -> + Tname = mk_variable_name(op_variable_count), + store_tmp_decl(" int ~s = 0;\n",[Tname]), + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ei_get_type(~s, " + "oe_size_count_index, &oe_type, &~s)) < 0) {\n", + [InBuffer, Tname]); + _ -> + emit(Fd, " int oe_type = 0;\n"), + emit(Fd, " int oe_temp = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ei_get_type(~s, " + "&oe_size_count_index, &oe_type, &oe_temp)) < 0) {\n", + [InBuffer]) + end, + ?emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + if + T#wstring.length == 0 -> + ok; + true -> + Length = ic_util:eval_c(G, N, T#wstring.length), + case CalcType of + generator -> + emit(Fd, " if (~s > ~s)\n",[Tname, Length]), + emit(Fd, " return -1;\n\n"); + _ -> + emit(Fd, " if (oe_temp > ~s)\n",[Length]), + emit(Fd, " return -1;\n\n") + end + end, + case CalcType of + generator -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_decode_wstring(~s, " + "oe_size_count_index, NULL)) < 0) {\n", [InBuffer]); + _ -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_decode_wstring(~s, " + "&oe_size_count_index, NULL)) < 0) {\n", [InBuffer]) + end, + ?emit_c_dec_rpt(Fd, " ", "oe_ei_decode_wstring", []), + emit(Fd, " return oe_error_code;\n }\n"), + case CalcType of + generator -> + emit(Fd, " oe_malloc_size =\n ~s;\n\n", + [ic_util:mk_align("oe_malloc_size + ((" + ++ Tname + ++"+ 1) * __OE_WCHAR_SIZE_OF__)")]); + _ -> + emit(Fd, " oe_malloc_size =\n ~s;\n\n", + [ic_util:mk_align("oe_malloc_size + ((" + "oe_temp + 1) * __OE_WCHAR_SIZE_OF__)")]) + end; +emit_malloc_size_stmt(G, N, Fd, T, InBuffer, Align, CalcType) -> + case Align of + 0 -> + emit(Fd, " oe_malloc_size += sizeof(~s);\n\n", + [mk_c_type(G, N, T)]); + _ -> + ok + end, + case normalize_type(T) of + {basic, Type} -> + emit_malloc_size_stmt_for_basic_type(G, N, T, Fd, Type, InBuffer); + {void, _} -> + ok; + {sequence, _, _} -> + ok; + {_, {array, SId, _}} -> + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), + ic_forms:get_id2(SId)]), + ?emit_c_dec_rpt(Fd, " ", "array1", []), + emit(Fd, " return oe_error_code;\n\n"); + _ -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "&oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), + ic_forms:get_id2(SId)]), + ?emit_c_dec_rpt(Fd, " ", "array2", []), + emit(Fd, " return oe_error_code;\n\n") + end; + {union, UId, _, _, _} -> + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), + ic_forms:get_id2(UId)]), + ?emit_c_dec_rpt(Fd, " ", "union1", []), + emit(Fd, " return oe_error_code;\n\n"); + _ -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "&oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), + ic_forms:get_id2(UId)]), + ?emit_c_dec_rpt(Fd, " ", "union2", []), + emit(Fd, " return oe_error_code;\n\n") + end; + {struct, UId, _, _} -> %% Struct as a member in struct ! + case CalcType of + generator -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), + ic_forms:get_id2(UId)]), + ?emit_c_dec_rpt(Fd, " ", "struct1", []), + emit(Fd, " return oe_error_code;\n\n"); + _ -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "&oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ic_util:mk_oe_name(G, "sizecalc_"), + ic_forms:get_id2(UId)]), + ?emit_c_dec_rpt(Fd, " ", "struct2", []), + emit(Fd, " return oe_error_code;\n\n") + end; + {any, _} -> %% Fix for any type + emit(Fd, " if ((oe_error_code = ei_decode_long(~s, " + "oe_size_count_index, NULL)) < 0) {\n", + [InBuffer]), + ?emit_c_dec_rpt(Fd, " ", "any", []), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end. + +%%------------------------------------------------------------ + +emit_malloc_size_stmt_for_basic_type(G, N, T, Fd, Type, InBuffer) -> + {Pre, DecType} = + case Type of + ushort -> {"", "ulong"}; + ulong -> {"", "ulong"}; + ulonglong -> {"oe_", "ulonglong"}; + short -> {"", "long"}; + long -> {"", "long"}; + longlong -> {"oe_", "longlong"}; + float -> {"", "double"}; + double -> {"", "double"}; + boolean -> {"", "atom"}; + char -> {"", "char"}; + wchar -> {"oe_", "wchar"}; + octet -> {"", "char"}; + any -> {"", "long"} + end, + Fmt = + " if ((oe_error_code = ~sei_decode_~s(~s, oe_size_count_index, " + "NULL)) < 0) {\n", + emit(Fd, Fmt, [Pre, DecType, InBuffer]), + ?emit_c_dec_rpt(Fd, " ", "~s", [DecType]), + emit(Fd, " return oe_error_code;\n }\n"). + +%%------------------------------------------------------------ +%% DECODING +%%------------------------------------------------------------ + +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, Align, + NextPos, DecType) -> + emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, Align, + NextPos, DecType, []). + +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, Align, NextPos, + DecType, AllocedPars) when element(1, T) == scoped_id -> + Fmt = + " if ((oe_error_code = ei_decode_~s(~s, &oe_env->_iin, ~s~s)) < 0)" + " {\n", + Emit = fun(Type) -> + emit(Fd, Fmt, [Type, InBuffer, IndOp, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n") + end, + case mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + Emit("pid"); + "erlang_port" -> + Emit("port"); + "erlang_ref" -> + Emit("ref"); + "ETERM*" -> + Emit("term"); + {enum, FSN} -> + emit_decoding_stmt(G, N, Fd, FSN, LName, IndOp, InBuffer, + Align, NextPos, DecType, AllocedPars); + FSN -> + emit_decoding_stmt(G, N, Fd, FSN, LName, IndOp, InBuffer, + Align, NextPos, DecType, AllocedPars) + end; + +%% XXX T is a string +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, NextPos, + DecType, AllocedPars) when is_list(T) -> + %% Already a fullscoped name + Type = ictype:name2type(G,T), + case ictype:isBasicType(Type) of + true -> + emit_decoding_stmt_for_basic_type(G, N, T, Fd, Type, InBuffer, IndOp, + LName, AllocedPars); + false -> + case DecType of + generator -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, oe_first, " + "~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"); + caller -> %% No malloc used, define oe_first + emit(Fd, " {\n"), + emit(Fd, " void *oe_first = NULL;\n"), + emit(Fd, " int oe_outindex = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_first, ~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n"); + caller_dyn -> %% Malloc used + emit(Fd, " {\n"), + emit(Fd, " int oe_outindex = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_first, ~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n"); + array_dyn -> %% Malloc used + emit(Fd, " {\n"), + emit(Fd, " int oe_outindex = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_first, ~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n"); + array_fix_ret -> + emit(Fd, " {\n"), + emit(Fd, " int oe_outindex = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_first, ~s,*~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n"); + array_fix_out -> %% No malloc used, define oe_first + emit(Fd, " {\n"), + emit(Fd, " void *oe_first = NULL;\n"), + emit(Fd, " int oe_outindex = 0;\n\n"), + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_first, ~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n") + end + end; +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, _NextPos, + DecType, AllocedPars) when is_record(T, string) -> + case DecType of + caller_dyn -> + emit(Fd, " if ((oe_error_code = ei_decode_string(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"); + _ -> + emit(Fd, " ~s~s = oe_first + *oe_outindex;\n\n", + [IndOp, LName]), + emit(Fd, " {\n"), + emit(Fd, " int oe_type=0;\n"), + emit(Fd, " int oe_string_ctr=0;\n\n"), + + emit(Fd, " (int) ei_get_type(~s, " + "&oe_env->_iin, &oe_type, &oe_string_ctr);\n\n", + [InBuffer]), + + emit(Fd, " if ((oe_error_code = ei_decode_string(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " *oe_outindex = ~s;\n", + [ic_util:mk_align("*oe_outindex+oe_string_ctr+1")]), + emit(Fd, " }\n\n") + end; +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, _NextPos, + DecType, AllocedPars) when is_record(T, wstring) -> + case DecType of + caller_dyn -> + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_decode_wstring(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }/* --- */\n"); % XXX + _ -> + emit(Fd, " ~s~s = oe_first + *oe_outindex;\n\n", + [IndOp, LName]), + + emit(Fd, " {\n"), + emit(Fd, " int oe_type=0;\n"), + emit(Fd, " int oe_string_ctr=0;\n\n"), + emit(Fd, " (int) ei_get_type(~s, " + "&oe_env->_iin, &oe_type, &oe_string_ctr);\n\n", + [InBuffer]), + %% Note prefix: oe_ei + emit(Fd, " if ((oe_error_code = oe_ei_decode_wstring(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " *oe_outindex = ~s;\n", + [ic_util:mk_align("*oe_outindex+oe_string_ctr+1")]), + emit(Fd, " }\n") + end; +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, NextPos, + _DecType, AllocedPars) -> + case normalize_type(T) of + {basic, Type} -> + emit_decoding_stmt_for_basic_type(G, N, T, Fd, Type, InBuffer, IndOp, + LName, AllocedPars); + {void, _} -> + emit(Fd, " if ((oe_error_code = ei_decode_atom(~s, " + "&oe_env->_iin, NULL)) < 0) {\n", + [InBuffer]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"); + {sequence, _, _} -> + ok; + {_, {array, SId, Dims}} -> + AName = ic_forms:get_id2({array, SId, Dims}), + Ptr = "oe_out->"++AName, + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, " + "oe_first, ~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + ic_forms:get_id2(SId), + NextPos, Ptr]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"); + {struct, _, _, _} -> %% Struct as a member in struct ! + ok; + _ -> + %%io:format("3 ------------> ~p~n", [T]), + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end. + +%% XXX DecType used in two senses in this file. +emit_decoding_stmt_for_basic_type(G, N, T, Fd, Type, InBuffer, IndOp, + LName, AllocedPars) -> + Fmt = + " if ((oe_error_code = ~sei_decode_~s(~s, &oe_env->_iin, " + "~s~s)) < 0) {\n", + Ret = + " return oe_error_code;\n" + "}\n", + + {Pre, DecType} = + case Type of + ushort -> {"", "ulong"}; + ulong -> {"", "ulong"}; + ulonglong -> {"oe_", "ulonglong"}; + short -> {"", "long"}; + long -> {"", "long"}; + longlong -> {"oe_", "longlong"}; + float -> {"", "double"}; + double -> {"", "double"}; + boolean -> {"", "atom"}; + char -> {"", "char"}; + wchar -> {"oe_", "wchar"}; + octet -> {"", "char"}; + any -> {"", "long"} + end, + case Type of + ushort -> + emit(Fd, " {\n"), + emit(Fd, " unsigned long oe_ulong;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_ulong(~s, " + "&oe_env->_iin, &oe_ulong)) < 0) {\n", + [InBuffer]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, "}\n"), + emit(Fd, " *(~s) = (unsigned short) oe_ulong;\n\n", + [LName]), + emit(Fd, " if (*(~s) != oe_ulong){\n", + [LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n\n"); + short -> + emit(Fd, " {\n"), + emit(Fd, " long oe_long;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_long(~s, " + "&oe_env->_iin, &oe_long)) < 0){\n", + [InBuffer]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n\n"), + emit(Fd, "}\n"), + emit(Fd, " *(~s) = (short) oe_long;\n\n",[LName]), + emit(Fd, " if (*(~s) != oe_long){\n", [LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n"); + float -> + emit(Fd, " {\n"), + emit(Fd, " double oe_double;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_double(~s, " + "&oe_env->_iin, &oe_double)) < 0){\n", + [InBuffer]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n\n"), + emit(Fd, "}\n"), + emit(Fd, " *(~s) = (float) oe_double;\n",[LName]), + emit(Fd, " }\n"); + boolean -> + emit(Fd, " {\n"), + emit(Fd, " char oe_bool[25];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(~s, " + "&oe_env->_iin, oe_bool)) < 0){\n",[InBuffer]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, "}\n"), + emit(Fd, " if (strcmp(oe_bool, \"false\") == 0) {\n"), + emit(Fd, " *(~s) = 0;\n",[LName]), + emit(Fd, " }\n"), + emit(Fd, " else if (strcmp(oe_bool, \"true\") == 0)" + " {\n"), + emit(Fd, " *(~s) = 1;\n",[LName]), + emit(Fd, " }\n"), + emit(Fd, " else {\n"), + emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n"); + _ -> + emit(Fd, Fmt, [Pre, DecType, InBuffer, IndOp, LName]), + ?emit_c_dec_rpt(Fd, " ", "~s", [LName]), + emit_dealloc_stmts(Fd, " ", AllocedPars), + emit(Fd, Ret) + end. + +%%------------------------------------------------------------ +%% +%%------------------------------------------------------------ +emit_dealloc_stmts(Fd, Prefix, AllocedPars) -> + Fmt = Prefix ++ "CORBA_free(~s);\n", + lists:foreach( + fun(Par) -> emit(Fd, Fmt, [Par]) end, + AllocedPars). + + +%%------------------------------------------------------------ +%% +%%------------------------------------------------------------ + +mk_variable_name(Var) -> + Nr = get(Var), + put(Var, Nr + 1), + "oe_tmp" ++ integer_to_list(Nr). + +%% IDL to C type conversion +%%------------------------------------------------------------ +mk_c_type(G, N, S) -> + mk_c_type(G, N, S, evaluate). + +mk_c_type(G, N, S, evaluate) when element(1, S) == scoped_id -> + {FullScopedName, _T, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + BT = ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)), + case BT of + "erlang_binary" -> + "erlang_binary"; + "erlang_pid" -> + "erlang_pid"; + "erlang_port" -> + "erlang_port"; + "erlang_ref" -> + "erlang_ref"; + "erlang_term" -> + "ETERM*"; + {enum, Type} -> + mk_c_type(G, N, Type, evaluate); + Type -> + mk_c_type(G, N, Type, evaluate) + end; + +mk_c_type(G, N, S, evaluate_not) when element(1, S) == scoped_id -> + {FullScopedName, _T, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + BT = ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)), + case BT of + "erlang_binary" -> + "erlang_binary"; + "erlang_pid" -> + "erlang_pid"; + "erlang_port" -> + "erlang_port"; + "erlang_ref" -> + "erlang_ref"; + "erlang_term" -> + "ETERM*"; + Type -> + Type + end; +mk_c_type(_G, _N, S, _) when is_list(S) -> + S; +mk_c_type(_G, _N, S, _) when is_record(S, string) -> + "CORBA_char *"; +mk_c_type(_G, _N, S, _) when is_record(S, wstring) -> + "CORBA_wchar *"; +mk_c_type(_G, _N, {boolean, _}, _) -> + "CORBA_boolean"; +mk_c_type(_G, _N, {octet, _}, _) -> + "CORBA_octet"; +mk_c_type(_G, _N, {void, _}, _) -> + "void"; +mk_c_type(_G, _N, {unsigned, U}, _) -> + case U of + {short,_} -> + "CORBA_unsigned_short"; + {long,_} -> + "CORBA_unsigned_long"; + {'long long',_} -> + "CORBA_unsigned_long_long" + end; + +mk_c_type(_G, _N, {'long long', _}, _) -> + "CORBA_long_long"; + +mk_c_type(_G, _N, S, _) when is_record(S, union)-> + ic_forms:get_id2(S); + +mk_c_type(_G, N, S, _) when is_record(S, struct) -> %% Locally defined member + Fullname = [ic_forms:get_id2(S) | N], + ic_util:to_undersc(Fullname); + +mk_c_type(_G, _N, {'any', _}, _) -> %% Fix for any type + "CORBA_long"; + +mk_c_type(_G, _N, {T, _}, _) -> + "CORBA_" ++ atom_to_list(T). + +%%------------------------------------------------------------------- +%% IDL to C type conversion used by the emit_c_*_rpt macros. +%%------------------------------------------------------------------- +mk_c_type2(G, N, S) when element(1, S) == scoped_id -> + {FullScopedName, _T, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + BT = ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)), + case BT of + "erlang_binary" -> + "erlang_binary"; + "erlang_pid" -> + "erlang_pid"; + "erlang_port" -> + "erlang_port"; + "erlang_ref" -> + "erlang_ref"; + "erlang_term" -> + "ETERM*"; + {enum, Type} -> + mk_c_type2(G, N, Type); + Type -> + mk_c_type2(G, N, Type) + end; + +mk_c_type2(_G, _N, S) when is_list(S) -> + S; +mk_c_type2(_G, _N, S) when is_record(S, string) -> + "CORBA_char *"; +mk_c_type2(_G, _N, S) when is_record(S, wstring) -> + "CORBA_wchar *"; +mk_c_type2(_G, _N, {boolean, _}) -> + "CORBA_boolean"; +mk_c_type2(_G, _N, {octet, _}) -> + "CORBA_octet"; +mk_c_type2(_G, _N, {void, _}) -> + "void"; +mk_c_type2(_G, _N, {unsigned, U}) -> + case U of + {short,_} -> + "CORBA_unsigned_short"; + {long,_} -> + "CORBA_unsigned_long"; + {'long long',_} -> + "CORBA_unsigned_long_long" + end; + +mk_c_type2(_G, _N, {'long long', _}) -> + "CORBA_long_long"; + +mk_c_type2(_G, _N, S) when is_record(S, union)-> + ic_forms:get_id2(S); + +mk_c_type2(_G, N, S) when is_record(S, struct) -> + Fullname = [ic_forms:get_id2(S) | N], + ic_util:to_undersc(Fullname); + +mk_c_type2(_G, _N, S) when is_record(S, sequence) -> + mk_c_type2(_G, _N, S#sequence.type); + +mk_c_type2(_G, _N, {'any', _}) -> %% Fix for any type + "CORBA_long"; + +mk_c_type2(_G, _N, {T, _}) -> + "CORBA_" ++ atom_to_list(T). + +%%----- + +is_variable_size_rec(Es) -> + lists:any( + fun({_N, T}) -> is_variable_size(T); + ({_, _N, T}) -> is_variable_size(T) + end, Es). + +is_variable_size({'tk_struct', _IFRId, "port", _ElementList}) -> + false; +is_variable_size({'tk_struct', _IFRId, "pid", _ElementList}) -> + false; +is_variable_size({'tk_struct', _IFRId, "ref", _ElementList}) -> + false; +is_variable_size({'tk_struct', _IFRId, "term", _ElementList}) -> + false; +is_variable_size({'tk_struct', _IFRId, _Name, ElementList}) -> + is_variable_size_rec(ElementList); +is_variable_size({'tk_array', ElemTC, _Length}) -> + is_variable_size(ElemTC); +is_variable_size({'tk_string', _}) -> + true; +is_variable_size({'tk_wstring', _}) -> + true; +is_variable_size({'tk_sequence', _ElemTC, _MaxLsextractength}) -> + true; +is_variable_size({'tk_union', _IFRId, _Name, _, _, ElementList}) -> + is_variable_size_rec(ElementList); +is_variable_size(_Other) -> + false. + + +is_variable_size(_G, _N, T) when is_record(T, string) -> + true; +is_variable_size(_G, _N, T) when is_record(T, wstring) -> + true; +is_variable_size(_G, _N, T) when is_record(T, sequence) -> + true; +is_variable_size(G, N, T) when is_record(T, union) -> + %%io:format("~n~p = ~p~n",[ic_forms:get_id2(T),ictype:fetchTk(G, N, T)]), + is_variable_size(ictype:fetchTk(G, N, T)); +is_variable_size(G, N, T) when is_record(T, struct) -> + is_variable_size(ictype:fetchTk(G, N, T)); +is_variable_size(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, TK, _} -> + is_variable_size(TK); + _ -> + ic_error:fatal_error(G, {name_not_found, T}) + end; +is_variable_size(_G, _N, _Other) -> + false. + +%% mk_dim produces +mk_dim([Arg | Args]) -> + "[" ++ Arg ++ "]" ++ mk_dim(Args); +mk_dim([]) -> []. + +mk_slice_dim(Args) -> + mk_dim(tl(Args)). + + +emit_tmp_variables(Fd) -> + DeclList = get(tmp_declarations), + emit_tmp_variables(Fd, DeclList), + ok. + +emit_tmp_variables(Fd, [Decl |Rest]) -> + emit_tmp_variables(Fd, Rest), + emit(Fd, "~s", [Decl]); +emit_tmp_variables(_Fd, []) -> + ok. + +store_tmp_decl(Format, Args) -> + Decl = io_lib:format(Format, Args), + DeclList = get(tmp_declarations), + put(tmp_declarations, [Decl |DeclList]). + +%%------------------------------------------------------------ +%% +%% Parser utilities +%% +%% Called from the yecc parser. Expands the identifier list of an +%% attribute so that the attribute generator never has to handle +%% lists. +%% +%%------------------------------------------------------------ + +extract_info(_G, N, X) when is_record(X, op) -> + Name = ic_util:to_undersc([ic_forms:get_id2(X) | N]), + Args = X#op.params, + ArgNames = mk_c_vars(Args), + TypeList = {ic_forms:get_type(X), + lists:map(fun(Y) -> ic_forms:get_type(Y) end, Args), + [] + }, + {Name, ArgNames, TypeList}; +extract_info(_G, N, X) -> + Name = ic_util:to_undersc([ic_forms:get_id2(X) | N]), + {Name, [], []}. + + + +%% Usefull functions +get_param_tk(Name, Op) -> + case get_param(Name, Op) of + error -> + error; + Param -> + ic_forms:get_tk(Param) + end. + +get_param(Name, Op) when is_record(Op, op) -> + get_param_loop(Name, Op#op.params); +get_param(_Name, _Op) -> + error. + +get_param_loop(Name,[Param|Params]) -> + case ic_forms:get_id2(Param) of + Name -> + Param; + _ -> + get_param_loop(Name,Params) + end; +get_param_loop(_Name, []) -> + error. + + +%% Input is a list of parameters (in parse form) and output is a list +%% of parameter attribute and variable names. +mk_c_vars(Params) -> + lists:map(fun(P) -> {A, _} = P#param.inout, + {A, ic_forms:get_id(P#param.id)} + end, + Params). + +normalize_type({unsigned, {short, _}}) -> {basic, ushort}; +normalize_type({unsigned, {long, _}}) -> {basic, ulong}; +normalize_type({unsigned, {'long long', _}}) -> {basic, ulonglong}; +normalize_type({short,_}) -> {basic, short}; +normalize_type({long, _}) -> {basic, long}; +normalize_type({'long long', _}) -> {basic, longlong}; +normalize_type({float,_}) -> {basic, float}; +normalize_type({double, _}) -> {basic, double}; +normalize_type({boolean, _}) -> {basic, boolean}; +normalize_type({char, _}) -> {basic, char}; +normalize_type({wchar, _}) -> {basic, wchar}; +normalize_type({octet, _}) -> {basic, octet}; +normalize_type({any, _}) -> {basic, any}; +normalize_type(tk_ushort) -> {basic, ushort}; +normalize_type(tk_ulong) -> {basic, ulong}; +normalize_type(tk_ulonglong) -> {basic, ulonglong}; +normalize_type(tk_short) -> {basic, short}; +normalize_type(tk_long) -> {basic, long}; +normalize_type(tk_longlong) -> {basic, longlong}; +normalize_type(tk_float) -> {basic, float}; +normalize_type(tk_double) -> {basic, double}; +normalize_type(tk_boolean) -> {basic, boolean}; +normalize_type(tk_char) -> {basic, char}; +normalize_type(tk_wchar) -> {basic, wchar}; +normalize_type(tk_octet) -> {basic, octet}; +normalize_type(tk_any) -> {basic, any}; +normalize_type(ushort) -> {basic, ushort}; +normalize_type(ulong) -> {basic, ulong}; +normalize_type(ulonglong) -> {basic, ulonglong}; +normalize_type(short) -> {basic, short}; +normalize_type(long) -> {basic, long}; +normalize_type(longlong) -> {basic, longlong}; +normalize_type(float) -> {basic, float}; +normalize_type(double) -> {basic, double}; +normalize_type(boolean) -> {basic, boolean}; +normalize_type(char) -> {basic, char}; +normalize_type(wchar) -> {basic, wchar}; +normalize_type(octet) -> {basic, octet}; +normalize_type(any) -> {basic, any}; +normalize_type(Type) -> Type. + diff --git a/lib/ic/src/ic_cclient.erl b/lib/ic/src/ic_cclient.erl new file mode 100644 index 0000000000..ebe7e0c207 --- /dev/null +++ b/lib/ic/src/ic_cclient.erl @@ -0,0 +1,1209 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights 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. diff --git a/lib/ic/src/ic_code.erl b/lib/ic/src/ic_code.erl new file mode 100644 index 0000000000..6802b9ca65 --- /dev/null +++ b/lib/ic/src/ic_code.erl @@ -0,0 +1,584 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_code). + + +-include_lib("ic/src/ic.hrl"). +-include_lib("ic/src/icforms.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([get_basetype/2, insert_typedef/3, codeDirective/2]). +-export([gen_includes/3, gen_includes/4, mk_list/1]). + +-export([type_expand_op/4, type_expand_handle_op/4]). +-export([ type_expand_op_exec/4, type_expand_all/6, type_expand/7]). + +-export([type_expand_null/3, type_expand_void/3, type_expand_float/3, type_expand_double/3]). +-export([type_expand_short/3, type_expand_ushort/3, type_expand_long/3, type_expand_ulong/3]). +-export([type_expand_longlong/3, type_expand_ulonglong/3]). +-export([type_expand_char/3, type_expand_wchar/3, type_expand_boolean/3]). +-export([type_expand_octet/3, type_expand_any/3, type_expand_wstring/3]). +-export([type_expand_object/3, type_expand_string/3, type_expand_struct/7, type_expand_union/7]). +-export([type_expand_enum/4, type_expand_sequence/7, type_expand_array/7, type_expand_error/3]). + +-export([type_expand_struct_rule/3, type_expand_union_rule/2, type_expand_enum_rule/4]). +-export([type_expand_enum_elements/3, type_expand_longdouble/3, type_expand_typecode/3]). +-export([type_expand_principal/3, type_expand_exception/7]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%------------------------------------------------------------------------------------- +%% +%% Trackrecording of generated sequence type structs, thist is just used for C today. +%% +%%------------------------------------------------------------------------------------- + +get_basetype(G, MyId) -> + case ?lookup(ic_genobj:typedeftab(G), MyId) of + [] -> + MyId; + X -> + get_basetype(G, X) + end. + +insert_typedef(_G, "erlang_term", _) -> + ok; +insert_typedef(G, MyId, DefinedAsId) -> + ?insert(ic_genobj:typedeftab(G), MyId, DefinedAsId). + +codeDirective(G,X) -> + case produceCode(X) of + true -> + case ic_options:get_opt(G, be) of + c_genserv -> + c; + c_client -> + c; + c_server -> + c_server; + _ -> + erlang + end; + false -> + case ic_options:get_opt(G, be) of + c_genserv -> + c_no_stub; + c_client -> + c_no_stub; + c_server -> + c_server_no_stub; + _ -> + erlang_no_stub + end + end. + +%% Checks if X should produce code +produceCode(X) when is_record(X, module) -> + case ic_forms:get_body(X) of + [] -> + true; + List -> + produceModuleCode(List) + end; +produceCode(_X) -> + false. + +produceModuleCode([]) -> + false; +produceModuleCode([X|_Xs]) when is_record(X, const) -> + true; +produceModuleCode([_X|Xs]) -> + produceModuleCode(Xs). + +%% Includes needed c file headers for included idl files +gen_includes(Fd,G,Type) -> + case Type of + c_client -> + IncludeList = + ic_pragma:get_included_c_headers(G), + gen_includes_loop(Fd,IncludeList,Type); + c_server -> + IncludeList = + ic_pragma:get_included_c_headers(G), + gen_includes_loop(Fd,IncludeList,Type); + _ -> + ok + end, + ic_codegen:nl(Fd), + ic_codegen:emit(Fd, "#ifdef __cplusplus\n"), + ic_codegen:emit(Fd, "extern \"C\" {\n"), + ic_codegen:emit(Fd, "#endif\n\n"). + + +%% Includes needed c file headers for local interfaces +gen_includes(Fd,G,X,Type) -> + case Type of + c_client -> + IncludeList = + ic_pragma:get_local_c_headers(G,X), + gen_includes_loop(Fd,IncludeList,Type); + c_server -> + IncludeList = + ic_pragma:get_local_c_headers(G,X), + gen_includes_loop(Fd,IncludeList,Type); + _ -> + ok + end, + ic_codegen:nl(Fd), + ic_codegen:emit(Fd, "#ifdef __cplusplus\n"), + ic_codegen:emit(Fd, "extern \"C\" {\n"), + ic_codegen:emit(Fd, "#endif\n\n"). + + +gen_includes_loop(_,[],_) -> + ok; +gen_includes_loop(Fd,[I|Is],Type) -> + L = string:tokens(I,"/"), + File = lists:last(L), + case File of + "erlang" -> % Erlang is NOT generated that way ! + gen_includes_loop(Fd,Is,Type); + "oe_erlang" -> % Erlang is NOT generated that way ! + gen_includes_loop(Fd,Is,Type); + _ -> + case Type of + c_client -> + ic_codegen:emit(Fd, "#include \"~s.h\"\n", [File]); + c_server -> + ic_codegen:emit(Fd, "#include \"~s__s.h\"\n", [File]) + end, + gen_includes_loop(Fd,Is,Type) + end. + + + + +%% +%% Used in NOC only +%% + + +%% +%% Type expand on function head comments +%% +type_expand_op(G,N,X,Fd) -> + case catch type_expand_op_exec(G,N,X,Fd) of + {'EXIT',_Reason} -> + ic_codegen:nl(Fd), + ic_codegen:emit(Fd,"%% Error under type expansion, does not affect generated code.~n",[]), + ic_codegen:emit(Fd,"%%------------------------------------------------------------~n",[]); + _ -> + ic_codegen:emit(Fd,"%%------------------------------------------------------------~n",[]) + end. + + +type_expand_op_exec(G,N,X,Fd) -> + InArgs = ic:filter_params([in,inout], X#op.params), + OutArgs = ic:filter_params([out,inout], X#op.params), + ParamNr = length(InArgs)+1, + Tabs = "", + + ic_codegen:nl(Fd), + ic_codegen:emit(Fd,"%%------------------------------------------------------------~n",[]), + + case ic_forms:is_oneway(X) of + false -> + ic_codegen:emit(Fd,"%% Operation: ~s/~p~n",[ic_forms:get_id2(X),ParamNr]); + true -> + ic_codegen:emit(Fd,"%% Operation: ~s/~p (oneway)~n",[ic_forms:get_id2(X),ParamNr]) + end, + + if X#op.raises == [] -> []; + true -> + ic_codegen:emit(Fd,"%%~n",[]), + RaisesList=["%% Raises: " ++ + mk_list(lists:map(fun(E) -> ic_util:to_colon(E) end, + X#op.raises))], + ic_codegen:emit(Fd,RaisesList,[]), + ic_codegen:nl(Fd) + end, + + %% Print argument names + ic_codegen:emit(Fd,"%%\n",[]), + InArgNames = ["OE_Ref"]++[ic_util:mk_var(ic_forms:get_id(InArg#param.id)) || InArg <- InArgs ], + OutArgNames = ["Ret"]++[ic_util:mk_var(ic_forms:get_id(OutArg#param.id)) || OutArg <- OutArgs ], + case length(InArgNames) > 1 of + true -> + ic_codegen:emit(Fd,"%% Input value(s) : ~s~n",[mk_list(InArgNames)]); + false -> + ic_codegen:emit(Fd,"%% Input value : ~s~n",[mk_list(InArgNames)]) + end, + case length(OutArgNames) > 1 of + true -> + ic_codegen:emit(Fd,"%% Return value(s) : ~s~n",[mk_list(OutArgNames)]); + false -> + ic_codegen:emit(Fd,"%% Return value : ~s~n",[mk_list(OutArgNames)]) + end, + ic_codegen:emit(Fd,"%%\n",[]), + + InArgsTypeList = + [{ic_util:mk_var(ic_forms:get_id(InArg#param.id)),ic_forms:get_tk(InArg)} || InArg <- InArgs ], + case InArgsTypeList of + [] -> %% no input parameters + ok; + _ -> + ic_codegen:emit(Fd,"%% --input-params-~n",[]), + type_expand_all(G,N,X,Fd,Tabs,InArgsTypeList) + end, + + ReturnTypeList =[{"Ret",X#op.tk}], + ic_codegen:emit(Fd,"%% --return-value-~n",[]), + type_expand_all(G,N,X,Fd,Tabs,ReturnTypeList), + + OutArgsTypeList = + [{ic_util:mk_var(ic_forms:get_id(OutArg#param.id)),ic_forms:get_tk(OutArg)} || OutArg <- OutArgs ], + case OutArgsTypeList of + [] -> %% no input parameters + ok; + _ -> + ic_codegen:emit(Fd,"%% -output-values-~n",[]), + type_expand_all(G,N,X,Fd,Tabs,OutArgsTypeList) + end. + + + + +type_expand_handle_op(G,N,X,Fd) -> + case catch type_expand_handle_op_exec(G,N,X,Fd) of + {'EXIT',_Reason} -> + ic_codegen:nl(Fd), + ic_codegen:emit(Fd,"%% Error under type expansion, does not affect generated code.~n",[]), + ic_codegen:emit(Fd,"%%------------------------------------------------------------~n",[]); + _ -> + ic_codegen:emit(Fd,"%%------------------------------------------------------------~n",[]) + end. + + +type_expand_handle_op_exec(_G,_N,X,Fd) -> + InArgs = ic:filter_params([in,inout], X#op.params), + ParamNr = length(InArgs)+1, + + ic_codegen:nl(Fd), + ic_codegen:emit(Fd,"%%------------------------------------------------------------~n",[]), + + case ic_forms:is_oneway(X) of + false -> + ic_codegen:emit(Fd,"%% Handle operation: handle_call/3~n",[]); + true -> + ic_codegen:emit(Fd,"%% Handle operation: handle_cast/3~n",[]) + end, + ic_codegen:emit(Fd,"%%~n",[]), + ic_codegen:emit(Fd,"%% Used for operation ~s/~p implementation~n",[ic_forms:get_id2(X),ParamNr]). + + + +type_expand_all(_G,_N,_X,_Fd,_Tabs,[]) -> + ok; +type_expand_all(G,N,X,Fd,Tabs,[{ArgName,Type}|Rest]) -> + type_expand(G,N,X,Fd,Tabs,ArgName,Type), + type_expand_all(G,N,X,Fd,Tabs,Rest); +type_expand_all(G,N,X,Fd,Tabs,[{default,_ArgName,Type}|Rest]) -> + type_expand(G,N,X,Fd,Tabs,"Def",Type), + type_expand_all(G,N,X,Fd,Tabs,Rest); +type_expand_all(G,N,X,Fd,Tabs,[{LabelNr,_ArgName,Type}|Rest]) when is_integer(LabelNr) -> + type_expand(G,N,X,Fd,Tabs,"V" ++ integer_to_list(LabelNr),Type), + type_expand_all(G,N,X,Fd,Tabs,Rest); +type_expand_all(G,N,X,Fd,Tabs,[{Label,_ArgName,Type}|Rest]) -> + type_expand(G,N,X,Fd,Tabs,Label,Type), + type_expand_all(G,N,X,Fd,Tabs,Rest). + + + +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_null) -> + type_expand_null(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_void) -> + type_expand_void(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_float) -> + type_expand_float(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_double) -> + type_expand_double(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_longdouble) -> + type_expand_longdouble(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_short) -> + type_expand_short(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_ushort) -> + type_expand_ushort(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_long) -> + type_expand_long(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_longlong) -> + type_expand_longlong(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_ulong) -> + type_expand_ulong(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_ulonglong) -> + type_expand_ulonglong(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_char) -> + type_expand_char(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_wchar) -> + type_expand_wchar(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_boolean) -> + type_expand_boolean(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_octet) -> + type_expand_octet(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_any) -> + type_expand_any(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_TypeCode) -> + type_expand_typecode(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,tk_Principal) -> + type_expand_principal(Fd,Tabs,Name); +type_expand(G, N, X,Fd,Tabs,Name, {tk_except, Id, ExcName, ElementList}) -> + type_expand_exception(G, N, X, Fd,Tabs,Name, + {tk_except, Id, ExcName, ElementList}); +type_expand(_G,_N,_X,Fd,Tabs,Name,{tk_fixed, _Digits, _Scale}) -> + type_expand_fixed(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,{tk_objref, _IFRId, _ObjTabs, _ObjName}) -> + type_expand_object(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,{tk_objref, _IFRId, _ObjName}) -> + type_expand_object(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,{tk_string, _Length}) -> + type_expand_string(Fd,Tabs,Name); +type_expand(_G,_N,_X,Fd,Tabs,Name,{tk_wstring, _Length}) -> + type_expand_wstring(Fd,Tabs,Name); +type_expand(G,N,X,Fd,Tabs,Name,{tk_union, IFRId, UnionName, DTC, DNr, LblList}) -> + type_expand_union(G,N,X,Fd,Tabs,Name,{tk_union, IFRId, UnionName, DTC, DNr, LblList}); +type_expand(_G,_N,_X,Fd,Tabs,Name,{tk_enum, IFRId, EnumName, ElemNameList}) -> + type_expand_enum(Fd,Tabs,Name,{tk_enum, IFRId, EnumName, ElemNameList}); +type_expand(G,N,X,Fd,Tabs,Name,{tk_sequence, ElemTC, Length}) -> + type_expand_sequence(G,N,X,Fd,Tabs,Name,{tk_sequence, ElemTC, Length}); +type_expand(G,N,X,Fd,Tabs,Name,{tk_array, ElemTC, Length}) -> + type_expand_array(G,N,X,Fd,Tabs,Name,{tk_array, ElemTC, Length}); +type_expand(G,N,X,Fd,Tabs,Name,{tk_struct, IFRId, StructName, TcList}) -> + type_expand_struct(G,N,X,Fd,Tabs,Name,{tk_struct, IFRId, StructName, TcList}); +type_expand(_G,_N,_X,Fd,Tabs,Name,_) -> + type_expand_error(Fd,Tabs,Name). + + +%% Basic OMG IDL types + +type_expand_null(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = null()~n",[Tabs,Name]). + +type_expand_void(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = void()~n",[Tabs,Name]). + +type_expand_float(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = float()~n",[Tabs,Name]). + +type_expand_double(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = double()~n",[Tabs,Name]). + +type_expand_longdouble(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = long_double()~n",[Tabs,Name]). + +type_expand_short(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = short()~n",[Tabs,Name]). + +type_expand_ushort(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = unsigned_Short()~n",[Tabs,Name]). + +type_expand_long(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = long()~n",[Tabs,Name]). + +type_expand_longlong(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = long_Long()~n",[Tabs,Name]). + +type_expand_ulong(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = unsigned_Long()~n",[Tabs,Name]). + +type_expand_ulonglong(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = unsigned_Long_Long()~n",[Tabs,Name]). + +type_expand_char(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = char()~n",[Tabs,Name]). + +type_expand_wchar(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = wchar()~n",[Tabs,Name]). + +type_expand_boolean(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = boolean()~n",[Tabs,Name]). + +type_expand_octet(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = octet()~n",[Tabs,Name]). + +type_expand_any(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = any()~n",[Tabs,Name]). + +type_expand_typecode(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = TypeCode()~n",[Tabs,Name]). + +type_expand_principal(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = principal()~n",[Tabs,Name]). + + +type_expand_fixed(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = fixed()~n",[Tabs,Name]). + +type_expand_object(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = Object_Ref()~n",[Tabs,Name]). + + +%% Constructed OMG IDL types + +type_expand_string(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = String()~n",[Tabs,Name]). + +type_expand_wstring(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = WString()~n",[Tabs,Name]). + +type_expand_exception(G, N, X, Fd, Tabs, Name, {tk_except, Id, ExcName, ElementList}) -> + ScopedStructName = getScopedName(G, N, ExcName, Id), + ic_codegen:emit(Fd,"%%~s ~s = ",[Tabs, Name]), + type_expand_exception_rule(Fd, ScopedStructName, ElementList), + type_expand_all(G, N, X, Fd, Tabs, ElementList). + +type_expand_struct(G,N,X,Fd,Tabs,Name,{tk_struct, IFRId, StructName, TcList}) -> + ScopedStructName = getScopedName(G,N,StructName,IFRId), + ic_codegen:emit(Fd,"%%~s ~s = ",[Tabs,Name]), + type_expand_struct_rule(Fd,ScopedStructName,TcList), + type_expand_all(G,N,X,Fd,Tabs,TcList). + +type_expand_union(G,N,X,Fd,Tabs,Name,{tk_union, IFRId, UnionName, DTC, _DNr, LblList}) -> + ScopedUnionName = getScopedName(G,N,UnionName,IFRId), + ic_codegen:emit(Fd,"%%~s ~s = #'~s'{label, value}\n",[Tabs,Name,ScopedUnionName]), + type_expand(G,N,X,Fd,Tabs,"label",DTC), + ic_codegen:emit(Fd,"%%~s value = ",[Tabs]), + type_expand_union_rule(Fd,LblList), + type_expand_all(G,N,X,Fd,Tabs,LblList). + +type_expand_enum(Fd,Tabs,Name,{tk_enum, _IFRId, EnumName, ElemNameList}) -> + ic_codegen:emit(Fd,"%%~s ~s = ~s~n",[Tabs,Name,EnumName]), + type_expand_enum_rule(Fd,Tabs,EnumName,ElemNameList). + +type_expand_sequence(G,N,X,Fd,Tabs,Name,{tk_sequence, ElemTC, _Length}) -> + ic_codegen:emit(Fd,"%%~s ~s = [ ~sElem ]~n",[Tabs,Name,Name]), + type_expand(G,N,X,Fd,Tabs,Name++"Elem",ElemTC). + +type_expand_array(G,N,X,Fd,Tabs,Name,{tk_array, ElemTC, _Length}) -> + ic_codegen:emit(Fd,"%%~s ~s = { ~sElem[,..~sElem] }~n",[Tabs,Name,Name,Name]), + type_expand(G,N,X,Fd,Tabs,Name++"Elem",ElemTC). + +type_expand_error(Fd,Tabs,Name) -> + ic_codegen:emit(Fd,"%%~s ~s = ????~n",[Tabs,Name]). + + +type_expand_exception_rule(Fd,_Name,[]) -> + ic_codegen:emit(Fd," ???? "); +type_expand_exception_rule(Fd,Name,TcList) -> + ic_codegen:emit(Fd,"#'~s'{",[Name]), + type_expand_exception_rule(Fd,TcList). + +type_expand_exception_rule(Fd,[{Name,_TC}]) -> + ic_codegen:emit(Fd,"~s}~n",[Name]); +type_expand_exception_rule(Fd,[{Name,_TC}|Rest]) -> + ic_codegen:emit(Fd,"~s,",[Name]), + type_expand_exception_rule(Fd,Rest). + +type_expand_struct_rule(Fd,_Name,[]) -> + ic_codegen:emit(Fd," ???? "); +type_expand_struct_rule(Fd,Name,TcList) -> + ic_codegen:emit(Fd,"#'~s'{",[Name]), + type_expand_struct_rule(Fd,TcList). + +type_expand_struct_rule(Fd,[{Name,_TC}]) -> + ic_codegen:emit(Fd,"~s}~n",[Name]); +type_expand_struct_rule(Fd,[{Name,_TC}|Rest]) -> + ic_codegen:emit(Fd,"~s,",[Name]), + type_expand_struct_rule(Fd,Rest). + + +type_expand_union_rule(Fd,[]) -> + ic_codegen:emit(Fd," ????"); +type_expand_union_rule(Fd,[{default,_Name,_TC}]) -> + ic_codegen:emit(Fd,"Def~n",[]); +type_expand_union_rule(Fd,[{LNr,_Name,_TC}]) when is_integer(LNr)-> + ic_codegen:emit(Fd,"V~p~n",[LNr]); +type_expand_union_rule(Fd,[{Label,_Name,_TC}]) -> + ic_codegen:emit(Fd,"~s~n",[Label]); +type_expand_union_rule(Fd,[{default,_Name,_TC}|Rest]) -> + ic_codegen:emit(Fd,"Default | "), + type_expand_union_rule(Fd,Rest); +type_expand_union_rule(Fd,[{LNr,_Name,_TC}|Rest]) when is_integer(LNr) -> + ic_codegen:emit(Fd,"V~p | ",[LNr]), + type_expand_union_rule(Fd,Rest); +type_expand_union_rule(Fd,[{Label,_Name,_TC}|Rest]) -> + ic_codegen:emit(Fd,"~s | ",[Label]), + type_expand_union_rule(Fd,Rest). + + +type_expand_enum_rule(Fd,Tabs,Name,[]) -> + ic_codegen:emit(Fd,"%%~s ~s = ????",[Tabs,Name]); +type_expand_enum_rule(Fd,Tabs,Name,ElList) -> + ic_codegen:emit(Fd,"%%~s ~s = ",[Tabs,Name]), + type_expand_enum_rule(Fd,ElList). + +type_expand_enum_rule(Fd,[ElName]) -> + ic_codegen:emit(Fd,"'~s' ~n",[ElName]); +type_expand_enum_rule(Fd,[First|Rest]) -> + ic_codegen:emit(Fd,"'~s' | ",[First]), + type_expand_enum_rule(Fd,Rest). + +type_expand_enum_elements(_Fd,_Tabs,[]) -> + ok; +type_expand_enum_elements(Fd,Tabs,[Elem|Elems]) -> + ic_codegen:emit(Fd,"%%~s ~s = Atom()~n",[Tabs,Elem]), + type_expand_enum_elements(Fd,Tabs,Elems). + + + +%% Returns the right scoped name to be used +%% along with the expansion comments +getScopedName(G,N,Name,IfrId) -> + PTab = ic_genobj:pragmatab(G), + case ets:match(PTab,{alias,'$0',IfrId}) of + [] -> %% No Alias - should never happen + ic_util:to_undersc(ic_pragma:mk_scope(IfrId)); + [[[_S|N]]] -> %% An alias + ic_util:to_undersc([Name|N]); + [[[S|FoundScope]]] -> %% Maybe inherited + case ic_pragma:is_inherited_by(FoundScope,N,PTab) of + false -> %% Not inherited + ic_util:to_undersc([S|FoundScope]); + true -> %% inherited + ic_util:to_undersc([Name|N]) + end + end. + + +%% mk_list produces a nice comma separated +%% string of variable names +mk_list([]) -> []; +mk_list([Arg | Args]) -> + Arg ++ mk_list2(Args). +mk_list2([Arg | Args]) -> + ", " ++ Arg ++ mk_list2(Args); +mk_list2([]) -> []. + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + + + diff --git a/lib/ic/src/ic_codegen.erl b/lib/ic/src/ic_codegen.erl new file mode 100644 index 0000000000..f611c69bea --- /dev/null +++ b/lib/ic/src/ic_codegen.erl @@ -0,0 +1,419 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_codegen). + +-include_lib("ic/src/ic.hrl"). +-include_lib("ic/src/icforms.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([emit/2, emit/3]). +-export([emit_c_enc_rpt/4, emit_c_dec_rpt/4]). +-export([comment/2, comment/3, comment/4, comment_inlined/5, comment_prefixed/4]). +-export([mcomment/2, mcomment/3, mcomment_inlined/5, mcomment_prefixed/3]). +-export([mcomment_light/2, mcomment_light/3, mcomment_light_inlined/5, mcomment_light_prefixed/3]). +-export([nl/1, export/2]). +-export([record/5]). +-export([emit_stub_head/4, emit_hrl_head/4, emit_hrl_foot/2]). +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Emit output as a formatted string, (old emit) +%%-------------------------------------------------------------------- +emit(nil, _) -> ok; +emit(Fd, Str) -> + file:write(Fd, Str). + +emit(nil, _, _) -> ok; +emit(Fd, Fmt, Args) -> + file:write(Fd, io_lib:format(Fmt, Args)). + +emit_c_enc_rpt(Fd, Prefix, Fmt, Args) -> + emit(Fd, Prefix ++ "OE_RPT_ERR(\"Encode error: " ++ Fmt ++ "\");\n", Args). + +emit_c_dec_rpt(Fd, Prefix, Fmt, Args) -> + emit(Fd, Prefix ++ "OE_RPT_ERR(\"Decode error: " ++ Fmt ++ "\");\n", Args). + +%%-------------------------------------------------------------------- +%% Emit comments +%%-------------------------------------------------------------------- +comment(Fd, C) -> + comment_prefixed(Fd, C, [], "%%"). + +comment(Fd, C, A) -> + comment_prefixed(Fd, C, A, "%%"). + +comment(Fd, C, A, c) -> + comment_inlined(Fd, C, A, "/*", "*/"); +comment(Fd, C, A, erl) -> + comment_prefixed(Fd, C, A, "%%"); +comment(Fd, C, A, java) -> + comment_prefixed(Fd, C, A, "//"); +%% Should be removed after a check if it's used !!!!! (LTH) +comment(Fd, C, A, CommentSequence) when is_list(CommentSequence) -> + comment_prefixed(Fd, C, A, CommentSequence). + +comment_inlined(Fd, C, A, Start, End) -> + emit(Fd, Start ++ " " ++ C ++ " " ++ End ++"\n", A). + +comment_prefixed(Fd, C, A, Prefix) -> + emit(Fd, Prefix ++ " " ++ C ++ "\n", A). + +%%-------------------------------------------------------------------- +%% Emit multiline comments with nice delimiters +%%-------------------------------------------------------------------- +mcomment(Fd, List) -> + mcomment_prefixed(Fd, List, "%%"). + +mcomment(Fd, List, c) -> + mcomment_inlined(Fd, List, "/*", "*/", " *"); +mcomment(Fd, List, erl) -> + mcomment_prefixed(Fd, List, "%%"); +mcomment(Fd, List, java) -> + mcomment_prefixed(Fd, List, "//"). + +mcomment_inlined(Fd, List, Start, End, Intermediate) -> + emit(Fd, Start ++ + "------------------------------------------------------------\n"), + emit(Fd, Intermediate ++ "\n"), + lists:foreach(fun(C) -> comment(Fd, C, [], Intermediate) end, List), + emit(Fd, Intermediate ++ "\n"), + emit(Fd, Intermediate ++ + "------------------------------------------------------------" ++ End ++ "\n"), + ok. +mcomment_prefixed(Fd, List, Prefix) -> + emit(Fd, Prefix ++ + "------------------------------------------------------------\n"), + emit(Fd, Prefix ++ "\n"), + lists:foreach(fun(C) -> comment(Fd, C, [], Prefix) end, List), + emit(Fd, Prefix ++ "\n"), + emit(Fd, Prefix ++ + "------------------------------------------------------------\n"), + ok. + + +%%-------------------------------------------------------------------- +%% Emit multiline comments with nice delimiters as above but a +%% little lighter +%%-------------------------------------------------------------------- +mcomment_light(Fd, List) -> + mcomment_light_prefixed(Fd, List, "%%"). + +mcomment_light(Fd, List, c) -> + mcomment_light_inlined(Fd, List, "/*", " */", " *"); +mcomment_light(Fd, List, erl) -> + mcomment_light_prefixed(Fd, List, "%%"); +mcomment_light(Fd, List, java) -> + mcomment_light_prefixed(Fd, List, "//"); +%% Should be removed after a check if it's used !!!!! (LTH) +mcomment_light(Fd, List, Prefix) when is_list(Prefix) -> + mcomment_light_prefixed(Fd, List, Prefix). + +mcomment_light_inlined(Fd, List, Start, End, Intermediate) -> + emit(Fd, "\n" ++ Start ++ "\n"), + lists:foreach(fun(C) -> comment(Fd, C, [], Intermediate) end, List), + emit(Fd, End ++ "\n"), + ok. + +mcomment_light_prefixed(Fd, List, Prefix) -> + emit(Fd, Prefix), + lists:foreach(fun(C) -> comment(Fd, C, [], Prefix) end, List), + emit(Fd, Prefix ++ "\n"), + ok. + +%%-------------------------------------------------------------------- +%% New line +%%-------------------------------------------------------------------- +nl(Fd) -> + emit(Fd, "\n"). + + +%%-------------------------------------------------------------------- +-define(IFRIDFIELD(G), ic_util:mk_name(G, "ID")). + +%%-------------------------------------------------------------------- +%% Emit record definitions for erlang +%%-------------------------------------------------------------------- +record(G, X, Name, _IFRID, Recs) when is_record(X, struct) -> + F = ic_genobj:hrlfiled(G), + emit(F, "-record(~p, {~p", [ic_util:to_atom(Name),hd(Recs)]), + lists:foreach(fun(Y) -> emit(F, ", ~p", [Y]) end, tl(Recs)), + emit(F, "}).\n"); +record(G, X, Name, _IFRID, _Recs) when is_record(X, union) -> + F = ic_genobj:hrlfiled(G), + emit(F, "-record(~p, {label, value}).\n",[ic_util:to_atom(Name)]); +record(G, _X, Name, IFRID, Recs) when length(Recs) > 3 -> + F = ic_genobj:hrlfiled(G), + emit(F, "-record(~p,~n {~p=~p", + [ic_util:to_atom(Name), ic_util:to_atom(?IFRIDFIELD(G)), IFRID]), + rec2(F, "", ", ", Recs), + emit(F, "}).\n"); +record(G, _X, Name, IFRID, Recs) -> + F = ic_genobj:hrlfiled(G), + emit(F, "-record(~p, {~p=~p", [ic_util:to_atom(Name), + ic_util:to_atom(?IFRIDFIELD(G)), + IFRID]), + lists:foreach(fun(Y) -> emit(F, ", ~p", [Y]) end, Recs), + emit(F, "}).\n"). + + +rec2(F, Align, Delim, [M1 , M2, M3 | Ms]) -> + emit(F, "~s~s~p, ~p, ~p", [Delim, Align, M1, M2, M3]), + rec2(F, " ", ",\n", Ms); +rec2(F, Align, Delim, [M1 , M2]) -> + emit(F, "~s~s~p, ~p", [Delim, Align, M1, M2]); +rec2(F, Align, Delim, [M]) -> + emit(F, "~s~s~p", [Delim, Align, M]); +rec2(_F, _Align, _Delim, []) -> + ok. + + +%%-------------------------------------------------------------------- +%% Emit export lists for erlang +%%-------------------------------------------------------------------- +export(F, [E1, E2, E3 | Exports]) -> + emit(F, "-export([~s]).\n", [exp_list([E1, E2, E3])]), + export(F, Exports); +export(_F, []) -> ok; +export(F, Exports) -> + emit(F, "-export([~s]).\n", [exp_list(Exports)]). + +exp_list([E1 | L]) -> + exp_to_string(E1) ++ + lists:map(fun(E) -> ", " ++ exp_to_string(E) end, L). + + +exp_to_string({F,N}) -> io_lib:format("~p/~p", [ic_util:to_atom(F), N]). + + +%%-------------------------------------------------------------------- +%% Emit Stub file header +%%-------------------------------------------------------------------- +emit_stub_head(_G, ignore, _Name, _) -> ignore; +emit_stub_head(G, F1, Name, erlang) -> + mcomment(F1, stub_header(G, Name)), + nl(F1), + emit(F1, "-module(~p).\n", [list_to_atom(Name)]), + emit(F1, "-ic_compiled(~p).\n", [compiler_vsn(?COMPILERVSN)]), + emit(F1, "\n\n"), F1; +emit_stub_head(G, F1, Name, erlang_template) -> + ic_erl_template:emit_header(G, F1, Name), + F1; +emit_stub_head(_G, F1, _Name, erlang_template_no_gen) -> + F1; +emit_stub_head(G, F1, Name, c) -> + mcomment(F1, stub_header(G, Name), c), + emit(F1, "int ic_compiled_~s_~s;\n", [compiler_vsn(?COMPILERVSN), Name]), + emit(F1, "\n\n"), F1; +emit_stub_head(G, F1, Name, c_server) -> + CSName = [Name, "__s"], + mcomment(F1, stub_header(G, CSName), c), + emit(F1, "int ic_compiled_~s_~s;\n", [compiler_vsn(?COMPILERVSN), CSName]), + emit(F1, "\n\n"), F1; +emit_stub_head(G, F1, Name, java) -> + mcomment(F1, stub_header(G, Name), java), + emit(F1, "\n\n"), F1. + +stub_header(G, Name) -> + ["Implementation stub file", + "", + io_lib:format("Target: ~s", [Name]), + io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]), + io_lib:format("IC vsn: ~s", [?COMPILERVSN]), + "", + "This file is automatically generated. DO NOT EDIT IT."]. + +compiler_vsn(Vsn) -> + lists:map(fun($.) -> $_; + (C) -> C + end, Vsn). + +%%-------------------------------------------------------------------- +%% Emit include file header +%%-------------------------------------------------------------------- +%% Name is Fully scoped (undescore) name of interface or module +emit_hrl_head(_G, ignore, _Name, _) -> ignore; +emit_hrl_head(G, Fd, Name, erlang) -> + mcomment(Fd, ["Erlang header file" | + hrl_header(G, Name)]), + nl(Fd), + nl(Fd), + IfdefName = ic_util:to_uppercase(Name++"_HRL"), + emit(Fd, "-ifndef(~s).~n", [IfdefName]), + emit(Fd, "-define(~s, true).~n", [IfdefName]), + nl(Fd), + nl(Fd), + Fd; +emit_hrl_head(G, Fd, Name, c) -> + mcomment(Fd, ["C header file" | + hrl_header(G, Name)], c), + nl(Fd), + nl(Fd), + IfdefName = ic_util:to_uppercase(Name++"_H"), + emit(Fd, "#ifndef ~s~n", [IfdefName]), + emit(Fd, "#define ~s ~n", [IfdefName]), + nl(Fd), + nl(Fd), + Fd; +emit_hrl_head(G, Fd, Name, c_server) -> + mcomment(Fd, ["C header file" | + hrl_header(G, [Name, "__s"])], c), + nl(Fd), + nl(Fd), + IfdefName = ic_util:to_uppercase(Name++"__S_H"), + emit(Fd, "#ifndef ~s~n", [IfdefName]), + emit(Fd, "#define ~s ~n", [IfdefName]), + nl(Fd), + nl(Fd), + Fd. + +hrl_header(G, Name) -> + ["", + io_lib:format("Target: ~s", [Name]), + io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]), + io_lib:format("IC vsn: ~s", [?COMPILERVSN]), + "", + "This file is automatically generated. DO NOT EDIT IT."]. + + + + +%%-------------------------------------------------------------------- +%% Emit include file footer +%%-------------------------------------------------------------------- +emit_hrl_foot(_G, erlang_template) -> + ok; +emit_hrl_foot(_G, erlang_template_no_gen) -> + ok; +emit_hrl_foot(G, erlang) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + nl(Fd), + nl(Fd), + emit(Fd, "-endif.\n"), + nl(Fd), + nl(Fd), + Fd; + false -> + ok + end; +emit_hrl_foot(G, erlang_no_stub) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + nl(Fd), + nl(Fd), + emit(Fd, "-endif.\n"), + nl(Fd), + nl(Fd), + Fd; + false -> + ok + end; +emit_hrl_foot(G, c) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + nl(Fd), + nl(Fd), + emit(Fd, "#ifdef __cplusplus\n"), + emit(Fd, "}\n"), + emit(Fd, "#endif\n"), + nl(Fd), + emit(Fd, "#endif\n"), + nl(Fd), + nl(Fd), + Fd; + false -> + ok + end; +emit_hrl_foot(G, c_server) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + nl(Fd), + nl(Fd), + emit(Fd, "#ifdef __cplusplus\n"), + emit(Fd, "}\n"), + emit(Fd, "#endif\n"), + nl(Fd), + emit(Fd, "#endif\n"), + nl(Fd), + nl(Fd), + Fd; + false -> + ok + end; +emit_hrl_foot(G, c_no_stub) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + nl(Fd), + nl(Fd), + emit(Fd, "#ifdef __cplusplus\n"), + emit(Fd, "}\n"), + emit(Fd, "#endif\n"), + nl(Fd), + emit(Fd, "#endif\n"), + nl(Fd), + nl(Fd), + Fd; + false -> + ok + end; +emit_hrl_foot(G, c_server_no_stub) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + nl(Fd), + nl(Fd), + emit(Fd, "#ifdef __cplusplus\n"), + emit(Fd, "}\n"), + emit(Fd, "#endif\n"), + nl(Fd), + emit(Fd, "#endif\n"), + nl(Fd), + nl(Fd), + Fd; + false -> + ok + end. + + + + + + + + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- diff --git a/lib/ic/src/ic_constant_java.erl b/lib/ic/src/ic_constant_java.erl new file mode 100644 index 0000000000..0a3172363f --- /dev/null +++ b/lib/ic/src/ic_constant_java.erl @@ -0,0 +1,99 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_constant_java). + + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([gen/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: gen/3 +%%----------------------------------------------------------------- +gen(G, N, X) when is_record(X, const) -> + ConstantName = ic_forms:get_java_id(X), + case inInterface(G, N) of + true -> + emit_constant(G, N, X, ConstantName); + false -> + emit_constant_interface(G, N, X, ConstantName) + end; +gen(_G, _N, _X) -> + ok. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: emit_constant/4 +%%----------------------------------------------------------------- +emit_constant(G, N, X, ConstantName) -> + Fd = ic_genobj:interfacefiled(G), + %%?PRINTDEBUG2("~p", [Fd]), + Type = ic_java_type:getType(G, N, ic_forms:get_type(X)), + ic_codegen:emit(Fd, " public static final ~s ~s = (~s) ~p;\n", + [Type, ConstantName, Type, X#const.val]), + ic_codegen:nl(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_constant_interface/4 +%%----------------------------------------------------------------- +emit_constant_interface(G, N, X, ConstantName) -> + {Fd, _} = ic_file:open_java_file(G, N, ConstantName), + + ic_codegen:emit(Fd, "final public class ~s {\n",[ConstantName]), + + Type = ic_java_type:getType(G, N, ic_forms:get_type(X)), + ic_codegen:emit(Fd, " public static final ~s value = (~s) ~p;\n", + [Type, Type, X#const.val]), + ic_codegen:emit(Fd, "}\n", []), + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_constant_interface/4 +%%----------------------------------------------------------------- +inInterface(_G, []) -> % Global constant + false; +inInterface(G, N) -> + [N1 |Ns] = N, + {_FullScopedName, T, _TK, _} = + ic_symtab:get_full_scoped_name(G, Ns, ic_symtab:scoped_id_new(N1)), + case T of + interface -> % Constant declare in an interface + true; + _ -> % Constant declared in a module + false + end. + diff --git a/lib/ic/src/ic_cserver.erl b/lib/ic/src/ic_cserver.erl new file mode 100644 index 0000000000..52d98c5795 --- /dev/null +++ b/lib/ic/src/ic_cserver.erl @@ -0,0 +1,2419 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_cserver). + +%% This module implements generation of C server code, where the +%% server acts as an Erlang C-node, where the functionality is that of +%% a gen_server (in C), and where the communication thus is according +%% to the Erlang distribution protocol. +%% + +-export([do_gen/3]). + +%% Silly dialyzer. +-export([filterzip/3]). + +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ + +-import(lists, [foreach/2, foldl/3, foldr/3, map/2]). +-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(OE_MSGBUFSIZE, "OE_MSGBUFSIZE"). +-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_server), + gen_headers(G2, [], Form), + R = gen(G2, [], Form), + ic_file:filename_pop(G2, c), + R. + +remove_ext(File) -> + filename:rootname(filename:basename(File)). + +%%------------------------------------------------------------ +%% +%% Generate the server side C stub and header files. +%% +%% For each module a separate file is generated. +%% +%% +%%------------------------------------------------------------ + +gen(G, N, [X| Xs]) when is_record(X, preproc) -> + NewG = change_file_stack(G, N, X#preproc.cat, X), + gen(NewG, 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_server), + N2 = [ic_forms:get_id2(X)| N], + gen_prototypes(G2, N2, X), + gen_serv(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) -> + 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, [_| Xs]) -> + gen(G, N, Xs); + +gen(_G, _N, []) -> + ok. + +%%------------------------------------------------------------ +%% Change file stack +%%------------------------------------------------------------ + +change_file_stack(G, _N, line_nr, X) -> + 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, _Other, _X) -> + G. + +%%------------------------------------------------------------ +%% Generate headers +%%------------------------------------------------------------ + +%% 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_server); + false -> ok + end; +gen_headers(G, [], _X) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + HFd = ic_genobj:hrlfiled(G), + emit(HFd, "#include <stdlib.h>\n"), + 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_server); + false -> ok + end; +gen_headers(_G, _N, _X) -> + ok. + +%%------------------------------------------------------------ +%% Generate prototypes +%%------------------------------------------------------------ + +gen_prototypes(G, N, X) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + 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, + + IName = ic_util:to_undersc(N), + INameUC = ic_util:to_uppercase(IName), + + emit(HFd, "#include \"~s\"\n", [filename:basename(Filename)]), + ic_code:gen_includes(HFd, G, X, c_server), + ic_codegen:nl(HFd), + + emit(HFd, "\n#ifndef __~s__\n", [ic_util:to_uppercase(IName)]), + emit(HFd, "#define __~s__\n", [ic_util:to_uppercase(IName)]), + ic_codegen:mcomment_light(HFd, + [io_lib:format("Interface " + "object " + "definition: ~s", + [IName])], c), + case get_c_timeout(G, "") of + "" -> + ok; + {SendTmo, RecvTmo} -> + emit(HFd, "#define OE_~s_SEND_TIMEOUT ~s\n", + [INameUC, SendTmo]), + emit(HFd, "#define OE_~s_RECV_TIMEOUT ~s\n", + [INameUC, 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\n", [IName]), + emit(HFd, "#endif\n\n"), + + Bodies = [{N, ic_forms:get_body(X)}| X#interface.inherit_body], + + emit(HFd, "\n/* Structure definitions */\n", []), + foreach(fun({N2, Body}) -> + emit_structs_inside_module(G, HFd, N2, Body) end, + Bodies), + + emit(HFd, "\n/* Switch and exec functions */\n", []), + emit(HFd, "int ~s__switch(~s oe_obj, CORBA_Environment " + "*oe_env);\n", [IName, IName]), + foreach(fun({_N2, Body}) -> + emit_exec_prototypes(G, HFd, N, Body) end, + Bodies), + + emit(HFd, "\n/* Generic decoder */\n", []), + emit(HFd, "int ~s__call_info(~s oe_obj, CORBA_Environment " + "*oe_env);\n", [IName, IName]), + + emit(HFd, "\n/* Restore function typedefs */\n", []), + foreach(fun({_N2, Body}) -> + emit_restore_typedefs(G, HFd, N, Body) end, + Bodies), + + emit(HFd, "\n/* Callback functions */\n", []), + foreach(fun({_N2, Body}) -> + emit_callback_prototypes(G, HFd, N, Body) end, + Bodies), + + emit(HFd, "\n/* Parameter decoders */\n", []), + foreach(fun({_N2, Body}) -> + emit_decoder_prototypes(G, HFd, N, Body) end, + Bodies), + + emit(HFd, "\n/* Message encoders */\n", []), + foreach(fun({_N2, Body}) -> + emit_encoder_prototypes(G, HFd, N, Body) end, + Bodies), + + %% Emit operation mapping structures + emit_operation_mapping_declaration(G, HFd, N, Bodies), + + ok; + + false -> + ok + end. + +%%------------------------------------------------------------ +%% Generate the server encoding/decoding function +%%------------------------------------------------------------ + + +gen_serv(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + Fd = ic_genobj:stubfiled(G), + + emit_switch(G, Fd, N, X), + emit_server_generic_decoding(G, Fd, N), + + %% Sets the temporary variable counter. + put(op_variable_count, 0), + put(tmp_declarations, []), + + %% Generate exec, decode and encoding functions, and + %% table of exec functions. + Bodies = [{N, ic_forms:get_body(X)}| + X#interface.inherit_body], + + foreach(fun({_N2, Body}) -> + emit_dispatch(G, Fd, N, Body) end, + Bodies), + emit_operation_mapping(G, Fd, N, Bodies); + false -> + ok + end. + +%%------------------------------------------------------------ +%% Emit structs inside module +%%------------------------------------------------------------ + +emit_structs_inside_module(G, _Fd, N, Xs)-> + lists:foreach( + fun(X) when is_record(X, enum) -> + icenum:enum_gen(G, N, X, c); + (X) when is_record(X, typedef) -> + icstruct:struct_gen(G, N, X, c); + (X) when is_record(X, struct) -> + icstruct:struct_gen(G, N, X, c); + (X) when is_record(X, union) -> + icstruct:struct_gen(G, N, X, c); + (_) -> + ok + end, Xs). + +%%------------------------------------------------------------ +%% Emit exec prototypes +%%------------------------------------------------------------ + +emit_exec_prototypes(G, Fd, N, Xs) -> + lists:foreach( + fun(X) when is_record(X, op) -> + {ScopedName, _, _} = ic_cbe:extract_info(G, N, X), + emit(Fd, + "int ~s__exec(~s oe_obj, CORBA_Environment *oe_env);\n", + [ScopedName, ic_util:to_undersc(N)]); + (X) when is_record(X, const) -> + emit_constant(G, N, X); + (_) -> + ok + end, Xs). + +%%------------------------------------------------------------ +%% Emit restore typedefs +%%------------------------------------------------------------ + +emit_restore_typedefs(G, Fd, N, [X| Xs]) when is_record(X, op) -> + %% Check if to use scoped call names + {ScopedName, ArgNames, Types} = ic_cbe:extract_info(G, N, X), + {RetType, ParTypes, _} = Types, + TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames), + RT = mk_c_ret_type(G, N, RetType), + + PL = ic_util:mk_list(mk_par_list_for_callback_prototypes(G, N, X, + TypeAttrArgs)), + RPL = case PL of + "" -> + ""; + _PL -> + ", " ++ PL + end, + + case RT of + "void" -> + case PL of + "" -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N)]); + _ -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, ~s, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N), PL]) + end; + + "erlang_port*" -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N), RT, RPL]); + + "erlang_pid*" -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N), RT, RPL]); + + "erlang_ref*" -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N), RT, RPL]); + + _ -> + case ictype:isArray(G, N, RetType) of + true -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N), RT, RPL]); + false -> + emit(Fd, "typedef void (*~s__rs(~s oe_obj, ~s*~s, " + "CORBA_Environment *oe_env));\n", + [ScopedName, ic_util:to_undersc(N), RT, RPL]) + end + end, + emit_restore_typedefs(G, Fd, N, Xs); +emit_restore_typedefs(G, Fd, N, [X| Xs]) when is_record(X, attr) -> + emit_restore_typedefs(G, Fd, N, Xs); +emit_restore_typedefs(G, Fd, N, [_X| Xs]) -> + emit_restore_typedefs(G, Fd, N, Xs); +emit_restore_typedefs(_G, _Fd, _N, []) -> ok. + + +%%------------------------------------------------------------ +%% Emit call-back prototypes +%%------------------------------------------------------------ + +emit_callback_prototypes(G, Fd, N, [X| Xs]) when is_record(X, op) -> + %% Check scoped names XXX + {ScopedName, ArgNames, Types} = ic_cbe:extract_info(G, N, X), + {RetType, ParTypes, _} = Types, + TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames), + RT = mk_c_ret_type(G, N, RetType), + + PL = ic_util:mk_list(mk_par_list_for_callback_prototypes(G, N, X, + TypeAttrArgs)), + CBPL = case PL of + "" -> + ""; + _PL -> + ", " ++ PL + end, + case RT of + "void" -> + case PL of + "" -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N)]); + _ -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, ~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N), PL]) + end; + "erlang_port*" -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N), RT, CBPL]); + + "erlang_pid*" -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N), RT, CBPL]); + + "erlang_ref*" -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N), RT, CBPL]); + + _ -> + case ictype:isArray(G, N, RetType) of + true -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, ~s~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N), RT, + CBPL]); + false -> + emit(Fd, "~s__rs* ~s__cb(~s oe_obj, ~s*~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ScopedName, ic_util:to_undersc(N), RT, + CBPL]) + end + end, + emit_callback_prototypes(G, Fd, N, Xs); +emit_callback_prototypes(G, Fd, N, [X| Xs]) when is_record(X, attr) -> + emit_callback_prototypes(G, Fd, N, Xs); +emit_callback_prototypes(G, Fd, N, [_X| Xs]) -> + emit_callback_prototypes(G, Fd, N, Xs); +emit_callback_prototypes(_G, _Fd, _N, []) -> ok. + +%%------------------------------------------------------------ +%% Emit decoder prototypes +%%------------------------------------------------------------ + +emit_decoder_prototypes(G, Fd, N, [X| Xs]) when is_record(X, op) -> + %% Check if to use scoped call names + {ScopedName, ArgNames, Types} = ic_cbe:extract_info(G, N, X), + {_RetType, ParTypes, _} = Types, + TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames), + case ic_util:mk_list(mk_par_list_for_decoder_prototypes(G, N, X, + TypeAttrArgs)) of + "" -> + ok; + PLFDP -> + emit(Fd, "int ~s__dec(~s oe_obj, ~s, CORBA_Environment " + "*oe_env);\n", + [ScopedName, ic_util:to_undersc(N), PLFDP]) + end, + emit_decoder_prototypes(G, Fd, N, Xs); +emit_decoder_prototypes(G, Fd, N, [X| Xs]) when is_record(X, attr) -> + emit_decoder_prototypes(G, Fd, N, Xs); +emit_decoder_prototypes(G, Fd, N, [_X| Xs]) -> + emit_decoder_prototypes(G, Fd, N, Xs); +emit_decoder_prototypes(_G, _Fd, _N, []) -> ok. + + +%%------------------------------------------------------------ +%% Emit encoder prototypes +%%------------------------------------------------------------ + +emit_encoder_prototypes(G, Fd, N, [X| Xs]) when is_record(X, op) -> + case ic_forms:is_oneway(X) of + true -> + emit_encoder_prototypes(G, Fd, N, Xs); + false -> + %% Check if to use scoped call names + {ScopedName, ArgNames, Types} = ic_cbe:extract_info(G, N, X), + {RetType, ParTypes, _} = Types, + TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames), + RType = mk_c_ret_type(G, N, RetType), + case ic_util:mk_list(mk_par_list_for_encoder_prototypes( + G, N, X, TypeAttrArgs)) of + "" -> + case RType of + "void" -> + emit(Fd, "int ~s__enc(~s oe_obj, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ic_util:to_undersc(N)]); + _ -> + emit(Fd, "int ~s__enc(~s oe_obj, ~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ic_util:to_undersc(N), RType]) + end; + PLFEP -> + case RType of + "void" -> + emit(Fd, "int ~s__enc(~s oe_obj, ~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ic_util:to_undersc(N), PLFEP]); + _ -> + emit(Fd, "int ~s__enc(~s oe_obj, ~s, ~s, " + "CORBA_Environment *oe_env);\n", + [ScopedName, ic_util:to_undersc(N), RType, + PLFEP]) + end + end, + emit_encoder_prototypes(G, Fd, N, Xs) + end; +emit_encoder_prototypes(G, Fd, N, [X| Xs]) when is_record(X, attr) -> + emit_encoder_prototypes(G, Fd, N, Xs); +emit_encoder_prototypes(G, Fd, N, [_X| Xs]) -> + emit_encoder_prototypes(G, Fd, N, Xs); +emit_encoder_prototypes(_G, _Fd, _N, []) -> ok. + +%%------------------------------------------------------------ +%% Emit operation mapping declaration +%%------------------------------------------------------------ + +emit_operation_mapping_declaration(G, Fd, N, Bodies) -> + Interface = ic_util:to_undersc(N), + Length = erlang:length(get_all_opnames(G, N, Bodies)), + emit(Fd, "\n/* Operation mapping */\n", []), + emit(Fd, "extern oe_map_t oe_~s_map;\n", [Interface]), + emit(Fd, "/* For backward compatibility */\n"), + emit(Fd, "#define ___~s_map___ oe_~s_map\n", + [Interface, Interface]), + case Length of + 0 -> + ok; + _ -> + emit(Fd, "extern oe_operation_t oe_~s_operations[];\n", + [Interface]), + emit(Fd, "/* For backward compatibility */\n"), + emit(Fd, "#define ___~s_operations___ oe_~s_operations\n", + [Interface, Interface]) + end. + + +%% Returns a list of {OpName, ScopedOpName} for all operations, where +%% OpName == ScopedOpName in case the `scoped_op_calls' option has +%% been set. +%% +get_all_opnames(G, N, Bodies) -> + ScNF = fun(X) -> + {ScName, _, _} = ic_cbe:extract_info(G, N, X), + ScName + end, + NF = case ic_options:get_opt(G, scoped_op_calls) of + true -> + ScNF; + false -> + fun(X) -> ic_forms:get_id2(X) end + end, + Filter = fun(X) when is_record(X, op) -> + {true, {NF(X), ScNF(X)}}; + (_) -> + false + end, + %% zf == filtermap + lists:flatmap(fun({_, Xs}) -> lists:zf(Filter, Xs) end, Bodies). + +%%------------------------------------------------------------ +%% Emit switch +%%------------------------------------------------------------ + +emit_switch(G, Fd, N, _X) -> + 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, + StartCode = + "#include \"ic.h\"\n" + "#include \"erl_interface.h\"\n" + "#include \"ei.h\"\n" + "#include \"~s__s.h\"\n\n" + "/*\n" + " * Main switch\n" + " */\n\n" + "int ~s__switch(~s oe_obj, CORBA_Environment *oe_env)\n" + "{\n" + " return oe_exec_switch(oe_obj, oe_env, &oe_~s_map);\n" + "}\n\n", + ScopedName = ic_util:to_undersc(N), + emit(Fd, StartCode, [ScopedName, ScopedName, ScopedName, ScopedName]). + +%%------------------------------------------------------------ +%% Emit server generic decoding. +%%------------------------------------------------------------ + +emit_server_generic_decoding(G, Fd, N) -> + UserProto = get_user_proto(G, oe), + Code = + "/*\n" + " * Returns call identity (left only for backward compatibility)\n" + " */\n\n" + "int ~s__call_info(~s oe_obj, CORBA_Environment *oe_env)\n" + "{\n" + " return ~s_prepare_request_decoding(oe_env);\n" + "}\n\n", + IName = ic_util:to_undersc(N), + emit(Fd, Code, [IName, IName, UserProto]). + +%%------------------------------------------------------------ +%% Emit dispatch +%%------------------------------------------------------------ + +emit_dispatch(G, Fd, N, Xs) -> + lists:foreach( + fun(X) when is_record(X, op) -> + {Name, ArgNames, Types} = ic_cbe:extract_info(G, N, X), + {RetType, ParTypes, _} = Types, + TypeAttrArgs = mk_type_attr_arg_list(ParTypes, ArgNames), + emit_exec_function(G, Fd, N, X, Name, RetType, TypeAttrArgs), + emit_parameter_decoder(G, Fd, N, X, Name, RetType, TypeAttrArgs), + emit_message_encoder(G, Fd, N, X, Name, RetType, TypeAttrArgs); + (_) -> + ok + end, Xs). + +%%------------------------------------------------------------ +%% Emit operation mapping +%%------------------------------------------------------------ + +emit_operation_mapping(G, Fd, N, Bodies) -> + OpNames = get_all_opnames(G, N, Bodies), + Interface = ic_util:to_undersc(N), + Length = erlang:length(OpNames), + emit(Fd, "\n/* Operation mapping */\n\n", []), + case Length of + 0 -> + emit(Fd, "oe_map_t oe_~s_map = { 0, NULL };\n\n", [Interface]); + _ -> + emit(Fd, "\noe_operation_t oe_~s_operations[~p] = {\n", + [Interface, Length]), + Members = lists:map( + fun({OpN, ScOpN}) -> + Name = ic_util:to_undersc([OpN]), + ScName = ic_util:to_undersc([ScOpN]), + io_lib:fwrite(" {~p, ~p, ~s__exec}", + [Interface, Name, ScName]) + end, OpNames), + emit(Fd, ic_util:join(Members, ",\n")), + emit(Fd, "};\n\n", []), + emit(Fd, "oe_map_t oe_~s_map = " + "{~p, oe_~s_operations};\n\n", + [Interface, Length, Interface]) + 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\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\n", [CName, + ConstRecord#const.val]); + true -> + emit(Fd, "#define ~s ~p\n\n", [CName, + ConstRecord#const.val]) + end, + + emit(Fd, "#endif\n\n") + end. + +%%------------------------------------------------------------ +%% Emit exec function +%%------------------------------------------------------------ + +emit_exec_function(G, Fd, N, X, Name, RetType, TypeAttrArgs) -> + %% Decoding operation specific part + InTypeAttrArgs = lists:filter(fun({_, in, _}) -> true; + ({_, _, _}) -> false + end, TypeAttrArgs), + ic_codegen:nl(Fd), + + emit(Fd, + "int ~s__exec(~s oe_obj, CORBA_Environment *oe_env)\n" + "{\n", + [Name, ic_util:to_undersc(N)]), + + emit(Fd, " if (oe_env->_received != ~p) {\n", [length(InTypeAttrArgs)]), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_PARAM, " + "\"Wrong number of operation parameters\");\n"), + emit_c_dec_rpt(Fd, " ", "wrong number of parameters", []), + emit_c_dec_rpt(Fd, " ", "server exec ~s\\n====\\n", [Name]), + emit(Fd, " return -1;\n", []), + emit(Fd, " }\n"), + emit(Fd, " else {\n", []), + + case InTypeAttrArgs of + [] -> + true; + _ -> + emit(Fd, " int oe_error_code = 0;\n") + end, + + %% Callback variable definition + emit_variable_defs(G, Fd, N, X, Name, RetType, TypeAttrArgs), + + %% Call to parameter decoder + emit_parameter_decoder_call(G, Fd, N, X, Name, RetType, TypeAttrArgs), + + %% Callback to user code + emit_callback(G, Fd, N, X, Name, RetType, TypeAttrArgs), + + %% Call to return message encoder + case ic_forms:is_oneway(X) of + true -> + true; + false -> + emit_message_encoder_call(G, Fd, N, X, Name, RetType, TypeAttrArgs) + end, + + %% Restore function call + emit_restore(G, Fd, N, X, Name, RetType, TypeAttrArgs), + + emit(Fd, " }\n return 0;\n}\n\n"). + +%%------------------------------------------------------------ +%% Emit parameter decoder +%%------------------------------------------------------------ + +emit_parameter_decoder(G, Fd, N, X, Name, _RetType, TypeAttrArgs) -> + %% Decoding operation specific part + InTypeAttrArgs = + lists:filter(fun({_, in, _}) -> true; + ({_, _, _}) -> false + end, TypeAttrArgs), + case InTypeAttrArgs of + [] -> + ok; + _ -> + case ic_util:mk_list(mk_par_list_for_decoder(G, N, X, + TypeAttrArgs)) of + "" -> + emit(Fd, "int ~s__dec(~s oe_obj, CORBA_Environment " + "*oe_env)\n{\n int oe_error_code;\n\n", + [Name, ic_util:to_undersc(N)]); + PLFD -> + emit(Fd, "int ~s__dec(~s oe_obj, ~s, CORBA_Environment " + "*oe_env)\n{\n", + [Name, ic_util:to_undersc(N), PLFD]), + emit(Fd, " int oe_error_code;\n\n") + end, + + APars = [], % XXX Alloced parameters + foldl( + fun({{'void', _}, _, _}, _Acc) -> + ok; + ({T1, A1, N1}, Acc) -> + emit_one_decoding(G, N, Fd, T1, A1, N1, Acc) + end, APars, InTypeAttrArgs), + + emit(Fd, " return 0;\n}\n\n") + end. + +%%------------------------------------------------------------ +%% Emit one decoding +%%------------------------------------------------------------ + +emit_one_decoding(G, N, Fd, T1, A1, N1, AllocedPars) -> + IndOp = mk_ind_op(A1), + case ic_cbe:is_variable_size(G, N, T1) of + false -> + %% The last parameter "oe_outindex" is not used in + %% the static case but must be there anyhow. + emit_decoding_stmt(G, N, Fd, T1, + N1, "", "oe_env->_inbuf", 1, "&oe_outindex", + caller, AllocedPars), + ic_codegen:nl(Fd), + AllocedPars; + true -> + emit_encoding_comment(G, N, Fd, "Decode", IndOp, T1, N1), + emit(Fd, " {\n"), + emit(Fd, " int oe_size_count_index = oe_env->_iin;\n"), + emit(Fd, " int oe_malloc_size = 0;\n"), + emit(Fd, " void *oe_first = NULL;\n"), + ic_cbe:emit_malloc_size_stmt(G, N, Fd, T1, + "oe_env->_inbuf", 1, caller), + %% This is the only malloc call in this file + emit(Fd, + " OE_MALLOC_SIZE_CHECK(oe_env, oe_malloc_size);\n" + " if ((*~s = oe_first = " + "malloc(oe_malloc_size)) == NULL) {\n", [N1]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit(Fd, + " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "NO_MEMORY, \"Cannot malloc\");\n" + " return -1;\n" + " }\n"), + ParName = "*" ++ N1, % XXX Why not IndOp? + NAllocedPars = [ParName| AllocedPars], + case ictype:isArray(G, N, T1) of + true -> + emit_decoding_stmt(G, N, Fd, T1, + "(*" ++ IndOp ++ N1 ++ ")", "", + "oe_env->_inbuf", 1, "&oe_outindex", + array_dyn, NAllocedPars); + false -> + emit_decoding_stmt(G, N, Fd, T1, + "(*" ++ IndOp ++ N1 ++ ")", "", + "oe_env->_inbuf", 1, "&oe_outindex", + caller_dyn, NAllocedPars) + end, + emit(Fd, " }\n\n"), + NAllocedPars + end. + +%%------------------------------------------------------------ +%% Emit message encoder +%%------------------------------------------------------------ + +emit_message_encoder(G, Fd, N, X, Name, RetType, TypeAttrArgs) -> + case ic_forms:is_oneway(X) of + false -> + %% Encoding operation specific part + emit(Fd, + "\nint ~s__enc(~s oe_obj", + [Name, ic_util:to_undersc(N)]), + RType = mk_c_ret_type(G, N, RetType), + ParList = mk_par_list_for_encoder(G, N, X, TypeAttrArgs), + case ic_util:mk_list(ParList) of + "" -> + case RType of + "void" -> + emit(Fd, ", CORBA_Environment *oe_env)\n{"); + _ -> + emit(Fd, ", ~s oe_return, CORBA_Environment " + "*oe_env)\n{", [RType]) + end; + PLFD -> + case RType of + "void" -> + emit(Fd, ", ~s, CORBA_Environment " + "*oe_env)\n{", [PLFD]); + _ -> + emit(Fd, ", ~s oe_return~s, CORBA_Environment " + "*oe_env)\n{", [RType, ", " ++ PLFD]) + end + end, + + + emit(Fd, "\n"), + emit(Fd, " int oe_error_code;\n\n"), + UserProto = get_user_proto(G, oe), + emit(Fd, " ~s_prepare_reply_encoding(oe_env);\n", [UserProto]), + + OutTypeAttrArgs = + lists:filter(fun({_, out, _}) -> true; + ({_, _, _}) -> false + end, TypeAttrArgs), + + OutLength = length(OutTypeAttrArgs), + case OutLength > 0 of + false -> + ic_codegen:nl(Fd); + true -> + emit(Fd, " oe_ei_encode_tuple_header(oe_env, ~p);\n\n", + [OutLength+1]) + + end, + + emit_encoding_comment(G, N, Fd, "Encode", "", RetType, + "oe_return"), + emit_encoding_stmt(G, N, X, Fd, RetType, "oe_return"), + + foreach(fun({T1, _A1, N1}) -> + case T1 of + {'void', _} -> + ok; + _ -> + emit_encoding_comment(G, N, Fd, "Encode", + "", T1, N1), + emit_encoding_stmt(G, N, X, Fd, T1, N1) + end + end, OutTypeAttrArgs), + emit(Fd, " return 0;\n}\n\n"); + _ -> + %% Oneway + ok + end. + +%%------------------------------------------------------------ +%% Emit message encoder call +%%------------------------------------------------------------ + +emit_message_encoder_call(G, Fd, N, X, Name, RetType, TypeAttrArgs) -> + emit(Fd, " /* Encoding reply message */\n"), + RType = mk_c_ret_type(G, N, RetType), + case ic_util:mk_list(mk_enc_par_list(G, N, X, TypeAttrArgs)) of + "" -> + case RType of + "void" -> + emit(Fd, " ~s(oe_obj, oe_env);\n", + [Name ++ "__enc"]); + "erlang_pid*" -> + emit(Fd, " ~s(oe_obj, &oe_return, oe_env);\n", + [Name ++ "__enc"]); + "erlang_port*" -> + emit(Fd, " ~s(oe_obj, &oe_return, oe_env);\n", + [Name ++ "__enc"]); + "erlang_ref*" -> + emit(Fd, " ~s(oe_obj, &oe_return, oe_env);\n", + [Name ++ "__enc"]); + _ -> + emit(Fd, " ~s(oe_obj, oe_return, oe_env);\n", + [Name ++ "__enc"]) + end; + + PLFE -> + case RType of + "void" -> + emit(Fd, " ~s(oe_obj, ~s, oe_env);\n", + [Name ++ "__enc", PLFE]); + "erlang_pid*" -> + emit(Fd, " ~s(oe_obj, &oe_return, ~s, oe_env);\n", + [Name ++ "__enc", PLFE]); + "erlang_port*" -> + emit(Fd, " ~s(oe_obj, &oe_return, ~s, oe_env);\n", + [Name ++ "__enc", PLFE]); + "erlang_ref*" -> + emit(Fd, " ~s(oe_obj, &oe_return, ~s, oe_env);\n", + [Name ++ "__enc", PLFE]); + _ -> + emit(Fd, " ~s(oe_obj, oe_return, ~s, oe_env);\n", + [Name ++ "__enc", PLFE]) + end + end, + ic_codegen:nl(Fd). + +%%------------------------------------------------------------ +%% Emit parameter decoding call +%%------------------------------------------------------------ + +emit_parameter_decoder_call(G, Fd, N, X, Name, _R, TypeAttrArgs) -> + case ic_util:mk_list(mk_dec_par_list(G, N, X, TypeAttrArgs)) of + "" -> %% No parameters ! skip it ! + ok; + PLFDC -> + ParDecName = Name ++ "__dec", + emit(Fd, + " /* Decode parameters */\n" + " if((oe_error_code = ~s(oe_obj, ~s, oe_env)) < 0) {\n", + [ParDecName, PLFDC]), + emit_c_dec_rpt(Fd, " ", "parmeters", []), + emit(Fd, + " if(oe_env->_major == CORBA_NO_EXCEPTION)\n" + " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad parameter on decode\");\n" + " return oe_error_code;\n }\n\n") + end. + +%%------------------------------------------------------------ +%% Emit call-back +%%------------------------------------------------------------ + +emit_callback(G, Fd, N, X, Name, RetType, TypeAttrArgs) -> + CallBackName = Name ++ "__cb", + emit(Fd, " /* Callback function call */\n"), + PL = ic_util:mk_list(mk_cb_par_list(G, N, X, TypeAttrArgs)), + case ic_forms:is_oneway(X) of + true -> + case PL of + "" -> + emit(Fd, " oe_restore = ~s(oe_obj, oe_env);\n\n", + [CallBackName]); + _ -> + emit(Fd, " oe_restore = ~s(oe_obj, ~s, oe_env);\n\n", + [CallBackName, PL]) + end; + false -> + CBPL = case PL of + "" -> + ""; + _PL -> + ", " ++ PL + end, + case mk_c_ret_type(G, N, RetType) of + "void" -> + case PL of + "" -> + emit(Fd, " oe_restore = ~s(oe_obj, oe_env);" + "\n\n", [CallBackName]); + _ -> + emit(Fd, " oe_restore = ~s(oe_obj, ~s, oe_env);" + "\n\n", [CallBackName, PL]) + end; + _ -> + case ictype:isArray(G, N, RetType) of + true -> + emit(Fd, + " oe_restore = ~s(oe_obj, oe_return~s, " + " oe_env);\n\n", [CallBackName, CBPL]); + false -> + emit(Fd, " oe_restore = ~s(oe_obj, " + "&oe_return~s, oe_env);\n\n", + [CallBackName, CBPL]) + end + end + end. + +%%------------------------------------------------------------ +%% Emit restore +%%------------------------------------------------------------ + +emit_restore(G, Fd, N, X, _Name, RetType, TypeAttrArgs) -> + emit(Fd, " /* Restore function call */\n"), + emit(Fd, " if (oe_restore != NULL)\n"), + PL = ic_util:mk_list(mk_cb_par_list(G, N, X, TypeAttrArgs)), + case ic_forms:is_oneway(X) of + true -> + case PL of + "" -> + emit(Fd, " (*oe_restore)(oe_obj, oe_env);\n\n"); + _ -> + emit(Fd, " (*oe_restore)(oe_obj, ~s, oe_env);\n\n", + [PL]) + end; + false -> + RPL = case PL of + "" -> + ""; + _PL -> + ", " ++ PL + end, + case mk_c_ret_type(G, N, RetType) of + "void" -> + case PL of + "" -> + emit(Fd, " (*oe_restore)(oe_obj, oe_env);" + "\n\n"); + _ -> + emit(Fd, " (*oe_restore)(oe_obj, ~s, oe_env);" + "\n\n", [PL]) + end; + _ -> + case ictype:isArray(G, N, RetType) of + true -> + emit(Fd, + " (*oe_restore)(oe_obj, oe_return~s, " + " oe_env);\n\n", [RPL]); + false -> + emit(Fd, " (*oe_restore)(oe_obj, " + "&oe_return~s, oe_env);\n\n", [RPL]) + end + end + end. + +%%------------------------------------------------------------ +%% Emit variable defs +%%------------------------------------------------------------ + +emit_variable_defs(G, Fd, N, X, _Name, RetType, TypeAttrArgs) -> + {ScopedName, _, _} = ic_cbe:extract_info(G, N, X), + emit(Fd, " ~s__rs* oe_restore = NULL;\n", [ScopedName]), + RestVars = mk_var_list(mk_var_decl_list(G, N, X, TypeAttrArgs)), + case ic_forms:is_oneway(X) of + true -> + emit(Fd, "~s\n\n", [RestVars]); + false -> + RType = mk_c_ret_type(G, N, RetType), + case RType of + "void" -> + emit(Fd, "~s\n\n", [RestVars]); + "CORBA_unsigned_long" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_unsigned_long_long" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_unsigned_short" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_short" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_long" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_long_long" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_float" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_double" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_char" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_wchar" -> %% WCHAR + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_boolean" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + "CORBA_octet" -> + emit(Fd, "~s ~s oe_return = 0;\n\n", [RestVars, RType]); + _ -> + case ic_cbe:is_variable_size(G, N, RetType) of + true -> + emit(Fd, "~s ~s oe_return;\n\n", + [RestVars, RType]); + false -> + TK = ic_forms:get_tk(X), + case TK of + {tk_enum, _, _, _List} -> + emit(Fd, "~s ~s oe_return;\n\n", + [RestVars, RType]); + _ -> + case RType of + "erlang_binary*" -> + emit(Fd, "~s erlang_binary " + "oe_return;\n\n", [RestVars]); + "erlang_pid*" -> + emit(Fd, "~s erlang_pid " + "oe_return;\n\n", [RestVars]); + "erlang_port*" -> + emit(Fd, "~s erlang_port " + "oe_return;\n\n", [RestVars]); + "erlang_ref*" -> + emit(Fd, "~s erlang_ref " + "oe_return;\n\n", [RestVars]); + _ -> + %% Structures are + %% initiated by memset + emit(Fd, "~s ~s " + "oe_return;\n\n", + [RestVars, RType]) + end, + emit(Fd, " memset(&oe_return, 0, " + "sizeof(oe_return));\n\n") + end + end + end + end. + +%%------------------------------------------------------------ +%% Make variable list +%%------------------------------------------------------------ + +%% XXX Modify +mk_var_list([]) -> + ""; +mk_var_list([Arg| Args]) -> + " " ++ Arg ++ ";\n" ++ mk_var_list(Args). + +%%------------------------------------------------------------ +%% Make return type +%%------------------------------------------------------------ + +mk_c_ret_type(G, N, Type) -> + Ctype = 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) -> %% WSTRING + "*"; + Ctype == "CORBA_wchar *" -> %% WSTRING + ""; + true -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + "*" + end + end; + false -> + if + Ctype == "erlang_pid" -> + "*"; + Ctype == "erlang_port" -> + "*"; + Ctype == "erlang_ref" -> + "*"; + true -> + "" + end + end, + Ctype ++ Dyn. + +%%------------------------------------------------------------ +%% Make call-back parameter list +%%------------------------------------------------------------ + +mk_cb_par_list(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [in, out], TypeAttrArgs0), + lists:map( + fun({Type, Attr, Arg}) -> + case ic_cbe:is_variable_size(G, N, Type) of + true -> + case Attr of + in -> + Arg; + out -> + case ictype:isArray(G, N, Type) of + true -> + Arg; + _ -> + "&" ++ Arg + end + end; + false -> + case ictype:isArray(G, N, Type) of + true -> + Arg; + _ -> + "&" ++ Arg + end + end + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make decoder parameter list +%%------------------------------------------------------------ + +mk_dec_par_list(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [in], + TypeAttrArgs0), + lists:map( + fun({Type, _Attr, Arg}) -> + Ctype = mk_c_type(G, N, Type), + case ic_cbe:is_variable_size(G, N, Type) of + true -> + if + is_record(Type, string) -> + "&" ++ Arg; + Ctype == "CORBA_char *" -> + Arg; + is_record(Type, wstring) -> + "&" ++ Arg; + Ctype == "CORBA_wchar *" -> + Arg; + true -> + "&" ++ Arg + end; + false -> + case ictype:isArray(G, N, Type) of + true -> + Arg; + _ -> + "&" ++ Arg + end + end + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make encoder parameter list +%%------------------------------------------------------------ + +mk_enc_par_list(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [out], + TypeAttrArgs0), + lists:map( + fun({Type, _Attr, Arg}) -> + Ctype = mk_c_type(G, N, Type), + case Ctype of + "erlang_pid" -> + "&" ++ Arg; + "erlang_port" -> + "&" ++ Arg; + "erlang_ref" -> + "&" ++ Arg; + _ -> + Arg + end + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make type argument list +%%------------------------------------------------------------ + +mk_type_attr_arg_list(Types, Args) -> + filterzip( + fun(Type, {Attr, Arg}) -> + {true, {Type, Attr, Arg}} + end, Types, Args). + +%%------------------------------------------------------------ +%% Filter type argument list +%%------------------------------------------------------------ + +filter_type_attr_arg_list(G, X, InOrOut, TypeAttrArgs) -> + lists:filter( + + fun({_Type, inout, Arg}) -> + ic_error:error(G, {inout_spec_for_c, X, Arg}), + false; + ({_Type, Attr, _Arg}) -> + lists:member(Attr, InOrOut) + end, TypeAttrArgs). + +%%------------------------------------------------------------ +%% Make indirection operator +%%------------------------------------------------------------ + +mk_ind_op(in) -> + ""; +mk_ind_op(inout) -> + error; +mk_ind_op(_) -> + "*". + +%%------------------------------------------------------------ +%% Make parameter list for decoder +%%------------------------------------------------------------ + +mk_par_list_for_decoder(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [in], TypeAttrArgs0), + lists:map( + fun({Type, Attr, Arg}) -> + Ctype = 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) -> %% WSTRING + "**"; + Ctype == "CORBA_wchar *" -> %% WSTRING + ""; + true -> + case ictype:isArray(G, N, Type) of + true -> + slice(Attr) ++ "*"; + _ -> + "**" + end + end; + false -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + "*" + end + end, + Ctype ++ Dyn ++ " " ++ Arg + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make parameter list for encoder +%%------------------------------------------------------------ + +mk_par_list_for_encoder(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [out], TypeAttrArgs0), + lists:map( + fun({Type, _Attr, Arg}) -> + Ctype = 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) -> %% WSTRING + "*"; + Ctype == "CORBA_wchar *" -> %% WSTRING + ""; + true -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + "*" + end + end; + false -> + if + Ctype == "erlang_pid" -> + "*"; + Ctype == "erlang_port" -> + "*"; + Ctype == "erlang_ref" -> + "*"; + true -> + "" + end + end, + Ctype ++ " " ++ Dyn ++ Arg + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make parameter list for decoder prototypes +%%------------------------------------------------------------ + +mk_par_list_for_decoder_prototypes(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [in], TypeAttrArgs0), + lists:map( + fun({Type, Attr, _Arg}) -> + Ctype = 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) -> %% WSTRING + "**"; + Ctype == "CORBA_wchar *" -> %% WSTRING + ""; + true -> + case ictype:isArray(G, N, Type) of + true -> + slice(Attr) ++ "*"; + _ -> + "**" + end + end; + false -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + "*" + end + end, + Ctype ++ Dyn + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make parameter list for encoder prototypes +%%------------------------------------------------------------ + +mk_par_list_for_encoder_prototypes(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [out], TypeAttrArgs0), + lists:map( + fun({Type, _Attr, _Arg}) -> + Ctype = 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) -> %% WSTRING + "*"; + Ctype == "CORBA_wchar *" -> %% WSTRING + ""; + true -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + "*" + end + end; + false -> + if + Ctype == "erlang_pid" -> + "*"; + Ctype == "erlang_port" -> + "*"; + Ctype == "erlang_ref" -> + "*"; + true -> + "" + end + end, + Ctype ++ Dyn + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make parameter list for call-back prototypes +%%------------------------------------------------------------ + +mk_par_list_for_callback_prototypes(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [in, out], + TypeAttrArgs0), + lists:map( + fun({Type, Attr, _Arg}) -> + IndOp = mk_ind_op(Attr), + Ctype = mk_c_type(G, N, Type), + Dyn = case ic_cbe:is_variable_size(G, N, Type) of + true -> + if + is_record(Type, string) -> + "*" ++ IndOp; + Ctype == "CORBA_char *" -> + "" ++ IndOp; + is_record(Type, wstring) -> %% WSTRING + "*" ++ IndOp; + Ctype == "CORBA_wchar *" -> %% WSTRING + "" ++ IndOp; + true -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + "*" ++ IndOp + end + end; + false -> + case ictype:isArray(G, N, Type) of + true -> + ""; + _ -> + case Attr of %% Should just be IndOp + in -> + "*" ++ IndOp; + out -> + IndOp + end + end + end, + Ctype ++ Dyn + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Make variable declaration list +%%------------------------------------------------------------ + +mk_var_decl_list(G, N, X, TypeAttrArgs0) -> + TypeAttrArgs1 = filter_type_attr_arg_list(G, X, [in, out], + TypeAttrArgs0), + lists:map( + fun({Type, Attr, Arg}) -> + Ctype = mk_c_type(G, N, Type), + VarDecl = case ic_cbe:is_variable_size(G, N, Type) of + true -> + if + is_record(Type, string) -> + Ctype ++ "* " ++ Arg ++ " = NULL"; + Ctype == "CORBA_char *" -> + Ctype ++ " " ++ Arg ++ " = NULL"; + is_record(Type, wstring) -> %% WSTRING + Ctype ++ "* " ++ Arg ++ " = NULL"; + Ctype == "CORBA_wchar *" -> %% WSTRING + Ctype ++ " " ++ Arg ++ " = NULL"; + true -> + case ictype:isArray(G, N, Type) of + true -> + Ctype ++ slice(Attr) ++ " " ++ + Arg; + _ -> + Ctype ++ "* " ++ Arg + end + end; + false -> + Ctype ++ " " ++ Arg + end, + + VarDecl + end, TypeAttrArgs1). + +%%------------------------------------------------------------ +%% Slice +%%------------------------------------------------------------ + +slice(in) -> + "_slice*"; +slice(_) -> + "". + +%%------------------------------------------------------------ +%% Special comment functions +%%------------------------------------------------------------ + +emit_encoding_comment(G, N, F, String, RefOrVal, Type, Name) -> + emit(F, [io_lib:format(" /* ~s parameter: ~s~s ~s */\n", + [String, mk_c_type(G, N, Type), + RefOrVal, Name])]). + + +%%------------------------------------------------------------ +%% Make C type +%%------------------------------------------------------------ + +%% +%% Warning this is NOT identical to mk_c_type in ic_cbe.erl +%% +mk_c_type(G, N, S) -> + mk_c_type(G, N, S, evaluate). + +mk_c_type(G, N, S, evaluate) when element(1, S) == scoped_id -> + {FullScopedName, _T, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + BT = ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)), + case BT of + "erlang_binary" -> + "erlang_binary"; + "erlang_pid" -> + "erlang_pid"; + "erlang_port" -> + "erlang_port"; + "erlang_ref" -> + "erlang_ref"; + "erlang_term" -> + "ETERM*"; + {enum, Type} -> + mk_c_type(G, N, Type, evaluate); + Type -> + mk_c_type(G, N, Type, evaluate) + end; +mk_c_type(G, N, S, evaluate_not) when element(1, S) == scoped_id -> + {FullScopedName, _T, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + BT = ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)), + case BT of + "erlang_binary" -> + "erlang_binary"; + "erlang_pid" -> + "erlang_pid"; + "erlang_port" -> + "erlang_port"; + "erlang_ref" -> + "erlang_ref"; + "erlang_term" -> + "ETERM*"; + Type -> + Type + end; +mk_c_type(_G, _N, S, _) when is_list(S) -> + S; +mk_c_type(_G, _N, S, _) when is_record(S, string) -> + "CORBA_char"; +mk_c_type(_G, _N, S, _) when is_record(S, wstring) -> %% WSTRING + "CORBA_wchar"; +mk_c_type(_G, _N, {boolean, _}, _) -> + "CORBA_boolean"; +mk_c_type(_G, _N, {octet, _}, _) -> + "CORBA_octet"; +mk_c_type(_G, _N, {void, _}, _) -> + "void"; +mk_c_type(_G, _N, {unsigned, U}, _) -> + case U of + {short, _} -> + "CORBA_unsigned_short"; + {long, _} -> + "CORBA_unsigned_long"; + {'long long', _} -> + "CORBA_unsigned_long_long" + end; +mk_c_type(_G, _N, {'long long', _}, _) -> + "CORBA_long_long"; +mk_c_type(_G, _N, {'any', _}, _) -> %% Fix for any type + "CORBA_long"; +mk_c_type(_G, _N, {T, _}, _) -> + "CORBA_" ++ atom_to_list(T). + +%%------------------------------------------------------------ +%% Emit encoding statement +%%------------------------------------------------------------ + +%% emit_encoding_stmt(G, N, X, Fd, T, LName) +%% +%% +emit_encoding_stmt(G, N, X, Fd, T, LName) when element(1, T) == scoped_id -> + case mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_pid(oe_env, ~s)) < 0) {\n", + [LName]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_pid", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_port(oe_env, ~s)) < 0) {\n", + [LName]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_port", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_ref" -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ref(oe_env, ~s)) < 0) {\n", + [LName]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_ref", []), + emit(Fd, " return oe_error_code;\n }\n"); + "ETERM*" -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_term(oe_env, ~s)) < 0) {\n", + [LName]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_term", []), + emit(Fd, " return oe_error_code;\n }\n"); + {enum, FSN} -> + emit_encoding_stmt(G, N, X, Fd, FSN, LName); + FSN -> + emit_encoding_stmt(G, N, X, Fd, FSN, LName) + end; +emit_encoding_stmt(G, N, X, Fd, T, LName) when is_list(T) -> + %% Already a fullscoped name + case get_param_tk(LName, X) of + error -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), T, LName]); + ParamTK -> + case ic_cbe:is_variable_size(ParamTK) of + true -> + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, ~s)) < 0)" + " {\n", + [ic_util:mk_oe_name(G, "encode_"), T, LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");" + "\n"), + ?emit_c_enc_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n }\n\n"); + false -> + if is_atom(ParamTK) -> + case ParamTK of + tk_ushort -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ulong(oe_env, " + "(unsigned long) ~s)) < 0) {\n", + [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "ushort", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_ulong -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ulong(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "ulong", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_ulonglong -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ulonglong(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "ulonglong", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_short -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_long(oe_env, " + "(long) ~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "short", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_long -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_long(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "long", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_longlong -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_longlong(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "longlong", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_float -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_double(oe_env, " + "(double) ~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "float", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_double -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_double(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "double", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_boolean -> + emit(Fd, " switch(~s) {\n", [LName]), + emit(Fd, " case 0 :\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, " + "\"false\")) < 0) {\n"), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean", []), + emit(Fd, " return " + "oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " case 1 :\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, " + "\"true\")) < 0) {\n"), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean", []), + emit(Fd, " return " + "oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " default :\n"), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean", []), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n\n"); + tk_char -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_char(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "char", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_wchar -> %% WCHAR + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_wchar(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "wchar", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_octet -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_char(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "octet", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + tk_any -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_long(oe_env, " + "~s)) < 0) {\n", [LName]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "any", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"); + _ -> + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + ?emit_c_enc_rpt(Fd, " ", "tk_unknown", []), + emit(Fd, " return " + "oe_error_code;\n }\n\n"), + ok + end; + true -> + case element(1, ParamTK) of + tk_enum -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "enum", []); + tk_array -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "array", []); + _ -> + emit(Fd, " if ((oe_error_code = " + "~s~s(oe_env, &~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "encode_"), + T, LName]), + ?emit_c_enc_rpt(Fd, " ", "", []) + end, + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation " + "parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n") + end + end + end; +emit_encoding_stmt(G, N, _X, Fd, T, LName) when is_record(T, string) -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_string(oe_env, (const char*) ~s)) < 0) {\n", + [LName]), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Cannot encode string\");\n"), + ?emit_c_enc_rpt(Fd, " ", "string", []), + emit(Fd, " return oe_error_code;\n }\n\n"); +emit_encoding_stmt(G, N, _X, Fd, T, LName) when is_record(T, wstring) -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_wstring(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "wstring", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Cannot encode string\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); +emit_encoding_stmt(G, N, _X, Fd, T, LName) -> + case T of + {unsigned, {short, _}} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ulong(oe_env, (unsigned long) ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "ushort", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {unsigned, {long, _}} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ulong(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "ulong", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {unsigned, {'long long', _}} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_ulonglong(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "ulonglong", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {short, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_long(oe_env, (long) ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "short", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {long, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_long(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "long", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {'long long', _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_longlong(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "longlong", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {float, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_double(oe_env, (double) ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "float", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {double, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_double(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "double", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {boolean, _} -> + emit(Fd, " switch(~s) {\n", [LName]), + emit(Fd, " case 0 :\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, \"false\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " case 1 :\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, \"true\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " default :\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n\n"); + {char, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_char(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "char", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {wchar, _} -> %% WCHAR + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_wchar(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "wchar", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {octet, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_char(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "octet", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {void, _} -> + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, \"void\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "void", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {sequence, _, _} -> + ?emit_c_enc_rpt(Fd, " ", "sequence", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + {any, _} -> %% Fix for any type + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_long(oe_env, ~s)) < 0) {\n", + [LName]), + ?emit_c_enc_rpt(Fd, " ", "any", []), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "BAD_PARAM, \"Bad operation parameter on encode\");\n"), + emit(Fd, " return oe_error_code;\n }\n\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end. + +%%------------------------------------------------------------ +%% Get type kind parameter +%%------------------------------------------------------------ + +%% Useful functions +get_param_tk("oe_return", Op) -> + ic_forms:get_tk(Op); +get_param_tk(Name, Op) -> + case get_param(Name, Op) of + error -> + error; + Param -> + ic_forms:get_tk(Param) + end. + +%%------------------------------------------------------------ +%% Get parameter (for what? XXX) +%%------------------------------------------------------------ + +get_param(Name, Op) when is_record(Op, op) -> + get_param_loop(Name, Op#op.params); +get_param(_Name, _Op) -> + error. + +get_param_loop(_Name, []) -> + error; +get_param_loop(Name, [Param| Params]) -> + case ic_forms:get_id2(Param) of + Name -> + Param; + _ -> + get_param_loop(Name, Params) + end. + +%%------------------------------------------------------------ +%% Emit decoding statement +%%------------------------------------------------------------ + +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, Align, NextPos, + DecType, AllocedPars) when element(1, T) == scoped_id -> + case mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + emit(Fd, " if ((oe_error_code = ei_decode_pid(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n\n"); + "erlang_port" -> + emit(Fd, " if ((oe_error_code = ei_decode_port(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n\n"); + "erlang_ref" -> + emit(Fd, " if ((oe_error_code = ei_decode_ref(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n\n"); + "ETERM*" -> + emit(Fd, " if ((oe_error_code = ei_decode_term(~s, " + "&oe_env->_iin, (void**)~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n\n"); + {enum, FSN} -> + emit_decoding_stmt(G, N, Fd, FSN, LName, IndOp, + InBuffer, Align, NextPos, DecType, AllocedPars); + FSN -> + emit_decoding_stmt(G, N, Fd, FSN, LName, IndOp, + InBuffer, Align, NextPos, DecType, AllocedPars) + end; +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, NextPos, + DecType, AllocedPars) when is_list(T) -> + %% Already a fullscoped name + Type = ictype:name2type(G, T), + case ictype:isBasicType(Type) of + true -> + emit_decoding_stmt_for_basic_type(Fd, Type, InBuffer, IndOp, + LName, AllocedPars); + false -> + emit(Fd, " {\n"), + case DecType of + caller -> %% No malloc used, define oe_first anyhow. + emit(Fd, " void *oe_first = NULL;\n"), + emit(Fd, " int oe_outindex = 0;\n\n"); + array_dyn -> %% Malloc used + emit(Fd, " int oe_outindex = 0;\n\n"); + %% [ic_util:mk_align(io_lib:format("sizeof(~s)", [T]))]); + caller_dyn -> %% Malloc used + emit(Fd, " int oe_outindex = 0;\n\n") + end, + emit(Fd, " if ((oe_error_code = ~s~s(oe_env, oe_first, " + "~s, ~s)) < 0) {\n", + [ic_util:mk_oe_name(G, "decode_"), + T, NextPos, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n") + end; +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, _NextPos, + _DecType, AllocedPars) when is_record(T, string) -> + emit(Fd, " if ((oe_error_code = ei_decode_string(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"); +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, _NextPos, + _DecType, AllocedPars) when is_record(T, wstring) -> + %% WSTRING + emit(Fd, " if ((oe_error_code = " + "oe_ei_decode_wstring(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n\n"), + emit(Fd, " }\n"); +emit_decoding_stmt(G, N, Fd, T, LName, IndOp, InBuffer, _Align, _NextPos, + _DecType, AllocedPars) -> + case ic_cbe:normalize_type(T) of + {basic, Type} -> + emit_decoding_stmt_for_basic_type(Fd, Type, InBuffer, IndOp, + LName, AllocedPars); + _ -> + case T of + {void, _} -> + emit(Fd, + " if ((oe_error_code = ei_decode_atom(~s, " + "&oe_env->_iin, 0)) < 0) {\n", + [InBuffer]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"); + {sequence, _, _} -> + %% XXX XXX Why? + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n\n"); + {any, _} -> %% Fix for any type + emit(Fd, + " if ((oe_error_code = ei_decode_long(~s, " + "&oe_env->_iin, ~s~s)) < 0) {\n", + [InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + ?emit_c_dec_rpt(Fd, " ", "", []), + emit(Fd, " return oe_error_code;\n\n"), + emit(Fd, " }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end + end. + +emit_decoding_stmt_for_basic_type(Fd, Type, InBuffer, IndOp, + LName, AllocedPars) -> + Fmt = + " if ((oe_error_code = ~sei_decode_~s(~s, &oe_env->_iin, " + "~s~s)) < 0) {\n", + Ret = + " return oe_error_code;\n" + "}\n", + + {Pre, DecType} = + case Type of + ushort -> {"", "ulong"}; + ulong -> {"", "ulong"}; + ulonglong -> {"oe_", "ulonglong"}; + short -> {"", "long"}; + long -> {"", "long"}; + longlong -> {"oe_", "longlong"}; + float -> {"", "double"}; + double -> {"", "double"}; + boolean -> {"", "atom"}; + char -> {"", "char"}; + wchar -> {"oe_", "wchar"}; + octet -> {"", "char"}; + any -> {"", "long"} + end, + case Type of + ushort -> + emit(Fd, " {\n"), + emit(Fd, " unsigned long oe_ulong;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_ulong(~s, " + "&oe_env->_iin, &oe_ulong)) < 0) {\n", [InBuffer]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit_c_dec_rpt(Fd, " ", "ushort", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " *~s = (unsigned short) oe_ulong;\n", [LName]), + emit(Fd, " }\n\n"); + short -> + emit(Fd, " {\n"), + emit(Fd, " long oe_long;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_long(~s, " + "&oe_env->_iin, &oe_long)) < 0) {\n", [InBuffer]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit_c_dec_rpt(Fd, " ", "short", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " *~s = (short) oe_long;\n", [LName]), + emit(Fd, " }\n\n"); + float -> + emit(Fd, " {\n"), + emit(Fd, " double oe_double;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_double(~s, " + "&oe_env->_iin, &oe_double)) < 0) {\n", [InBuffer]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit_c_dec_rpt(Fd, " ", "float", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " *~s = (float) oe_double;\n", [LName]), + emit(Fd, " }\n\n"); + boolean -> + emit(Fd, " {\n"), + emit(Fd, " char oe_bool[25];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(~s, " + "&oe_env->_iin, oe_bool)) < 0) {\n", [InBuffer]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit_c_dec_rpt(Fd, " ", "boolean", []), + emit(Fd, " return oe_error_code;\n"), + emit(Fd, " }\n"), + emit(Fd, " if (strcmp(oe_bool, \"false\") == 0) {\n"), + emit(Fd, " *(~s) = 0;\n", [LName]), + emit(Fd, " }\n"), + emit(Fd, " else if (strcmp(oe_bool, \"true\") == 0) {\n"), + emit(Fd, " *(~s) = 1;\n", [LName]), + emit(Fd, " } else {\n"), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit_c_dec_rpt(Fd, " ", "boolean", []), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n"), + emit(Fd, " }\n\n"); + _ -> + emit(Fd, Fmt, [Pre, DecType, InBuffer, IndOp, LName]), + ic_cbe:emit_dealloc_stmts(Fd, " ", AllocedPars), + emit(Fd, Ret) + end. + + +%%------------------------------------------------------------ +%% 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(_, [], []) -> + []. + + diff --git a/lib/ic/src/ic_debug.hrl b/lib/ic/src/ic_debug.hrl new file mode 100644 index 0000000000..c0490b4c13 --- /dev/null +++ b/lib/ic/src/ic_debug.hrl @@ -0,0 +1,37 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +%%---------------------------------------------------------------------- +%% Debug macro +%%---------------------------------------------------------------------- +-ifndef(ic_debug_hrl). +-define(ic_debug_hrl, true). + +-ifdef(debug). + -define(PRINTDEBUG(Msg), + io:format("~p :~p ~p~n", [Msg, ?FILE, ?LINE])). + -define(PRINTDEBUG2(F, A), + io:format(F ++ ":~p ~p~n", A ++ [?FILE, ?LINE])). +-else. + -define(PRINTDEBUG(Msg), ok). + -define(PRINTDEBUG2(F, A), ok). +-endif. + +-endif. diff --git a/lib/ic/src/ic_enum_java.erl b/lib/ic/src/ic_enum_java.erl new file mode 100644 index 0000000000..5978c3468e --- /dev/null +++ b/lib/ic/src/ic_enum_java.erl @@ -0,0 +1,312 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_enum_java). + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([gen/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: gen/3 +%%----------------------------------------------------------------- +gen(G, N, X) when is_record(X, enum) -> + %%?PRINTDEBUG2("enum: ~p", [X]), + EnumName = ic_forms:get_java_id(X), + N2 = ["_" ++ EnumName |N], + ic_jbe:gen(G, N2, ic_forms:get_body(X)), + + emit_enum_class(G, N, X, EnumName), + emit_holder_class(G, N, X, EnumName), + emit_helper_class(G, N, X, EnumName); +gen(_G, _N, _X) -> + ok. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: emit_enum_class/4 +%%----------------------------------------------------------------- +emit_enum_class(G, N, X, EnumName) -> + {Fd, _} = ic_file:open_java_file(G, N, EnumName), + + EList = enum_member_name_list(G, N, X), + %%?PRINTDEBUG2("EList: ~p", [EList]), + ic_codegen:emit(Fd, ["final public class ",EnumName," {\n\n" + + " // instance variables\n"]), + + emit_enum_member_int_values_initialization(G, N, X, Fd, EList), + emit_enum_public_instance_variables(G, N, X, Fd, EnumName, EList), + + ic_codegen:emit(Fd, [" private int _value;\n\n" + + " // constructors\n" + " private ",EnumName,"(int __value) {\n" + " _value = __value;\n" + " }\n\n" + + " // methods\n" + " public int value() {\n" + " return _value;\n" + " }\n"]), + + emit_enum_from_int_function(G, N, X, Fd, EnumName, EList), + + ic_codegen:emit(Fd, "\n}\n"), + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_holder_class/4 +%%----------------------------------------------------------------- +emit_holder_class(G, N, _X, EnumName) -> + EName = string:concat(EnumName, "Holder"), + {Fd, _} = ic_file:open_java_file(G, N, EName), + + ic_codegen:emit(Fd, ["final public class ",EnumName,"Holder {\n\n" + + " // instance variables\n" + " public ",EnumName," value;\n\n" + + " // constructors\n" + " public ",EnumName,"Holder() {}\n\n" + + " public ",EnumName,"Holder(",EnumName," initial) {\n" + " value = initial;\n" + " }\n\n" + + " // methods\n" + " public void _marshal(",?ERLANGPACKAGE,"OtpOutputStream out) throws java.lang.Exception {\n" + " ",EnumName,"Helper.marshal(out, value);\n" + " }\n\n" + + " public void _unmarshal(",?ERLANGPACKAGE,"OtpInputStream in) throws java.lang.Exception {\n" + " value = ",EnumName,"Helper.unmarshal(in);\n" + " }\n\n" + "}\n"]), + file:close(Fd). + + +%%----------------------------------------------------------------- +%% Func: emit_helper_class/4 +%%----------------------------------------------------------------- +emit_helper_class(G, N, X, EnumName) -> + EName = string:concat(EnumName, "Helper"), + WEList = enum_member_atom_list(G, N, X), + {Fd, _} = ic_file:open_java_file(G, N, EName), + + ic_codegen:emit(Fd, ["public class ",EnumName,"Helper {\n\n" + + " // constructors\n" + " private ",EnumName,"Helper() {}\n\n" + + " // methods\n" + + " public static void marshal(",?ERLANGPACKAGE,"OtpOutputStream _out, ",EnumName," _value)\n" + " throws java.lang.Exception {\n\n"]), + + emit_enum_write_function(G, N, X, Fd, EnumName), + + ic_codegen:emit(Fd, [" }\n\n" + + " public static ",EnumName," unmarshal(",?ERLANGPACKAGE,"OtpInputStream _in)\n" + " throws java.lang.Exception {\n\n"]), + + emit_enum_read_function(G, N, X, Fd, EnumName), + + ic_codegen:emit(Fd, "\n }\n\n"), + + emit_enum_private_member_variables(Fd, WEList), + + ic_codegen:emit(Fd, ["\n // Get integer value of enum from string\n" + " private static int _getIntFromName(String name) throws java.lang.Exception {\n" + " for(int i = 0; i < _memberCount; i++) {\n" + " if (name.equals(_members[i]))\n" + " return i;\n" + " }\n" + " throw new java.lang.Exception(\"\");\n" + " }\n\n" + + " public static String id() {\n" + " return \"",ictk:get_IR_ID(G, N, X),"\";\n" + " }\n\n" + + " public static String name() {\n" + " return \"",EnumName,"\";\n" + " }\n\n"]), + + ic_jbe:emit_type_function(G, N, X, Fd), + + ic_codegen:emit(Fd, [" public static void insert(",?ICPACKAGE,"Any _any, ",EnumName," _this)\n" + " throws java.lang.Exception {\n\n" + + " ",?ERLANGPACKAGE,"OtpOutputStream _os = \n" + " new ",?ERLANGPACKAGE,"OtpOutputStream();\n\n" + + " _any.type(type());\n" + " marshal(_os, _this);\n" + " _any.insert_Streamable(_os);\n" + " }\n\n" + + " public static ",EnumName," extract(",?ICPACKAGE,"Any _any)\n" + " throws java.lang.Exception {\n\n" + + " return unmarshal(_any.extract_Streamable());\n" + " }\n\n" + + "}\n"]), + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_enum_public_instance_variables/6 +%%----------------------------------------------------------------- +emit_enum_public_instance_variables(_G, _N, _X, _Fd, _EnumName, []) -> + ok; +emit_enum_public_instance_variables(G, N, X, Fd, EnumName, [Enumerator |EList]) -> + ic_codegen:emit(Fd, [" public static final ",EnumName," ",Enumerator," = new ",EnumName,"(_",Enumerator,");\n"]), + emit_enum_public_instance_variables(G, N, X, Fd, EnumName, EList). + +%%----------------------------------------------------------------- +%% Func: emit_enum_member_int_values_initialization/5 +%%----------------------------------------------------------------- +emit_enum_member_int_values_initialization(G, N, X, Fd, EList) -> + InitString = emit_enum_member_int_values_initialization_1(G, N, X, Fd, EList, 0), + ic_codegen:emit(Fd, [" public static final int ",InitString,";\n"]). + + +%%----------------------------------------------------------------- +%% Func: emit_enum_member_int_values_initialization_1/6 +%%----------------------------------------------------------------- +emit_enum_member_int_values_initialization_1(_G, _N, _X, _Fd, [Enumerator], Num) -> + " _" ++ Enumerator ++ " = " ++ ic_util:to_list(Num); +emit_enum_member_int_values_initialization_1(G, N, X, Fd, [Enumerator |EList], Num) -> + Spaces = if + Num == 0 -> + ""; + true -> + " " + end, + Spaces ++ "_" ++ Enumerator ++ " = " ++ ic_util:to_list(Num) ++ ",\n" ++ + emit_enum_member_int_values_initialization_1(G, N, X, Fd, EList, Num + 1). + +%%----------------------------------------------------------------- +%% Func: emit_enum_from_int_function/6 +%%----------------------------------------------------------------- +emit_enum_from_int_function(_G, _N, _X, Fd, EnumName, EList) -> + ic_codegen:emit(Fd, + [" public static final ",EnumName," from_int(int __value) throws java.lang.Exception {\n" + " switch (__value) {\n"]), + emit_enum_from_int_function_switchbody(Fd, EList), + ic_codegen:emit(Fd, [" }\n" + " }\n"]). + +%%----------------------------------------------------------------- +%% Func: emit_enum_from_int_function_switchbody/2 +%%----------------------------------------------------------------- +emit_enum_from_int_function_switchbody(Fd, []) -> + ic_codegen:emit(Fd, [" default:\n" + " throw new java.lang.Exception(\"\");\n"]); +emit_enum_from_int_function_switchbody(Fd, [Enumerator |EList]) -> + ic_codegen:emit(Fd, [" case _",Enumerator,":\n" + " return ",Enumerator,";\n"]), + emit_enum_from_int_function_switchbody(Fd, EList). + +%%----------------------------------------------------------------- +%% Func: emit_enum_private_member_variables/2 +%%----------------------------------------------------------------- +emit_enum_private_member_variables(Fd, EList) -> + ic_codegen:emit(Fd, [" private static final int _memberCount = ",integer_to_list(length(EList)),";\n" + " private static String[] _members = {\n"]), + emit_enum_private_member_variables_1(Fd, EList), + ic_codegen:emit(Fd, " };\n"). + +%%----------------------------------------------------------------- +%% Func: emit_enum_private_member_variables_1/2 +%%----------------------------------------------------------------- +emit_enum_private_member_variables_1(Fd, [Enumerator]) -> + ic_codegen:emit(Fd, [" \"",Enumerator,"\"\n"]); +emit_enum_private_member_variables_1(Fd, [Enumerator |EList]) -> + ic_codegen:emit(Fd, [" \"",Enumerator,"\",\n"]), + emit_enum_private_member_variables_1(Fd, EList). + +%%----------------------------------------------------------------- +%% Func: emit_enum_read_function/5 +%%----------------------------------------------------------------- +emit_enum_read_function(_G, _N, _X, Fd, EnumName) -> + ic_codegen:emit(Fd, [" return ",EnumName,".from_int(_getIntFromName(_in.read_atom()));"]). + +%%----------------------------------------------------------------- +%% Func: emit_enum_write_function/5 +%%----------------------------------------------------------------- +emit_enum_write_function(_G, _N, _X, Fd, _EnumName) -> + ic_codegen:emit(Fd, " _out.write_atom(_members[_value.value()]);\n"). + + +%%----------------------------------------------------------------- +%% Func: enum_member_name_list/3 +%% +%% Note: The names generated are checked for name coalition +%% with java keywords. If so the name is always prefixed +%% by "_" +%%----------------------------------------------------------------- +enum_member_name_list(_G, _N, X) -> + lists:map( + fun(Enumerator) -> + ic_forms:get_java_id(Enumerator) + end, + ic_forms:get_body(X)). + +%%----------------------------------------------------------------- +%% Func: enum_member_atom_list/3 +%% +%% Note : Similar to the emit_member_list/3 but does not +%% solves name coalitions with java keywords. +%% Used for wire encoding only +%%----------------------------------------------------------------- +enum_member_atom_list(_G, _N, X) -> + lists:map( + fun(Enumerator) -> + ic_forms:get_id2(Enumerator) + end, + ic_forms:get_body(X)). + + + + + + + + diff --git a/lib/ic/src/ic_erl_template.erl b/lib/ic/src/ic_erl_template.erl new file mode 100644 index 0000000000..f5983a53bd --- /dev/null +++ b/lib/ic/src/ic_erl_template.erl @@ -0,0 +1,639 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_erl_template). + + +-export([do_gen/3, emit_header/3]). + +-import(ic_codegen, [emit/2, emit/3, nl/1]). + +-include("icforms.hrl"). +-include("ic.hrl"). + +-include_lib("stdlib/include/erl_compile.hrl"). + +-define(TAB, " "). +-define(TAB2, "% "). + +-define(TEMPLATE_1_A, + "%%----------------------------------------------------------------------\n" + "%% <LICENSE>\n" + "%% \n" + "%% $Id$\n" + "%%\n" + "%%----------------------------------------------------------------------\n" + "%% Module : ~s.erl\n" + "%% \n" + "%% Source : ~s\n" + "%% \n" + "%% Description : \n" + "%% \n" + "%% Creation date: ~s\n" + "%%\n" + "%%----------------------------------------------------------------------\n" + "-module(~p).\n\n"). + +-define(TEMPLATE_1_B, + "%%----------------------------------------------------------------------\n" + "%% Internal Exports\n" + "%%----------------------------------------------------------------------\n" + "-export([init/1,\n" + " terminate/2,\n" + " code_change/3,\n" + " handle_info/2]).\n\n" + "%%----------------------------------------------------------------------\n" + "%% Include Files\n" + "%%----------------------------------------------------------------------\n" + "\n\n" + "%%----------------------------------------------------------------------\n" + "%% Macros\n" + "%%----------------------------------------------------------------------\n" + "\n\n" + "%%----------------------------------------------------------------------\n" + "%% Records\n" + "%%----------------------------------------------------------------------\n" + "-record(state, {}).\n\n" + "%%======================================================================\n" + "%% API Functions\n" + "%%======================================================================\n"). + +-define(TEMPLATE_1_C, + "%%======================================================================\n" + "%% Internal Functions\n" + "%%======================================================================\n" + "%%----------------------------------------------------------------------\n" + "%% Function : init/1\n" + "%% Arguments : Env = term()\n" + "%% Returns : {ok, State} |\n" + "%% {ok, State, Timeout} |\n" + "%% ignore |\n" + "%% {stop, Reason}\n" + "%% Raises : -\n" + "%% Description: Initiates the server\n" + "%%----------------------------------------------------------------------\n" + "init(_Env) ->\n" + "\t{ok, #state{}}.\n\n\n" + "%%----------------------------------------------------------------------\n" + "%% Function : terminate/2\n" + "%% Arguments : Reason = normal | shutdown | term()\n" + "%% State = term()\n" + "%% Returns : ok\n" + "%% Raises : -\n" + "%% Description: Invoked when the object is terminating.\n" + "%%----------------------------------------------------------------------\n" + "terminate(_Reason, _State) ->\n" + "\tok.\n\n\n" + "%%----------------------------------------------------------------------\n" + "%% Function : code_change/3\n" + "%% Arguments : OldVsn = undefined | term()\n" + "%% State = NewState = term()\n" + "%% Extra = term()\n" + "%% Returns : {ok, NewState}\n" + "%% Raises : -\n" + "%% Description: Invoked when the object should update its internal state\n" + "%% due to code replacement.\n" + "%%----------------------------------------------------------------------\n" + "code_change(_OldVsn, State, _Extra) ->\n" + "\t{ok, State}.\n\n\n" + "%%----------------------------------------------------------------------\n" + "%% Function : handle_info/2\n" + "%% Arguments : Info = normal | shutdown | term()\n" + "%% State = NewState = term()\n" + "%% Returns : {noreply, NewState} |\n" + "%% {noreply, NewState, Timeout} |\n" + "%% {stop, Reason, NewState}\n" + "%% Raises : -\n" + "%% Description: Invoked when, for example, the server traps exits.\n" + "%%----------------------------------------------------------------------\n" + "handle_info(_Info, State) ->\n" + "\t{noreply, State}.\n\n\n"). + +-define(TEMPLATE_2_A, + "%%% #0. BASIC INFORMATION\n" + "%%% ----------------------------------------------------------------------\n" + "%%% %CCaseFile : ~s.erl %\n" + "%%% Author : \n" + "%%% Description : \n" + "%%%\n" + "%%% Modules used: \n" + "%%%\n" + "%%%\n" + "%%% ----------------------------------------------------------------------\n" + "-module(~p).\n" + "-author('unknown').\n" + "-id('').\n" + "-vsn('').\n" + "-date('~s').\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% Template Id: <ID>\n" + "%%%\n" + "%%% #Copyright (C) 2004\n" + "%%% by <COMPANY>\n" + "%%% <ADDRESS>\n" + "%%% <OTHER INFORMATION>\n" + "%%% \n" + "%%% <LICENSE>\n" + "%%% \n" + "%%% \n" + "%%% ----------------------------------------------------------------------\n" + "%%% #1. REVISION LOG\n" + "%%% ----------------------------------------------------------------------\n" + "%%% Rev Date Name What\n" + "%%% ----- ------- -------- --------------------------\n" + "%%% \n" + "%%% ----------------------------------------------------------------------\n" + "%%%\n" + "%%% \n" + "%%% #2. EXPORT LISTS\n" + "%%% ----------------------------------------------------------------------\n" + "%%% #2.1 EXPORTED INTERFACE FUNCTIONS\n" + "%%% ----------------------------------------------------------------------\n"). + +-define(TEMPLATE_2_B, + "%%% ----------------------------------------------------------------------\n" + "%%% #2.2 EXPORTED INTERNAL FUNCTIONS\n" + "%%% ----------------------------------------------------------------------\n" + "-export([init/1,\n" + " terminate/2,\n" + " code_change/3,\n" + " handle_info/2]).\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% #2.3 INCLUDE FILES\n" + "%%% ----------------------------------------------------------------------\n" + "\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% #2.4 MACROS\n" + "%%% ----------------------------------------------------------------------\n" + "\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% #2.5 RECORDS\n" + "%%% ----------------------------------------------------------------------\n" + "-record(state, {}).\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% #3. CODE\n" + "%%% #---------------------------------------------------------------------\n" + "%%% #3.1 CODE FOR EXPORTED INTERFACE FUNCTIONS\n" + "%%% #---------------------------------------------------------------------\n"). + +-define(TEMPLATE_2_C, + "%%% ----------------------------------------------------------------------\n" + "%%% #3.3 CODE FOR INTERNAL FUNCTIONS\n" + "%%% ----------------------------------------------------------------------\n" + "%%% ----------------------------------------------------------------------\n" + "%%% # init/1\n" + "%%% Input : Env = term()\n" + "%%% Output : {ok, State} |\n" + "%%% {ok, State, Timeout} |\n" + "%%% ignore |\n" + "%%% {stop, Reason}\n" + "%%% Exceptions : -\n" + "%%% Description: Initiates the server\n" + "%%% ----------------------------------------------------------------------\n" + "init(_Env) ->\n" + "\t{ok, #state{}}.\n\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% # terminate/2\n" + "%%% Input : Reason = normal | shutdown | term()\n" + "%%% State = term()\n" + "%%% Output : ok\n" + "%%% Exceptions : -\n" + "%%% Description: Invoked when the object is terminating.\n" + "%%% ----------------------------------------------------------------------\n" + "terminate(_Reason, _State) ->\n" + "\tok.\n\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% # code_change/3\n" + "%%% Input : OldVsn = undefined | term()\n" + "%%% State = NewState = term()\n" + "%%% Extra = term()\n" + "%%% Output : {ok, NewState}\n" + "%%% Exceptions : -\n" + "%%% Description: Invoked when the object should update its internal state\n" + "%%% due to code replacement.\n" + "%%% ----------------------------------------------------------------------\n" + "code_change(_OldVsn, State, _Extra) ->\n" + "\t{ok, State}.\n\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% # handle_info/2\n" + "%%% Input : Info = normal | shutdown | term()\n" + "%%% State = NewState = term()\n" + "%%% Output : {noreply, NewState} |\n" + "%%% {noreply, NewState, Timeout} |\n" + "%%% {stop, Reason, NewState}\n" + "%%% Exceptions : -\n" + "%%% Description: Invoked when, for example, the server traps exits.\n" + "%%% ----------------------------------------------------------------------\n" + "handle_info(_Info, State) ->\n" + "\t{noreply, State}.\n\n\n" + "%%% ----------------------------------------------------------------------\n" + "%%% #4 CODE FOR TEMPORARY CORRECTIONS\n" + "%%% ----------------------------------------------------------------------\n\n"). + + +%%------------------------------------------------------------ +%% +%% Generate the client side Erlang stubs. +%% +%% Each module is generated to a separate file. +%% +%% Export declarations for all interface functions must be +%% generated. Each function then needs to generate a function head and +%% a body. IDL parameters must be converted into Erlang parameters +%% (variables, capitalised) and a type signature list must be +%% generated (for later encode/decode). +%% +%%------------------------------------------------------------ +do_gen(G, _File, Form) -> + gen_head(G, [], Form), + gen(G, [], Form). + + +gen(G, N, [X|Xs]) when is_record(X, preproc) -> + NewG = ic:handle_preproc(G, N, X#preproc.cat, X), + gen(NewG, N, Xs); +gen(G, N, [X|Xs]) when is_record(X, module) -> + G2 = ic_file:filename_push(G, N, X, erlang_template_no_gen), + N2 = [ic_forms:get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, ic_forms:get_body(X)), + G3 = ic_file:filename_pop(G2, erlang_template_no_gen), + gen(G3, N, Xs); +gen(G, N, [X|Xs]) when is_record(X, interface) -> + G2 = ic_file:filename_push(G, N, X, erlang_template), + N2 = [ic_forms:get_id2(X) | N], + gen_head(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), + Fd = ic_genobj:stubfiled(G2), + case get_template_version(G2) of + ?IC_FLAG_TEMPLATE_2 -> + emit(Fd, ?TEMPLATE_2_C, []); + _ -> + emit(Fd, ?TEMPLATE_1_C, []) + end, + G3 = ic_file:filename_pop(G2, erlang_template), + gen(G3, N, Xs); +gen(G, N, [X|Xs]) when is_record(X, op) -> + {Name, InArgNames, OutArgNames, Reply} = extract_info(X), + emit_function(G, N, X, ic_genobj:is_stubfile_open(G), + ic_forms:is_oneway(X), Name, InArgNames, OutArgNames, Reply), + gen(G, N, Xs); +gen(G, N, [X|Xs]) when is_record(X, attr) -> + emit_attr(G, N, X, ic_genobj:is_stubfile_open(G), fun emit_function/9), + gen(G, N, Xs); +gen(G, N, [_X|Xs]) -> + gen(G, N, Xs); +gen(_G, _N, []) -> + ok. + +%% Module Header +emit_header(G, Fd, Name) -> + Date = get_date(), + case get_template_version(G) of + ?IC_FLAG_TEMPLATE_2 -> + emit(Fd, ?TEMPLATE_2_A, [Name, list_to_atom(Name), Date]); + _ -> + IDLFile = ic_genobj:idlfile(G), + emit(Fd, ?TEMPLATE_1_A, [Name, IDLFile, Date, list_to_atom(Name)]) + end. + + +emit_attr(G, N, X, Open, F) -> + XX = #id_of{type=X}, + lists:foreach(fun(Id) -> + X2 = XX#id_of{id=Id}, + IsOneWay = ic_forms:is_oneway(X2), + {Get, Set} = mk_attr_func_names(N, ic_forms:get_id(Id)), + F(G, N, X2, Open, IsOneWay, Get, [], [], + [{ic_util:mk_var(ic_forms:get_id(Id)), + ic_forms:get_tk(X)}]), + case X#attr.readonly of + {readonly, _} -> + ok; + _ -> + F(G, N, X2, Open, IsOneWay, Set, + [{ic_util:mk_var(ic_forms:get_id(Id)), + ic_forms:get_tk(X)}], [], ["ok"]) + end + end, ic_forms:get_idlist(X)). + + +%% The automaticly generated get and set operation names for an +%% attribute. +mk_attr_func_names(_Scope, Name) -> + {"_get_" ++ Name, "_set_" ++ Name}. + + +extract_info(X) when is_record(X, op) -> + Name = ic_forms:get_id2(X), + InArgs = ic:filter_params([in,inout], X#op.params), + OutArgs = ic:filter_params([out,inout], X#op.params), + Reply = case ic_forms:get_tk(X) of + tk_void -> + ["ok"]; + Type -> + [{"OE_Reply", Type}] + end, + InArgsTypeList = + [{ic_util:mk_var(ic_forms:get_id(InArg#param.id)), + ic_forms:get_tk(InArg)} || InArg <- InArgs ], + OutArgsTypeList = + [{ic_util:mk_var(ic_forms:get_id(OutArg#param.id)), + ic_forms:get_tk(OutArg)} || OutArg <- OutArgs ], + {Name, InArgsTypeList, OutArgsTypeList, Reply}. + +get_template_version(G) -> + case ic_options:get_opt(G, flags) of + Flags when is_integer(Flags) -> + case ?IC_FLAG_TEST(Flags, ?IC_FLAG_TEMPLATE_2) of + true -> + ?IC_FLAG_TEMPLATE_2; + false -> + ?IC_FLAG_TEMPLATE_1 + end; + _ -> + ?IC_FLAG_TEMPLATE_1 + end. + + +get_date() -> + {{Y,M,D}, _} = calendar:now_to_datetime(now()), + if + M < 10, D < 10 -> + lists:concat([Y, "-0", M, "-0",D]); + M < 10 -> + lists:concat([Y, "-0", M, "-", D]); + D < 10 -> + lists:concat([Y, "-", M, "-0", D]); + true -> + lists:concat([Y, "-", M, "-", D]) + end. + + +%%------------------------------------------------------------ +%% +%% Export stuff +%% +%% Gathering of all names that should be exported from a stub +%% file. +%% + + +gen_head_special(G, N, X) when is_record(X, interface) -> + Fd = ic_genobj:stubfiled(G), + lists:foreach(fun({_Name, Body}) -> + ic_codegen:export(Fd, exp_top(G, N, Body, [])) + end, X#interface.inherit_body), + nl(Fd), + ok; +gen_head_special(_G, _N, _X) -> + ok. + + +%% Generate all export declarations +gen_head(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + Fd = ic_genobj:stubfiled(G), + ic_codegen:export(Fd, exp_top(G, N, X, [])), + gen_head_special(G, N, X), + case get_template_version(G) of + ?IC_FLAG_TEMPLATE_2 -> + emit(Fd, ?TEMPLATE_2_B, []); + _ -> + emit(Fd, ?TEMPLATE_1_B, []) + end; + false -> + ok + end. + +exp_top(_G, _N, X, Acc) when element(1, X) == preproc -> + Acc; +exp_top(G, N, L, Acc) when is_list(L) -> + exp_list(G, N, L, Acc); +exp_top(G, N, M, Acc) when is_record(M, module) -> + exp_list(G, N, ic_forms:get_body(M), Acc); +exp_top(G, N, I, Acc) when is_record(I, interface) -> + exp_list(G, N, ic_forms:get_body(I), Acc); +exp_top(G, N, X, Acc) -> + exp3(G, N, X, Acc). + +exp3(G, N, Op, Acc) when is_record(Op, op) -> + FuncName = ic_forms:get_id(Op#op.id), + Arity = length(ic:filter_params([in, inout], Op#op.params)) + 1 + + count_extras(G, N, Op), + [{FuncName, Arity} | Acc]; +exp3(G, N, A, Acc) when is_record(A, attr) -> + Extra = count_extras(G, N, A), + lists:foldr(fun(Id, Acc2) -> + {Get, Set} = mk_attr_func_names([], ic_forms:get_id(Id)), + case A#attr.readonly of + {readonly, _} -> + [{Get, 1 + Extra} | Acc2]; + _ -> + [{Get, 1 + Extra}, {Set, 2 + Extra} | Acc2] + end + end, Acc, ic_forms:get_idlist(A)); +exp3(_G, _N, _X, Acc) -> + Acc. + +exp_list(G, N, L, OrigAcc) -> + lists:foldr(fun(X, Acc) -> + exp3(G, N, X, Acc) + end, OrigAcc, L). + +count_extras(G, N, Op) -> + case {use_this(G, N, Op), use_from(G, N, Op)} of + {[], []} -> + 0; + {[], _} -> + 1; + {_, []} -> + 1; + _ -> + 2 + end. + +%%------------------------------------------------------------ +%% +%% Emit stuff +%% +%% Low level generation primitives +%% + +emit_function(_G, _N, _X, false, _, _, _, _, _) -> + ok; +emit_function(G, N, X, true, false, Name, InArgs, OutArgs, Reply) -> + Fd = ic_genobj:stubfiled(G), + This = use_this(G, N, Name), + From = use_from(G, N, Name), + State = ["State"], + Vers = get_template_version(G), + case OutArgs of + [] -> + ReplyString = create_string(Reply), + emit_function_header(G, Fd, X, N, Name, create_extra(This, From, Vers), + InArgs, length(InArgs), OutArgs, Reply, + ReplyString, Vers), + emit(Fd, "~p(~s) ->\n\t{reply, ~s, State}.\n\n", + [ic_util:to_atom(Name), create_string(This ++ From ++ State ++ InArgs), + ReplyString]); + _ -> + ReplyString = "{" ++ create_string(Reply ++ OutArgs) ++ "}", + emit_function_header(G, Fd, X, N, Name, create_extra(This, From, Vers), + InArgs, length(InArgs), OutArgs, Reply, + ReplyString, Vers), + emit(Fd, "~p(~s) ->\n\t{reply, ~s, State}.\n\n", + [ic_util:to_atom(Name), create_string(This ++ From ++ State ++ InArgs), + ReplyString]) + end; +emit_function(G, N, X, true, true, Name, InArgs, _OutArgs, _Reply) -> + Fd = ic_genobj:stubfiled(G), + This = use_this(G, N, Name), + State = ["State"], + Vers = get_template_version(G), + emit_function_header(G, Fd, X, N, Name, create_extra(This, [], Vers), + InArgs, length(InArgs), "", "", "", Vers), + emit(Fd, "~p(~s) ->\n\t{noreply, State}.\n\n", + [ic_util:to_atom(Name), create_string(This ++ State ++ InArgs)]). + +create_string([]) -> + ""; +create_string([{Name, _Type}|T]) -> + Name ++ create_string2(T); +create_string([Name|T]) -> + Name ++ create_string2(T). + +create_string2([{Name, _Type}|T]) -> + ", " ++ Name ++ create_string2(T); +create_string2([Name|T]) -> + ", " ++ Name ++ create_string2(T); +create_string2([]) -> + "". + +create_extra([], [], _Vers) -> + {"State - term()", 1}; +create_extra([], _From, ?IC_FLAG_TEMPLATE_2) -> + {"OE_From - term()\n%%% " ++ ?TAB ++ "State - term()", 2}; +create_extra([], _From, _Vers) -> + {"OE_From - term()\n%% " ++ ?TAB ++ "State - term()", 2}; +create_extra(_This, [], ?IC_FLAG_TEMPLATE_2) -> + {"OE_This - #objref{} (i.e., self())\n%%% " ++ ?TAB ++ "State - term()", 2}; +create_extra(_This, [], _Vers) -> + {"OE_This - #objref{} (i.e., self())\n%% " ++ ?TAB ++ "State - term()", 2}; +create_extra(_This, _From, ?IC_FLAG_TEMPLATE_2) -> + {"OE_This - #objref{} (i.e., self())\n%%% " ++ ?TAB ++ + "OE_From - term()\n%%% " ++ ?TAB ++ "State - term()", 3}; +create_extra(_This, _From, _Vers) -> + {"OE_This - #objref{} (i.e., self())\n%% " ++ ?TAB ++ + "OE_From - term()\n%% " ++ ?TAB ++ "State - term()", 3}. + +use_this(G, N, OpName) -> + FullOp = ic_util:to_colon([OpName|N]), + FullIntf = ic_util:to_colon(N), + case {ic_options:get_opt(G, {this, FullIntf}), + ic_options:get_opt(G, {this, FullOp}), + ic_options:get_opt(G, {this, true})} of + {_, force_false, _} -> + []; + {force_false, false, _} -> + []; + {false, false, false} -> + []; + _ -> + ["OE_This"] + end. + +use_from(G, N, OpName) -> + FullOp = ic_util:to_colon([OpName|N]), + FullIntf = ic_util:to_colon(N), + case {ic_options:get_opt(G, {from, FullIntf}), + ic_options:get_opt(G, {from, FullOp}), + ic_options:get_opt(G, {from, true})} of + {_, force_false, _} -> + []; + {force_false, false, _} -> + []; + {false, false, false} -> + []; + _ -> + ["OE_From"] + end. + + +emit_function_header(G, Fd, X, N, Name, {Extra, ExtraNo}, InP, Arity, OutP, + Reply, ReplyString, ?IC_FLAG_TEMPLATE_2) -> + emit(Fd, + "%%% ----------------------------------------------------------------------\n" + "%%% # ~p/~p\n" + "%%% Input : ~s\n", + [ic_util:to_atom(Name), (ExtraNo+Arity), Extra]), + ic_code:type_expand_all(G, N, X, Fd, ?TAB2, InP), + case Reply of + ["ok"] -> + emit(Fd, "%%% Output : ReturnValue = ~s\n", [ReplyString]); + _ -> + emit(Fd, "%%% Output : ReturnValue = ~s\n", [ReplyString]), + ic_code:type_expand_all(G, N, X, Fd, "% ", Reply) + end, + ic_code:type_expand_all(G, N, X, Fd, ?TAB2, OutP), + emit(Fd, + "%%% Exceptions : ~s\n" + "%%% Description: \n" + "%%% ----------------------------------------------------------------------\n", + [get_raises(X, ?IC_FLAG_TEMPLATE_2)]); +emit_function_header(G, Fd, X, N, Name, {Extra, ExtraNo}, InP, Arity, OutP, + Reply, ReplyString, Vers) -> + emit(Fd, + "%%----------------------------------------------------------------------\n" + "%% Function : ~p/~p\n" + "%% Arguments : ~s\n", + [ic_util:to_atom(Name), (ExtraNo+Arity), Extra]), + ic_code:type_expand_all(G, N, X, Fd, ?TAB, InP), + case Reply of + ["ok"] -> + emit(Fd, "%% Returns : ReturnValue = ~s\n", [ReplyString]); + _ -> + emit(Fd, "%% Returns : ReturnValue = ~s\n", [ReplyString]), + ic_code:type_expand_all(G, N, X, Fd, " ", Reply) + end, + ic_code:type_expand_all(G, N, X, Fd, ?TAB, OutP), + emit(Fd, + "%% Raises : ~s\n" + "%% Description: \n" + "%%----------------------------------------------------------------------\n", + [get_raises(X, Vers)]). + +get_raises(#op{raises = []}, _Vers) -> + ""; +get_raises(#op{raises = ExcList}, Vers) -> + get_raises2(ExcList, [], Vers); +get_raises(_X, _Vers) -> + []. + +get_raises2([H], Acc, _Vers) -> + lists:flatten(lists:reverse([ic_util:to_colon(H)|Acc])); +get_raises2([H|T], Acc, ?IC_FLAG_TEMPLATE_2) -> + get_raises2(T, ["\n%%% ", ic_util:to_colon(H) |Acc], + ?IC_FLAG_TEMPLATE_2); +get_raises2([H|T], Acc, _Vers) -> + get_raises2(T, ["\n%% ", ic_util:to_colon(H) |Acc], _Vers). + diff --git a/lib/ic/src/ic_erlbe.erl b/lib/ic/src/ic_erlbe.erl new file mode 100644 index 0000000000..75c87929db --- /dev/null +++ b/lib/ic/src/ic_erlbe.erl @@ -0,0 +1,1141 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_erlbe). + + +-export([do_gen/3]). +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ + +-export([unfold/1, mk_attr_func_names/2]). + + +-import(ic_util, [mk_name/2, mk_var/1, mk_oe_name/2, to_atom/1, to_list/1]). +-import(ic_forms, [get_id/1, get_id2/1, get_body/1, is_oneway/1]). +-import(ic_codegen, [emit/2, emit/3, nl/1]). +-import(ic_options, [get_opt/2]). + +-import(lists, [foreach/2, foldr/3, map/2]). + + +-include("icforms.hrl"). +-include("ic.hrl"). + +-include_lib("stdlib/include/erl_compile.hrl"). + + +%%------------------------------------------------------------ +%% +%% Generate the client side Erlang stubs. +%% +%% Each module is generated to a separate file. +%% +%% Export declarations for all interface functions must be +%% generated. Each function then needs to generate a function head and +%% a body. IDL parameters must be converted into Erlang parameters +%% (variables, capitalised) and a type signature list must be +%% generated (for later encode/decode). +%% +%%------------------------------------------------------------ +do_gen(G, File, Form) -> + GT = get_opt(G, be), + G2 = ic_file:filename_push(G, [], mk_oe_name(G, + ic_file:remove_ext(to_list(File))), + erlang), + Light = ic_options:get_opt(G, light_ifr), + R = if + GT == erl_corba, Light == false -> + case ic_genobj:is_stubfile_open(G2) of + true -> + emit(ic_genobj:stubfiled(G2), "-include_lib(\"~s/include/~s\").\n\n", + [?ORBNAME, ?IFRTYPESHRL]); + false -> ok + end, + gen_head(G2, [], Form), + ic_codegen:export(ic_genobj:stubfiled(G2), + [{ictk:register_name(G2), 0}, + {ictk:unregister_name(G2), 0}, + {oe_get_module,5}, + {oe_dependency,0}]), + R0= gen(G2, [], Form), + ictk:reg_gen(G2, [], Form), + ictk:unreg_gen(G2, [], Form), % "new" unreg_gen/3 + genDependency(G2), % creates code for dependency list + R0; + GT == erl_corba, Light == true -> + case ic_genobj:is_stubfile_open(G2) of + true -> + emit(ic_genobj:stubfiled(G2), "-include_lib(\"~s/include/~s\").\n\n", + [?ORBNAME, ?IFRTYPESHRL]); + false -> ok + end, + gen_head(G2, [], Form), + ic_codegen:export(ic_genobj:stubfiled(G2), + [{ictk:register_name(G2), 0}, + {ictk:register_name(G2), 1}, + {ictk:unregister_name(G2), 0}, + {ictk:unregister_name(G2), 1}]), + R0= gen(G2, [], Form), + ictk:reg_gen(G2, [], Form), + ictk:unreg_gen(G2, [], Form), % "new" unreg_gen/3 + R0; + true -> + gen_head(G2, [], Form), + gen(G2, [], Form) + end, + ic_file:filename_pop(G2, erlang), + R. + + +gen(G, N, [X|Xs]) when is_record(X, preproc) -> + NewG = ic:handle_preproc(G, N, X#preproc.cat, X), + gen(NewG, 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 = [get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, 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, erlang), + N2 = [get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, get_body(X)), + foreach(fun({_Name, Body}) -> gen(G2, N2, Body) end, + X#interface.inherit_body), + gen_serv(G2, N, X), + G3 = ic_file:filename_pop(G2, erlang), + gen(G3, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, const) -> +% N2 = [get_id2(X) | N], + emit_constant_func(G, X#const.id, X#const.val), + gen(G, N, Xs); %% N2 or N? + +gen(G, N, [X|Xs]) when is_record(X, op) -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + emit_stub_func(G, N, X, Name, ArgNames, TypeList, OutArgs, + is_oneway(X), get_opt(G, be)), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, attr) -> + emit_attr(G, N, X, fun emit_stub_func/9), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, except) -> + icstruct:except_gen(G, N, X, erlang), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) -> + case may_contain_structs(X) of + true -> icstruct:struct_gen(G, N, X, erlang); + false -> ok + end, + gen(G, N, Xs); + +gen(_G, _N, []) -> ok. + + +may_contain_structs(X) when is_record(X, typedef) -> true; +may_contain_structs(X) when is_record(X, struct) -> true; +may_contain_structs(X) when is_record(X, union) -> true; +may_contain_structs(_X) -> false. + + + +%%-------------------------------------------------------------------- +%% +%% Generate the server side (handle_call and handle_cast) +%% + +gen_serv(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + GT = get_opt(G, be), + gen_oe_is_a(G, N, X, GT), + N2 = [get_id2(X) | N], + gen_oe_tc(G, N2, X, GT), + + emit_serv_std(GT, G, N, X), + + gen_calls(G, N2, get_body(X)), + lists:foreach(fun({_Name, Body}) -> + gen_calls(G, N2, Body) end, + X#interface.inherit_body), + gen_end_of_call(GT, G), + + gen_casts(G, N2, get_body(X)), + lists:foreach(fun({_Name, Body}) -> + gen_casts(G, N2, Body) end, + X#interface.inherit_body), + gen_end_of_cast(GT, G), + emit_skel_footer(GT, G, N, X); % Note N instead of N2 + false -> + ok + end. + +gen_oe_is_a(G, N, X, erl_corba) when is_record(X, interface) -> + Fd = ic_genobj:stubfiled(G), + ic_codegen:mcomment(Fd, ["Inherited Interfaces"]), + emit(Fd, "oe_is_a(~p) -> true;\n", [ictk:get_IR_ID(G, N, X)]), + lists:foreach(fun(ScopedName) -> + emit(Fd, "oe_is_a(~p) -> true;\n", + [ic_pragma:scope2id(G, ScopedName)]) + end, X#interface.inherit), + emit(Fd, "oe_is_a(_) -> false.\n"), + nl(Fd), + ok; +gen_oe_is_a(_G, _N, _X, _BE) -> ok. + + +%% Generates the oe_tc function +gen_oe_tc(G, N, X, erl_corba) -> + Fd = ic_genobj:stubfiled(G), + ic_codegen:mcomment(Fd, ["Interface TypeCode"]), + LocalInterface = gen_oe_tc2(G, N, get_body(X), Fd, []), + CompleteInterface = + lists:foldl(fun({Name, Body}, FunAcc) -> + AName = ic_util:to_atom(ic_util:to_undersc(Name)), + gen_oe_tc3(G, AName, Body, Fd, FunAcc) + end, LocalInterface, X#interface.inherit_body), + emit(Fd, "oe_tc(_) -> undefined.\n"), + nl(Fd), + emit(Fd, "oe_get_interface() -> \n\t["), + emit_oe_get_interface(Fd, CompleteInterface), + nl(Fd), + ok; +gen_oe_tc(_, _, _, _) -> + ok. + +emit_oe_get_interface(Fd, []) -> + emit(Fd, "].\n"); +emit_oe_get_interface(Fd, [Item]) -> + emit(Fd, "~s].\n", [lists:flatten(Item)]); +emit_oe_get_interface(Fd, [H|T]) -> + emit(Fd, "~s,\n\t", [lists:flatten(H)]), + emit_oe_get_interface(Fd, T). + +gen_oe_tc2(_,_,[],_, Acc) -> + Acc; +gen_oe_tc2(G, N, [X|Rest], Fd, Acc) when is_record(X, op) -> + R = ic_forms:get_tk(X), + IN = lists:map(fun(P) -> ic_forms:get_tk(P) end, + ic:filter_params([in, inout], X#op.params)), + OUT = lists:map(fun(P) -> ic_forms:get_tk(P) end, + ic:filter_params([out, inout], X#op.params)), + Function = get_id2(X), + FunctionAtom = ic_util:to_atom(Function), + emit(Fd, "oe_tc(~p) -> \n\t~p;\n",[FunctionAtom, {R, IN, OUT}]), + GI = io_lib:format("{~p, oe_tc(~p)}",[Function, FunctionAtom]), + gen_oe_tc2(G, N, Rest, Fd, [GI|Acc]); + +gen_oe_tc2(G, N, [X|Rest], Fd, Acc) when is_record(X, attr) -> + {GetT, SetT} = mk_attr_func_types([], X), + NewAcc = + lists:foldl(fun(Id, FunAcc) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + GetAttrAtom = ic_util:to_atom(Get), + emit(Fd, "oe_tc(~p) -> \n\t~p;\n", + [GetAttrAtom, GetT]), + case X#attr.readonly of + {readonly, _} -> + GI = io_lib:format("{~p, oe_tc(~p)}", + [Get, GetAttrAtom]), + [GI|FunAcc]; + _ -> + SetAttrAtom = ic_util:to_atom(Set), + + emit(Fd, "oe_tc(~p) -> \n\t~p;\n", + [SetAttrAtom, SetT]), + GetGI = io_lib:format("{~p, oe_tc(~p)}", + [Get, GetAttrAtom]), + SetGI = io_lib:format("{~p, oe_tc(~p)}", + [Set, SetAttrAtom]), + [GetGI, SetGI|FunAcc] + end + end, Acc, ic_forms:get_idlist(X)), + gen_oe_tc2(G, N, Rest, Fd, NewAcc); + +gen_oe_tc2(G,N,[_X|Rest], Fd, Acc) -> + gen_oe_tc2(G,N,Rest, Fd, Acc). + + +gen_oe_tc3(_,_,[],_, Acc) -> + Acc; +gen_oe_tc3(G, N, [X|Rest], Fd, Acc) when is_record(X, op) -> + Function = get_id2(X), + FunctionAtom = ic_util:to_atom(get_id2(X)), + GI = io_lib:format("{~p, ~p:oe_tc(~p)}",[Function, N, FunctionAtom]), + emit(Fd, "oe_tc(~p) -> ~p:oe_tc(~p);\n", + [FunctionAtom, N, FunctionAtom]), + gen_oe_tc3(G, N, Rest, Fd, [GI|Acc]); + +gen_oe_tc3(G, N, [X|Rest], Fd, Acc) when is_record(X, attr) -> + NewAcc = lists:foldl(fun(Id, FunAcc) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + GetAttrAtom = ic_util:to_atom(Get), + emit(Fd, "oe_tc(~p) -> ~p:oe_tc(~p);\n", + [GetAttrAtom, N, GetAttrAtom]), + case X#attr.readonly of + {readonly, _} -> + [io_lib:format("{~p, ~p:oe_tc(~p)}", + [Get, N, GetAttrAtom])|FunAcc]; + _ -> + SetAttrAtom = ic_util:to_atom(Set), + emit(Fd, "oe_tc(~p) -> ~p:oe_tc(~p);\n", + [SetAttrAtom, N, SetAttrAtom]), + [io_lib:format("{~p, ~p:oe_tc(~p)}", + [Get, N, GetAttrAtom]), + io_lib:format("{~p, ~p:oe_tc(~p)}", + [Set, N, SetAttrAtom])|FunAcc] + end + end, Acc, ic_forms:get_idlist(X)), + gen_oe_tc3(G, N, Rest, Fd, NewAcc); + +gen_oe_tc3(G,N,[_X|Rest], Fd, Acc) -> + gen_oe_tc3(G,N,Rest, Fd, Acc). + +gen_calls(G, N, [X|Xs]) when is_record(X, op) -> + case is_oneway(X) of + false -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + emit_skel_func(G, N, X, Name, ArgNames, TypeList, OutArgs, false, + get_opt(G, be)), + gen_calls(G, N, Xs); + true -> + gen_calls(G, N, Xs) + end; + +gen_calls(G, N, [X|Xs]) when is_record(X, attr) -> + emit_attr(G, N, X, fun emit_skel_func/9), + gen_calls(G, N, Xs); + +gen_calls(G, N, [_X|Xs]) -> gen_calls(G, N, Xs); +gen_calls(_G, _N, []) -> ok. + +gen_casts(G, N, [X|Xs]) when is_record(X, op) -> + case is_oneway(X) of + true -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + emit_skel_func(G, N, X, Name, ArgNames, TypeList, OutArgs, true, + get_opt(G, be)), + gen_casts(G, N, Xs); + false -> + gen_casts(G, N, Xs) + end; + +gen_casts(G, N, [_X|Xs]) -> gen_casts(G, N, Xs); +gen_casts(_G, _N, []) -> ok. + +emit_attr(G, N, X, F) -> + XX = #id_of{type=X}, + BE = get_opt(G, be), + {GetType, SetType} = mk_attr_func_types(N, X), + lists:foreach(fun(Id) -> + X2 = XX#id_of{id=Id}, + {Get, Set} = mk_attr_func_names(N, get_id(Id)), + F(G, N, X2, Get, [], GetType, [], + is_oneway(X2), BE), + case X#attr.readonly of + {readonly, _} -> ok; + _ -> + F(G, N, X2, Set, [mk_name(G, "Value")], + SetType, [], + is_oneway(X2), BE) + end end, ic_forms:get_idlist(X)). + + +extract_info(G, _N, X) when is_record(X, op) -> + Name = get_id2(X), + InArgs = ic:filter_params([in,inout], X#op.params), + OutArgs = ic:filter_params([out,inout], X#op.params), + ArgNames = mk_erl_vars(G, InArgs), + TypeList = {ic_forms:get_tk(X), + map(fun(Y) -> ic_forms:get_tk(Y) end, InArgs), + map(fun(Y) -> ic_forms:get_tk(Y) end, OutArgs) + }, + {Name, ArgNames, TypeList, OutArgs}. + + + +%% This function generates the standard functions of an object +%% gen_server +emit_serv_std(erl_corba, G, N, X) -> + Fd = ic_genobj:stubfiled(G), + Impl = ic_genobj:impl(G), + TypeID = ictk:get_IR_ID(G, N, X), + + nl(Fd), nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Object server implementation."]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Function for fetching the interface type ID."]), + nl(Fd), + emit(Fd, "typeID() ->\n"), + emit(Fd, " \"~s\".\n", [TypeID]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Object creation functions."]), + nl(Fd), + emit(Fd, "oe_create() ->\n"), + emit(Fd, " corba:create(?MODULE, \"~s\").\n", [TypeID]), + nl(Fd), + emit(Fd, "oe_create_link() ->\n"), + emit(Fd, " corba:create_link(?MODULE, \"~s\").\n", [TypeID]), + nl(Fd), + emit(Fd, "oe_create(Env) ->\n"), + emit(Fd, " corba:create(?MODULE, \"~s\", Env).\n", [TypeID]), + nl(Fd), + emit(Fd, "oe_create_link(Env) ->\n"), + emit(Fd, " corba:create_link(?MODULE, \"~s\", Env).\n", [TypeID]), + nl(Fd), + emit(Fd, "oe_create(Env, RegName) ->\n"), + emit(Fd, " corba:create(?MODULE, \"~s\", Env, RegName).\n", [TypeID]), + nl(Fd), + emit(Fd, "oe_create_link(Env, RegName) ->\n"), + emit(Fd, " corba:create_link(?MODULE, \"~s\", Env, RegName).\n", [TypeID]), + nl(Fd), + ic_codegen:mcomment(Fd, ["Init & terminate functions."]), + nl(Fd), + emit(Fd, "init(Env) ->\n"), + ic_codegen:comment(Fd, "Call to implementation init"), + emit(Fd, " corba:handle_init(~p, Env).\n", [to_atom(Impl)]), + nl(Fd), + emit(Fd, "terminate(Reason, State) ->\n"), + emit(Fd, " corba:handle_terminate(~p, Reason, State).\n", + [to_atom(Impl)]), + nl(Fd), nl(Fd), + Fd; +emit_serv_std(erl_genserv, G, N, X) -> + Fd = ic_genobj:stubfiled(G), + Impl = ic_genobj:impl(G), + TypeID = ictk:get_IR_ID(G, N, X), + + nl(Fd), nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Server implementation."]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Function for fetching the interface type ID."]), + nl(Fd), + emit(Fd, "typeID() ->\n"), + emit(Fd, " \"~s\".\n", [TypeID]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Server creation functions."]), + nl(Fd), + emit(Fd, "oe_create() ->\n"), + emit(Fd, " start([], []).\n", []), + nl(Fd), + emit(Fd, "oe_create_link() ->\n"), + emit(Fd, " start_link([], []).\n", []), + nl(Fd), + emit(Fd, "oe_create(Env) ->\n"), + emit(Fd, " start(Env, []).\n", []), + nl(Fd), + emit(Fd, "oe_create_link(Env) ->\n"), + emit(Fd, " start_link(Env, []).\n", []), + nl(Fd), + emit(Fd, "oe_create(Env, RegName) ->\n"), + emit(Fd, " start(RegName, Env, []).\n", []), + nl(Fd), + emit(Fd, "oe_create_link(Env, RegName) ->\n"), + emit(Fd, " start_link(RegName, Env, []).\n", []), + nl(Fd), + ic_codegen:mcomment(Fd, ["Start functions."]), + nl(Fd), + emit(Fd, "start(Env, Opt) ->\n"), + emit(Fd, " gen_server:start(?MODULE, Env, Opt).\n"), + nl(Fd), + emit(Fd, "start_link(Env, Opt) ->\n"), + emit(Fd, " gen_server:start_link(?MODULE, Env, Opt).\n"), + nl(Fd), + emit(Fd, "start(RegName, Env, Opt) ->\n"), + emit(Fd, " gen_server:start(RegName, ?MODULE, Env, Opt).\n"), + nl(Fd), + emit(Fd, "start_link(RegName, Env, Opt) ->\n"), + emit(Fd, " gen_server:start_link(RegName, ?MODULE, Env, Opt).\n"), + nl(Fd), + ic_codegen:comment(Fd, "Standard gen_server termination"), + emit(Fd, "stop(OE_THIS) ->\n"), + emit(Fd, " gen_server:cast(OE_THIS,stop).\n"), + nl(Fd), + ic_codegen:comment(Fd, "Call to implementation init"), + emit(Fd, "init(Env) ->\n"), + emit(Fd, " ~p:~p(Env).\n", [to_atom(Impl), init]), + nl(Fd), + emit(Fd, "terminate(Reason, State) ->\n"), + emit(Fd, " ~p:~p(Reason, State).\n", + [to_atom(Impl), terminate]), + nl(Fd), nl(Fd), + Fd. + +gen_end_of_call(erl_corba, G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server call handle"]), + emit(Fd, "handle_call(stop, _, State) ->\n"), + emit(Fd, " {stop, normal, ok, State}"), + case get_opt(G, serv_last_call) of + exception -> + emit(Fd, ";\n"), + nl(Fd), + emit(Fd, "handle_call(_, _, State) ->\n"), + emit(Fd, " {reply, catch corba:raise(#'BAD_OPERATION'{minor=1163001857, completion_status='COMPLETED_NO'}), State}.\n"); + exit -> + emit(Fd, ".\n"), + nl(Fd), + nl(Fd) + end, + ok; +gen_end_of_call(erl_genserv, G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server call handle"]), + emit(Fd, "handle_call(stop, _, State) ->\n"), + emit(Fd, " {stop, normal, ok, State}"), + emit(Fd, ".\n"), + nl(Fd), nl(Fd), + ok. + +gen_end_of_cast(erl_corba, G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server cast handle"]), + emit(Fd, "handle_cast(stop, State) ->\n"), + emit(Fd, " {stop, normal, State}"), + case get_opt(G, serv_last_call) of + exception -> + emit(Fd, ";\n"), + nl(Fd), + emit(Fd, "handle_cast(_, State) ->\n"), + emit(Fd, " {noreply, State}.\n"); + exit -> + emit(Fd, ".\n"), + nl(Fd), nl(Fd) + end, + ok; +gen_end_of_cast(erl_genserv, G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server cast handle"]), + emit(Fd, "handle_cast(stop, State) ->\n"), + emit(Fd, " {stop, normal, State}"), + emit(Fd, ".\n"), + nl(Fd), nl(Fd), + ok. + +emit_skel_footer(erl_corba, G, N, X) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server handles"]), + case use_impl_handle_info(G, N, X) of + true -> + emit(Fd, "handle_info(Info, State) ->\n"), + emit(Fd, " corba:handle_info(~p, Info, State).\n\n", + [list_to_atom(ic_genobj:impl(G))]); + false -> + emit(Fd, "handle_info(_, State) ->\n"), + emit(Fd, " {noreply, State}.\n\n") + end, + nl(Fd), + case get_opt(G, no_codechange) of + false -> + emit(Fd, "code_change(OldVsn, State, Extra) ->\n"), + emit(Fd, " corba:handle_code_change(~p, OldVsn, State, Extra).\n\n", + [list_to_atom(ic_genobj:impl(G))]); + true -> + emit(Fd, "code_change(_, State, _) ->\n"), + emit(Fd, " {ok, State}.\n\n") + end, + ok; +emit_skel_footer(erl_genserv, G, N, X) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server handles"]), + case use_impl_handle_info(G, N, X) of + true -> + emit(Fd, "handle_info(Info, State) ->\n"), + emit(Fd, " ~p:handle_info(Info, State).\n\n", + [list_to_atom(ic_genobj:impl(G))]); + false -> + emit(Fd, "handle_info(_, State) ->\n"), + emit(Fd, " {noreply, State}.\n\n") + end, + nl(Fd), nl(Fd), + case get_opt(G, no_codechange) of + false -> + emit(Fd, "code_change(OldVsn, State, Extra) ->\n"), + emit(Fd, " ~p:code_change(OldVsn, State, Extra).\n\n", + [list_to_atom(ic_genobj:impl(G))]); + true -> + emit(Fd, "code_change(_, State, _) ->\n"), + emit(Fd, " {ok, State}.\n\n") + end, + ok. + + +use_impl_handle_info(G, N, X) -> + FullName = ic_util:to_colon([get_id2(X) | N]), + case {get_opt(G, {handle_info, true}), get_opt(G, {handle_info, FullName})} of + {_, force_false} -> false; + {false, false} -> false; + _ -> true + end. + +use_timeout(G, N, _X) -> + FullName = ic_util:to_colon(N), + case {get_opt(G, {timeout, true}), get_opt(G, {timeout, FullName})} of + {_, force_false} -> false; + {false, false} -> false; + _ -> true + end. + +use_precond(G, N, X) -> + FullName = ic_util:to_colon([get_id2(X) | N]), + case get_opt(G, {precond, FullName}) of + false -> + InterfaceName = ic_util:to_colon(N), + case get_opt(G, {precond, InterfaceName}) of + false -> + case get_opt(G, precond) of + false -> false; + V2 -> V2 + end; + V2 -> V2 + end; + V1 -> V1 + end. + +use_postcond(G, N, X) -> + FullName = ic_util:to_colon([get_id2(X) | N]), + case get_opt(G, {postcond, FullName}) of + false -> + InterfaceName = ic_util:to_colon(N), + case get_opt(G, {postcond, InterfaceName}) of + false -> + case get_opt(G, postcond) of + false -> false; + V3 -> V3 + end; + V2 -> V2 + end; + V1 -> V1 + end. + + +%%------------------------------------------------------------ +%% +%% Export stuff +%% +%% Gathering of all names that should be exported from a stub +%% file. +%% + + +gen_head_special(G, N, X) when is_record(X, interface) -> + Fd = ic_genobj:stubfiled(G), + + foreach(fun({Name, Body}) -> + ic_codegen:comment(Fd, "Exports from ~p", + [ic_util:to_colon(Name)]), + ic_codegen:export(Fd, exp_top(G, N, Body, [], get_opt(G, be))), + nl(Fd) + end, X#interface.inherit_body), + + ic_codegen:comment(Fd, "Type identification function"), + ic_codegen:export(Fd, [{typeID, 0}]), + nl(Fd), + ic_codegen:comment(Fd, "Used to start server"), + ic_codegen:export(Fd, [{oe_create, 0}, {oe_create_link, 0}, {oe_create, 1}, {oe_create_link, 1}, + {oe_create, 2}, {oe_create_link, 2}]), + nl(Fd), + case get_opt(G, be) of + erl_corba -> + ic_codegen:comment(Fd, "TypeCode Functions and inheritance"), + ic_codegen:export(Fd, [{oe_tc, 1}, {oe_is_a, 1}, {oe_get_interface, 0}]); + _ -> + ic_codegen:export(Fd, [{start, 2}, {start_link, 3}]) + end, + nl(Fd), + ic_codegen:comment(Fd, "gen server export stuff"), + emit(Fd, "-behaviour(gen_server).\n"), + + case get_opt(G, be) of + erl_genserv -> %% stop/1 is only for erl_genserv backend + ic_codegen:export(Fd, [{stop, 1}, {init, 1}, {terminate, 2}, {handle_call, 3}, + {handle_cast, 2}, {handle_info, 2}, {code_change, 3}]); + _ -> + ic_codegen:export(Fd, [{init, 1}, {terminate, 2}, {handle_call, 3}, + {handle_cast, 2}, {handle_info, 2}, {code_change, 3}]) + end, + + case get_opt(G, be) of + erl_corba -> + nl(Fd), + emit(Fd, "-include_lib(\"~s/include/~s\").\n", [?ORBNAME, ?CORBAHRL]); + _ -> + ok + end, + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Object interface functions."]), + nl(Fd), nl(Fd), nl(Fd), + Fd; +gen_head_special(_G, _N, _X) -> ok. + + + +%% Shall generate all export declarations +gen_head(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + F = ic_genobj:stubfiled(G), + ic_codegen:comment(F, "Interface functions"), + ic_codegen:export(F, exp_top(G, N, X, [], get_opt(G, be))), + nl(F), + gen_head_special(G, N, X); + false -> ok + end. + +exp_top(_G, _N, X, Acc, _) when element(1, X) == preproc -> + Acc; +exp_top(G, N, L, Acc, BE) when is_list(L) -> + exp_list(G, N, L, Acc, BE); +exp_top(G, N, M, Acc, BE) when is_record(M, module) -> + exp_list(G, N, get_body(M), Acc, BE); +exp_top(G, N, I, Acc, BE) when is_record(I, interface) -> + exp_list(G, N, get_body(I), Acc, BE); +exp_top(G, N, X, Acc, BE) -> + exp3(G, N, X, Acc, BE). + +exp3(_G, _N, C, Acc, _BE) when is_record(C, const) -> + [{get_id(C#const.id), 0} | Acc]; +exp3(_G, _N, Op, Acc, erl_corba) when is_record(Op, op) -> + FuncName = get_id(Op#op.id), + Arity = length(ic:filter_params([in, inout], Op#op.params)) + 1, + [{FuncName, Arity}, {FuncName, Arity+1} | Acc]; +exp3(G, N, Op, Acc, _BE) when is_record(Op, op) -> + FuncName = get_id(Op#op.id), + Arity = + case use_timeout(G,N,Op) of + true -> + %% NO TimeOut on ONEWAYS here !!!! + case is_oneway(Op) of + true -> + length(ic:filter_params([in, inout], Op#op.params)) + 1; + false -> + length(ic:filter_params([in, inout], Op#op.params)) + 2 + end; + false -> + length(ic:filter_params([in, inout], Op#op.params)) + 1 + end, + [{FuncName, Arity} | Acc]; + +exp3(_G, _N, A, Acc, erl_corba) when is_record(A, attr) -> + lists:foldr(fun(Id, Acc2) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + case A#attr.readonly of + {readonly, _} -> [{Get, 1}, {Get, 2} | Acc2]; + _ -> [{Get, 1}, {Get, 2}, + {Set, 2}, {Set, 3} | Acc2] + end end, Acc, ic_forms:get_idlist(A)); +exp3(_G, _N, A, Acc, _BE) when is_record(A, attr) -> + lists:foldr(fun(Id, Acc2) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + case A#attr.readonly of + {readonly, _} -> [{Get, 1} | Acc2]; + _ -> [{Get, 1}, {Set, 2} | Acc2] + end end, Acc, ic_forms:get_idlist(A)); + +exp3(_G, _N, _X, Acc, _BE) -> Acc. + +exp_list(G, N, L, OrigAcc, BE) -> + lists:foldr(fun(X, Acc) -> exp3(G, N, X, Acc, BE) end, OrigAcc, L). + + + + +%%------------------------------------------------------------ +%% +%% Emit stuff +%% +%% Low level generation primitives +%% + +emit_stub_func(G, N, X, Name, ArgNames, _TypeList, OutArgs, Oneway, Backend) -> + case ic_genobj:is_stubfile_open(G) of + false -> + ok; + true -> + Fd = ic_genobj:stubfiled(G), + StubName = list_to_atom(Name), + UsingTimeout = use_timeout(G, N, X), + Timeout = case UsingTimeout of + true -> + mk_name(G, "Timeout"); + false -> + "infinity" + end, + Options = mk_name(G, "Options"), + This = mk_name(G, "THIS"), + CallOrCast = + case is_oneway(X) of + true -> ?CAST; + _ -> ?CALL + end, + emit_op_comment(G, Fd, X, StubName, ArgNames, OutArgs), + case Backend of + erl_corba -> + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This | ArgNames])]), + emit(Fd, " ~s:~s(~s, ~p, [~s], ?MODULE).\n\n", + [?CORBAMOD, CallOrCast, This, StubName, mk_list(ArgNames)]), + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This, Options| ArgNames])]), + emit(Fd, " ~s:~s(~s, ~p, [~s], ?MODULE, ~s).\n\n", + [?CORBAMOD, CallOrCast, This, StubName, mk_list(ArgNames), + Options]); + _ -> + FunName = case ic_options:get_opt(G, scoped_op_calls) of + true -> + list_to_atom(ic_util:to_undersc([Name | N])); + false -> + StubName + end, + %% NO TimeOut on ONEWAYS here !!!! + case Oneway of + true -> + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This | ArgNames])]); + false -> + case UsingTimeout of + true -> + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This, Timeout| ArgNames])]); + false -> + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This | ArgNames])]) + end + end, + + %% NO TimeOut on ONEWAYS here !!!! + if + length(ArgNames) == 0 -> + case is_oneway(X) of + true -> + emit(Fd, " ~s:~s(~s, ~p).\n\n", + [?GENSERVMOD, CallOrCast, This, FunName]); + false -> + emit(Fd, " ~s:~s(~s, ~p, ~s).\n\n", + [?GENSERVMOD, CallOrCast, This, FunName, Timeout]) + end; + true -> + case is_oneway(X) of + true -> + emit(Fd, " ~s:~s(~s, {~p, ~s}).\n\n", + [?GENSERVMOD, CallOrCast, This, FunName, + mk_list(ArgNames)]); + false -> + emit(Fd, " ~s:~s(~s, {~p, ~s}, ~s).\n\n", + [?GENSERVMOD, CallOrCast, This, FunName, + mk_list(ArgNames), Timeout]) + end + end + end + end. + +emit_skel_func(G, N, X, OpName, ArgNames, TypeList, OutArgs, Oneway, Backend) -> + case ic_genobj:is_stubfile_open(G) of + false -> + ok; + true -> + emit_skel_func_helper(G, N, X, OpName, ArgNames, TypeList, OutArgs, + Oneway, Backend) + end. + +emit_skel_func_helper(G, N, X, OpName, ArgNames, _TypeList, OutArgs, Oneway, + erl_corba) -> + Fd = ic_genobj:stubfiled(G), + Name = list_to_atom(OpName), + ImplF = Name, + ImplM = list_to_atom(ic_genobj:impl(G)), + ThisStr = mk_name(G, "THIS"), + FromStr = mk_name(G, "From"), + State = mk_name(G, "State"), + Context = mk_name(G, "Context"), + + {UseFrom, From} = + case Oneway of + false -> + case use_from(G, N, OpName) of + true -> + {FromStr, FromStr}; + false -> + {"false", "_"} + end; + true -> + {"false", "_"} + end, + {UseThis, This} = + case use_this(G, N, OpName) of + true -> + {ThisStr, ThisStr}; + false -> + {"false", "_"} + end, + %% Create argument list string + CallArgs = mk_list(ArgNames), + emit_op_comment(G, Fd, X, Name, ArgNames, OutArgs), + + %% Check if pre and post conditions are specified for this operation + Precond = use_precond(G, N, X), + Postcond = use_postcond(G, N, X), + + case Oneway of + true -> + emit(Fd, "handle_cast({~s, ~s, ~p, [~s]}, ~s) ->\n", + [This, Context, Name, CallArgs, State]), + case {Precond, Postcond} of + {false, false} -> + emit(Fd, " corba:handle_cast(~p, ~p, [~s], ~s, ~s, ~s);\n\n", + [ImplM, ImplF, CallArgs, State, Context, UseThis]); + _ -> + emit(Fd, " corba:handle_cast(~p, ~p, [~s], ~s, ~s, ~s, ~p, ~p, ?MODULE);\n\n", + [ImplM, ImplF, CallArgs, State, Context, UseThis, + Precond, Precond]) + end; + false -> + emit(Fd, "handle_call({~s, ~s, ~p, [~s]}, ~s, ~s) ->\n", + [This, Context, Name, CallArgs, From, State]), + case {Precond, Postcond} of + {false, false} -> + emit(Fd, " corba:handle_call(~p, ~p, [~s], ~s, ~s, ~s, ~s);\n\n", + [ImplM, ImplF, CallArgs, State, Context, UseThis, UseFrom]); + _-> + emit(Fd, " corba:handle_call(~p, ~p, [~s], ~s, ~s, ~s, ~s, ~p, ~p, ?MODULE);\n\n", + [ImplM, ImplF, CallArgs, State, Context, UseThis, UseFrom, + Precond, Postcond]) + end + end; +emit_skel_func_helper(G, N, X, OpName, ArgNames, _TypeList, OutArgs, Oneway, + _Backend) -> + Fd = ic_genobj:stubfiled(G), + Name = list_to_atom(OpName), + ImplF = Name, + ImplM = list_to_atom(ic_genobj:impl(G)), + FromStr = mk_name(G, "From"), + State = mk_name(G, "State"), + + %% Create argument list + CallArgs1 = [State | ArgNames], + {CallArgs2, From} = + case is_oneway(X) of + false -> + case use_from(G, N, OpName) of + true -> + {[FromStr | CallArgs1], FromStr}; + false -> + {CallArgs1, "_"} + end; + true -> + {CallArgs1, "_"} + end, + %% Create argument list string + CallArgs = mk_list(CallArgs2), + emit_op_comment(G, Fd, X, Name, ArgNames, OutArgs), + FunName = case ic_options:get_opt(G, scoped_op_calls) of + true -> + list_to_atom(ic_util:to_undersc([OpName | N])); + false -> + list_to_atom(OpName) + end, + case Oneway of + true -> + if + length(ArgNames) == 0 -> + emit(Fd, "handle_cast(~p, ~s) ->\n", [FunName, State]); + true -> + emit(Fd, "handle_cast({~p, ~s}, ~s) ->\n", + [FunName, mk_list(ArgNames), State]) + end, + emit(Fd, " ~p:~p(~s);\n\n", [ImplM, ImplF, CallArgs]); + false -> + if + length(ArgNames) == 0 -> + emit(Fd, "handle_call(~p, ~s, ~s) ->\n", + [FunName, From, State]); + true -> + emit(Fd, "handle_call({~p, ~s}, ~s, ~s) ->\n", + [FunName, mk_list(ArgNames), From, State]) + end, + emit(Fd, " ~p:~p(~s);\n\n", [ImplM, ImplF, CallArgs]) + end. + +use_this(G, N, OpName) -> + FullOp = ic_util:to_colon([OpName|N]), + FullIntf = ic_util:to_colon(N), + case {get_opt(G, {this, FullIntf}), get_opt(G, {this, FullOp}), + get_opt(G, {this, true})} of + {_, force_false, _} -> false; + {force_false, false, _} -> false; + {false, false, false} -> false; + _ -> true + end. + +use_from(G, N, OpName) -> + FullOp = ic_util:to_colon([OpName|N]), + FullIntf = ic_util:to_colon(N), + case {get_opt(G, {from, FullIntf}), get_opt(G, {from, FullOp}), + get_opt(G, {from, true})} of + {_, force_false, _} -> false; + {force_false, false, _} -> false; + {false, false, false} -> false; + _ -> true + end. + + +emit_constant_func(G, Id, Val) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + N = list_to_atom(get_id(Id)), + emit_const_comment(G, Fd, Id, N), + emit(Fd, "~p() -> ~p.\n\n", [N, Val]) + end. + + + +emit_const_comment(_G, F, _X, Name) -> + ic_codegen:mcomment_light(F, + [io_lib:format("Constant: ~p", [Name])]). + + +emit_op_comment(G, F, X, Name, InP, OutP) -> + ic_codegen:mcomment_light(F, + [io_lib:format("~s: ~p", [get_title(X), Name]), + "", + get_returns(G, X, InP, OutP) | + get_raises(X)]). + +get_title(X) when is_record(X, attr) -> "Attribute Operation"; +get_title(_X) -> "Operation". + +get_raises(X) when is_record(X, op) -> + if X#op.raises == [] -> []; + true -> + [" Raises: " ++ + mk_list(lists:map(fun(E) -> ic_util:to_colon(E) end, + X#op.raises))] + end; +get_raises(_X) -> []. + +get_returns(_G, _X, _InP, []) -> + " Returns: RetVal"; +get_returns(G, _X, _InP, OutP) -> + " Returns: "++mk_list(["RetVal" | mk_erl_vars(G, OutP)]). + + + + +%%------------------------------------------------------------ +%% +%% Utilities +%% +%% Convenient little go-get functions +%% +%%------------------------------------------------------------ + +%% The automaticly generated get and set operation names for an +%% attribute. +mk_attr_func_names(_Scope, Name) -> + {"_get_" ++ Name, "_set_" ++ Name}. +%% {scoped_name(Scope, "_get_"++Name), scoped_name(Scope, "_set_"++Name)}. + +%% Returns TK of the Get and Set attribute functions. +mk_attr_func_types(_N, X) -> + TK = ic_forms:get_tk(X), + {{TK, [], []}, {tk_void, [TK], []}}. + + + +%%------------------------------------------------------------ +%% +%% Generation utilities and common stuff +%% +%% Convenient stuff for generation +%% +%%------------------------------------------------------------ + + +%% Input is a list of parameters (in parse form) and output is a list +%% of capitalised variable names. mk_var is in icgen +mk_erl_vars(_G, Params) -> + map(fun(P) -> mk_var(get_id(P#param.id)) end, Params). + + +%% mk_list produces a nice comma separated string of variable names +mk_list([]) -> []; +mk_list([Arg | Args]) -> + Arg ++ mk_list2(Args). +mk_list2([Arg | Args]) -> + ", " ++ Arg ++ mk_list2(Args); +mk_list2([]) -> []. + + +%%------------------------------------------------------------ +%% +%% Parser utilities +%% +%% Called from the yecc parser. Expands the identifier list of an +%% attribute so that the attribute generator never has to handle +%% lists. +%% +%%------------------------------------------------------------ + + +%% Unfold identifier lists or nested lists. Note that many records +%% contain an entry named id that is a list before unfold and a single +%% id afterwards. +unfold(L) when is_list(L) -> + lists:flatten(map(fun(X) -> unfold2(X) end, L)); +unfold(X) -> unfold2(X). + +unfold2(A) when is_record(A, attr) -> + map(fun(Id) -> A#attr{id=Id} end, A#attr.id); +unfold2(M) when is_record(M, member) -> + map(fun(Id) -> M#member{id=Id} end, M#member.id); +unfold2(M) when is_record(M, case_dcl) -> + map(fun(Id) -> M#case_dcl{label=Id} end, M#case_dcl.label); +unfold2(T) when is_record(T, typedef) -> + map(fun(Id) -> T#typedef{id=Id} end, T#typedef.id). + + + + +%% Code produce for dependency function +genDependency(G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd),nl(Fd), + ic_codegen:comment(Fd, "Idl file dependency list function"), + emit(Fd, "oe_dependency() ->\n\n", []), + emit(Fd, " ~p.\n\n", [ic_pragma:get_dependencies(G)]). diff --git a/lib/ic/src/ic_error.erl b/lib/ic/src/ic_error.erl new file mode 100644 index 0000000000..f41e78a8be --- /dev/null +++ b/lib/ic/src/ic_error.erl @@ -0,0 +1,375 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_error). + +-include_lib("ic/src/ic.hrl"). +-include_lib("ic/src/ic_debug.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([error/2, + fatal_error/2, + init_errors/1, + return/1, + warn/2, + get_error_count/1]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% +%% Error and warning utilities. +%% +%% Note that errors are somewhat brutal and that warnings are kept in +%% a list for the user to extract at a later stage. The handling of +%% warnings is entirely up to the user while handling of errors is +%% never left to the user. +%% +%%-------------------------------------------------------------------- + +return(G) -> + case ic_options:get_opt(G, silent2) of + true -> + case get_error_count(G) of + 0 -> {ok, get_list(G, warn_list)}; + _X -> {error, get_list(G, warn_list), get_list(G, error_list)} + end; + false -> + case get_error_count(G) of + 0 -> ok; + X -> print_error(G, {error, g, ic_genobj:idlfile(G), {error_count, X}}), + error + end + end. + + +get_list(G, ListName) -> + ?lookup(G#genobj.options, ListName). + + +%% Public function for reporting an error +error(G, Err) -> + Error = {error, g, ic_genobj:idlfile(G), Err}, + case insert_in_list(G, Error, error_list) of + new -> + print_error(G, Error), + MaxErrs = ic_options:get_opt(G, maxerrs), + case incr_counter(G, error_count) of + X when X >= MaxErrs -> + fatal_error(G, {error_count_exceeded, X}); + _ -> Error + end; + old -> + Error + end. + +%% Public function for reporting an error. NOTE: also stops execution +fatal_error(G, Err) -> + Error = {error, g, ic_genobj:idlfile(G), Err}, + insert_in_list(G, Error, error_list), + incr_counter(G, error_count), + print_error(G, Error), + throw(Error). + + +%% Public function for reporting a warning +warn(G, Warn) -> + Warning = {warn, g, ic_genobj:idlfile(G), Warn}, + case insert_in_list(G, Warning, warn_list) of + new -> + print_warn(G, Warning), + MaxErrs = ic_options:get_opt(G, maxwarns), + case incr_counter(G, warn_count) of + X when X >= MaxErrs -> + fatal_error(G, {warn_count_exceeded, X}); + _ -> ok + end; + old -> ok +end. + + +%% Initialisation of all counters and lists associated with errors and +%% warnings. +init_errors(G) -> + reset_counter(G, error_count), + reset_counter(G, warn_count), + reset_list(G, error_list), + reset_list(G, warn_list), + ok. + + + +%%-------------------------------------------------------------------- +%% Counter and list (warn and error) handling +%% + +incr_counter(G, Counter) -> + Num = ?lookup(G#genobj.options, Counter) + 1, + ?insert(G#genobj.options, Counter, Num), + Num. + +reset_counter(G, Counter) -> + ?insert(G#genobj.options, Counter, 0). + +get_error_count(G) -> + ?lookup(G#genobj.options, error_count). + +reset_list(G, ListName) -> + ?insert(G#genobj.options, ListName, []). + +insert_in_list(G, Item, ListName) -> + List = ?lookup(G#genobj.options, ListName), + case lists:member(Item, List) of + true -> old; + false -> + ?insert(G#genobj.options, ListName, [Item| List]), + new + end. + + +%%-------------------------------------------------------------------- +%% +%% Nice printouts of errors and warnings +%% + + +%% Errors + +print_error(G, Error) -> + case {ic_options:get_opt(G, silent), ic_options:get_opt(G, silent2)} of + {true, _} -> ok; + {_, true} -> ok; + _ -> format_error(Error) + end, + error. + +format_error({error, _, File, {parse_error, Line, Args}}) -> + Fmt = lists:foldl(fun(_, Acc) -> [$~, $s | Acc] end, [], Args), + display(File, Line, Fmt, Args); +format_error({error, _, File, {error_count, X}}) -> + display(File, "~p errors found", [X]); +format_error({error, _, File, {error_count_exceeded, X}}) -> + display(File, "too many errors found (~p)", [X]); +format_error({error, _, File, {warn_count_exceeded, X}}) -> + display(File, "too many warnings found (~p)", [X]); +format_error({error, _, File, {inherit_name_collision, + {Orig, Item}, {Base, NewItem}}}) -> + display(File, ic_forms:get_line(Item), "~s collides with ~s", + [pp([ic_forms:get_id2(Item) | Orig]), pp([ic_forms:get_id2(NewItem) | Base])]); +format_error({error, _, File, {unsupported_op, {'~', Line}}}) -> + display(File, Line, "unsupported unary operation ~~", []); +format_error({error, _, File, {multiply_defined, X}}) -> + display(File, ic_forms:get_line(X), "multiple defined identifier ~p", [ic_forms:get_id2(X)]); +format_error({error, _, File, {illegal_spelling, X}}) -> + display(File, ic_forms:get_line(X), +% "illegal spelling of identifier ~s (capitalisation?)", + "identifier ~p multiply declared - differs in case only", + [ic_forms:get_id2(X)]); +format_error({error, _, File, {illegal_enumerant_value, X}}) -> + display(File, ic_forms:get_line(X), + "Enumerant ~s's value collide by name with other type", + [ic_forms:get_id2(X)]); +format_error({error, _, File, {illegal_forward, X}}) -> + display(File, ic_forms:get_line(X), + "cannot inherit from forwarded interface ~s", [ic_forms:get_id2(X)]); +format_error({error, _, File, {illegal_const_t, X, Type}}) -> + display(File, ic_forms:get_line(X), + "Illegal constant type ~s of ~s", [pp(Type), ic_forms:get_id2(X)]); +format_error({error, _, File, {multiple_cases, X}}) -> + display(File, ic_forms:get_line(X), "multiple case values ~s", [pp(X)]); +format_error({error, _, File, {symtab_not_found, X}}) -> + display(File, ic_forms:get_line(X), "undeclared identifier ~s", [ic_forms:get_id2(X)]); +format_error({error, _, File, {preproc, Lines}}) -> + display(File, "preprocessor error: ~s", [hd(Lines)]); +format_error({error, _, File, {ic_pp_error, Lines}}) -> + display(File, "preprocessor error: ~s", [Lines]); +format_error({error, _, File, {illegal_float, Line}}) -> + display(File, Line, "illegal floating point number", []); +format_error({error, _, File, {bad_type_combination, E, V1, V2}}) -> + display(File, ic_forms:get_line(E), "incompatible types, ~p and ~p", [V1, V2]); +format_error({error, _, File, {bad_oneway_type, X, _TK}}) -> + display(File, ic_forms:get_line(X), "oneway operations must be declared void", []); +format_error({error, _, File, {inout_spec_for_c, X, Arg}}) -> + display(File, ic_forms:get_line(X), "inout parameter ~s specified in native c mode", + [Arg]); +format_error({error, _, File, {sequence_not_defined, X, Arg}}) -> + display(File, ic_forms:get_line(X), "sequence ~s not defined", [Arg]); +format_error({error, _, File, {illegal_typecode_for_c, Arg}}) -> + display(File, not_specified, "illegal typecode ~s used in native c mode", + [Arg]); +format_error({error, _, File, {name_not_found, N}}) -> + display(File, not_specified, "name ~s not found", [N]); +format_error({error, _, File, {illegal_typecode_for_c, Arg, N}}) -> + display(File, not_specified, "illegal typecode ~p used for ~p in native c mode", [Arg, N]); +format_error({error, _, File, {oneway_outparams, X}}) -> + display(File, ic_forms:get_line(X), + "oneway operations may not have out or inout parameters", []); +format_error({error, _, File, {oneway_raises, X}}) -> + display(File, ic_forms:get_line(X), "oneway operations may not raise exceptions", + []); +format_error({error, _, File, {bad_tk_match, T, TK, V}}) -> + display(File, ic_forms:get_line(T), + "value ~p does not match declared type ~s", [V, pp(TK)]); +format_error({error, _, File, {bad_scope_enum_case, ScopedId}}) -> + display(File, ic_forms:get_line(ScopedId), + "scoped enum identifiers not allowed as case (~s)", + [pp(ScopedId)]); +format_error({error, _, File, {bad_type, Expr, Op, _TypeList, V}}) -> + display(File, ic_forms:get_line(Expr), + "parameter value ~p to ~s is of illegal type", [V, pp(Op)]); +format_error({error, _, File, {bad_case_type, TK, X, Val}}) -> + display(File, ic_forms:get_line(X), + "case value ~s does not match discriminator type ~s", + [case_pp(X, Val), pp(TK)]); +format_error({error, _, File, {tk_not_found, X}}) -> + display(File, ic_forms:get_line(X), "undeclared identifier ~s", [pp(X)]); +%%% New format_errors +format_error({error, _, File, {bad_fixed, Format, Args, Line}}) -> + display(File, Line, Format, Args); +format_error({error, _, File, {illegal_switch_t, Arg, _N}}) -> + display(File, ic_forms:get_line(Arg), "illegal switch", []); +format_error({error, _, File, {inherit_resolve, Arg, N}}) -> + display(File, ic_forms:get_line(Arg), "cannot resolve ~s", [N]); +format_error({error, _, File, {bad_escape_character, Line, Char}}) -> + display(File, Line, "bad escape character \"~c\"", [Char]); +format_error({error, _, File, {pragma_code_opt_bad_option_list, Line}}) -> + display(File, Line, "bad option list on pragma \"CODEOPT\"", []); +format_error({error, _, File, {bad_string, Line}}) -> + display(File, Line, "bad string", []); +format_error({error, _, File, {create_dir, Path, Reason}}) -> + display(File, not_specified, "couldn't create directory ~p due to ~p", [Path, Reason]); +format_error({error, _, File, {open_file, Path, Reason}}) -> + display(File, not_specified, "couldn't open ~p due to ~p", [Path, Reason]); +format_error({error, _, File, {plain_error_string, ErrString}}) -> + display(File, not_specified, "~s", [ErrString]); +format_error({error, _, File, {plain_error_string, T, ErrString}}) -> + display(File, ic_forms:get_line(T), "~s", [ErrString]); +format_error({error, _, File, {ErrString, Line}}) -> + display(File, Line, ErrString, []). + + +%% Warnings +print_warn(G, Warn) -> + case {ic_options:get_opt(G, silent), ic_options:get_opt(G, silent2)} of + {true, _} -> ok; + {_, true} -> ok; + _ -> format_warn(Warn) + end. + +format_warn({warn, _, File, {ic_pp_warning, Lines}}) -> + display(File, "preprocessor warning: ~s", [Lines]); +format_warn({warn, _, _File, {cfg_open, _Reason, File}}) -> + display(File, "warning: could not open file: ~p", [File]); +format_warn({warn, _, _File, {cfg_read, File}}) -> + display(File, "warning: syntax error in configuration file", []); +format_warn({warn, _, File, {multi_modules, Id}}) -> + display(File, ic_forms:get_line(Id), "warning: multiple modules in file", []); +format_warn({warn, _, File, {illegal_opt, Opt}}) -> + display(File, "warning: unrecognised option: ~p", [Opt]); +format_warn({warn, _, File, {nested_mod, Id}}) -> + display(File, ic_forms:get_line(Id), "warning: nested module: ~s", [ic_forms:get_id(Id)]); +format_warn({warn, _, File, {inherit_name_shadow, {Orig, Item}, + {Base, NewItem}}}) -> + display(File, ic_forms:get_line(Item), + "warning: ~s shadows ~s", [pp([ic_forms:get_id2(Item) | Orig]), + pp([ic_forms:get_id2(NewItem) | Base])]); +format_warn({warn, _, File, {internal_307, X, Y}}) -> + %% If global Scope variable is not [] at top level constant + display(File, ic_forms:get_line(X), "warning: internal 307: ~p ~p", [X, Y]); +format_warn({warn, _, File, {WarnString, Line}}) -> + display(File, Line, WarnString, []). + +%% Display an error or warning +display(File, not_specified, F, A) -> + io:format("~p : ~s~n", [File, io_lib:format(F, A)]); +display(File, Line, F, A) -> + io:format("~p on line ~p: ~s~n", [File, Line, io_lib:format(F, A)]). +display(File, F, A) -> + io:format("~p: ~s~n", [File, io_lib:format(F, A)]). + + + +%%format_warn2(G, WarnStr) -> +%% case {ic_options:get_opt(G, silent), ic_options:get_opt(G, silent2), +%% ic_options:get_opt(G, nowarn)} of +%% {false, false, false} -> +%% io:format("~p: warning: ~s~n", [ic_genobj:idlfile(G), WarnStr]); +%% _ -> ok +%% end. + +%%format_warn2(G, Line, WarnStr) -> +%% case {ic_options:get_opt(G, silent), ic_options:get_opt(G, silent2), +%% ic_options:get_opt(G, nowarn)} of +%% {false, false, false} -> +%% io:format("~p on line ~p: warning: ~s~n", +%% [ic_genobj:idlfile(G), Line, WarnStr]); +%% _ -> ok +%% end. + + + + +%% pretty print various stuff + +pp({tk_string, _}) -> "string"; +pp({tk_wstring, _}) -> "wstring"; +pp(tk_long) -> "long"; +pp(tk_short) -> "short"; +pp(tk_ushort) -> "unsigned short"; +pp(tk_ulong) -> "unsigned long"; +pp(tk_float) -> "float"; +pp(tk_double) -> "double"; +pp(tk_boolean) -> "boolean"; +pp(tk_char) -> "char"; +pp(tk_wchar) -> "wchar"; +pp(tk_octet) -> "octet"; +pp(tk_null) -> "null"; +pp(tk_void) -> "void"; +pp(tk_any) -> "any"; +pp({tk_fixed, _, _}) -> "fixed"; +pp({tk_objref, _, _}) -> "object reference"; +pp(rshift) -> ">>"; +pp(lshift) -> "<<"; +pp(X) when element(1, X) == tk_enum -> "enum"; +pp(X) when is_record(X, scoped_id) -> ic_util:to_colon(X); +pp(X) when element(1, X) == '<identifier>' -> ic_forms:get_id(X); +pp(X) when is_list(X) andalso is_list(hd(X)) -> ic_util:to_colon(X); +pp({_, Num, Beef}) when is_integer(Num) -> Beef; +pp({Beef, Num}) when is_integer(Num) -> ic_util:to_list(Beef); +pp(X) -> ic_util:to_list(X). + +%% special treatment of case label names +case_pp(X, _Val) when is_record(X, scoped_id) -> pp(X); +case_pp(_X, Val) -> pp(Val). + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- diff --git a/lib/ic/src/ic_fetch.erl b/lib/ic/src/ic_fetch.erl new file mode 100644 index 0000000000..c1b140ef11 --- /dev/null +++ b/lib/ic/src/ic_fetch.erl @@ -0,0 +1,388 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_fetch). + +-include("icforms.hrl"). + +-export([member2type/3]). + +-export([fetchTk/3, isArray/3, isBasicType/1, isBasicType/2, + isBasicType/3, isBasicTypeOrEterm/3, isEterm/3, isString/3, + isStruct/3, isUnion/3, name2type/2, searchIncludedTk/2, + searchInsideTks/2, searchTk/2, searchTk/3]). + +name2type(G, Name) -> + S = ic_genobj:tktab(G), + ScopedName = lists:reverse(string:tokens(Name,"_")), + InfoList = ets:lookup( S, ScopedName ), + filter( InfoList ). + + + +%% This is en overloaded function, +%% differs in input on unions +member2type(_G, X, I) when is_record(X, union)-> + Name = ic_forms:get_id2(I), + case lists:keysearch(Name,2,element(6,X#union.tk)) of + false -> + error; + {value,Rec} -> + fetchType(element(3,Rec)) + end; +member2type( G, SName, MName ) -> + + S = ic_genobj:tktab( G ), + SNList = lists:reverse(string:tokens(SName,"_")), + ScopedName = [MName | SNList], + InfoList = ets:lookup( S, ScopedName ), + + case filter( InfoList ) of + error -> + %% Try a little harder, seeking inside tktab + case lookup_member_type_in_tktab(S, ScopedName, MName) of + error -> + %% Check if this is the "return to return1" case + case MName of + "return1" -> + %% Do it all over again ! + ScopedName2 = ["return" | SNList], + InfoList2 = ets:lookup( S, ScopedName2 ), + case filter( InfoList2 ) of + error -> + %% Last resort: seek in pragma table + lookup_type_in_pragmatab(G, SName); + + Other -> + Other + end; + _ -> + %% Last resort: seek in pragma table + lookup_type_in_pragmatab(G, SName) + end; + Other -> + Other + end; + Other -> + Other + end. + + +lookup_member_type_in_tktab(S, ScopedName, MName) -> + case ets:match_object(S, {'_',member,{MName,'_'},nil}) of + [] -> + error; + [{_FullScopedName,member,{MName,TKInfo},nil}]-> + fetchType( TKInfo ); + List -> + lookup_member_type_in_tktab(List,ScopedName) + end. + +lookup_member_type_in_tktab([],_ScopedName) -> + error; +lookup_member_type_in_tktab([{FullScopedName,_,{_,TKInfo},_}|Rest],ScopedName) -> + case lists:reverse(string:tokens(ic_util:to_undersc(FullScopedName),"_")) of + ScopedName -> + fetchType(TKInfo); + _ -> + lookup_member_type_in_tktab(Rest,ScopedName) + end. + + +lookup_type_in_pragmatab(G, SName) -> + S = ic_genobj:pragmatab(G), + + %% Look locally first + case ets:match(S,{file_data_local,'_','_','$2','_','_',SName,'_','_'}) of + [] -> + %% No match, seek included + case ets:match(S,{file_data_included,'_','_','$2','_','_',SName,'_','_'}) of + + [] -> + error; + [[Type]] -> + io:format("1 Found(~p) : ~p~n",[SName,Type]), + Type + end; + + [[Type]] -> + io:format("2 Found(~p) : ~p~n",[SName,Type]), + Type + end. + + + + +filter( [] ) -> + error; +filter( [I | Is ] ) -> + case I of + { _, member, { _, TKINFO }, _ } -> + fetchType( TKINFO ); + + { _, struct, _, _ } -> + struct; + + { _, typedef, TKINFO, _ } -> + fetchType( TKINFO ); + + { _, module, _, _ } -> + module; + + { _, interface, _, _ } -> + interface; + + { _, op, _, _ } -> + op; + + { _,enum, _, _ } -> + enum; + + { _, spellcheck } -> + filter( Is ); + + _ -> + error + end. + + +fetchType( { tk_sequence, _, _ } ) -> + sequence; +fetchType( { tk_array, _, _ } ) -> + array; +fetchType( { tk_struct, _, _, _} ) -> + struct; +fetchType( { tk_string, _} ) -> + string; +fetchType( tk_short ) -> + short; +fetchType( tk_long ) -> + long; +fetchType( tk_ushort ) -> + ushort; +fetchType( tk_ulong ) -> + ulong; +fetchType( tk_float ) -> + float; +fetchType( tk_double ) -> + double; +fetchType( tk_boolean ) -> + boolean; +fetchType( tk_char ) -> + char; +fetchType( tk_octet ) -> + octet; +fetchType( { tk_enum, _, _, _ } ) -> + enum; +fetchType( { tk_union, _, _, _, _, _ } ) -> + union; +fetchType( tk_any ) -> + any; +fetchType( _ ) -> + error. + +isBasicTypeOrEterm(G, N, S) -> + case isBasicType(G, N, S) of + true -> + true; + false -> + isEterm(G, N, S) + end. + + +isEterm(G, N, S) when element(1, S) == scoped_id -> + {FullScopedName, _, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + case ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)) of + "erlang_term" -> + true; + "ETERM*" -> + true; + _X -> + false + end; +isEterm(_G, _Ni, _X) -> + false. + +isBasicType(G, N, S) when element(1, S) == scoped_id -> + {_, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + isBasicType(fetchType(TK)); +isBasicType(_G, _N, {string, _} ) -> + false; +isBasicType(_G, _N, {Type, _} ) -> + isBasicType(Type). + + +isBasicType(G, Name) -> + isBasicType(name2type(G, Name )). + + +isBasicType(Type) -> + lists:member(Type, + [tk_short,short, + tk_long,long, + tk_ushort,ushort, + tk_ulong,ulong, + tk_float,float, + tk_double,double, + tk_boolean,boolean, + tk_char,char, + tk_octet,octet]). + + + +isString(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_string',_}, _} -> + true; + _ -> + false + end; +isString(_G, _N, T) when is_record(T, string) -> + true; +isString(_G, _N, _Other) -> + false. + + +isArray(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_array', _, _}, _} -> + true; + _ -> + false + end; +isArray(_G, _N, T) when is_record(T, array) -> + true; +isArray(_G, _N, _Other) -> + false. + + + +isStruct(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_struct', _, _, _}, _} -> + true; + _ -> + false + end; +isStruct(_G, _N, T) when is_record(T, struct) -> + true; +isStruct(_G, _N, _Other) -> + false. + + + +isUnion(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_union', _, _, _,_,_}, _} -> + true; + _Other -> + false + end; +isUnion(_G, _N, T) when is_record(T, union) -> + true; +isUnion(_G, _N, _Other) -> + false. + + + +%%------------------------------------------------------------ +%% +%% Always fetchs TK of a record. +%% +%%------------------------------------------------------------ +fetchTk(G,N,X) -> + case ic_forms:get_tk(X) of + undefined -> + searchTk(G,ictk:get_IR_ID(G, N, X)); + TK -> + TK + end. + + +%%------------------------------------------------------------ +%% +%% seek type code when not accessible by get_tk/1 +%% +%%------------------------------------------------------------ +searchTk(G,IR_ID) -> + S = ic_genobj:tktab(G), + case catch searchTk(S,IR_ID,typedef) of + {value,TK} -> + TK; + _ -> %% false / exit + case catch searchTk(S,IR_ID,struct) of + {value,TK} -> + TK; + _ -> %% false / exit + case catch searchTk(S,IR_ID,union) of + {value,TK} -> + TK; + _ -> + undefined + end + end + end. + + +searchTk(S,IR_ID,Type) -> + L = lists:flatten(ets:match(S,{'_',Type,'$1','_'})), + case lists:keysearch(IR_ID,2,L) of + {value,TK} -> + {value,TK}; + false -> + searchInsideTks(L,IR_ID) + end. + + +searchInsideTks([],_IR_ID) -> + false; +searchInsideTks([{tk_array,TK,_}|Xs],IR_ID) -> + case searchIncludedTk(TK,IR_ID) of + {value,TK} -> + {value,TK}; + false -> + searchInsideTks(Xs,IR_ID) + end. + + +searchIncludedTk({tk_array,TK,_},IR_ID) -> + searchIncludedTk(TK,IR_ID); +searchIncludedTk({tk_sequence,TK,_},IR_ID) -> + searchIncludedTk(TK,IR_ID); +searchIncludedTk(TK,_IR_ID) when is_atom(TK) -> + false; +searchIncludedTk(TK,IR_ID) -> + case element(2,TK) == IR_ID of + true -> + {value,TK}; + false -> + false + end. + + + + + + + + + + + diff --git a/lib/ic/src/ic_file.erl b/lib/ic/src/ic_file.erl new file mode 100644 index 0000000000..6a99d6cfde --- /dev/null +++ b/lib/ic/src/ic_file.erl @@ -0,0 +1,447 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_file). + +-include_lib("ic/src/ic.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([filename_push/4, filename_pop/2, open/2, close/1, remove_ext/1, join/2, + add_dot_erl/1, add_dot_hrl/1, add_dot_c/1, add_dot_h/1, add_dot_java/1, + add_dot_idl/1, javaInterfaceFilePush/3, javaInterfaceFilePop/1, + createDirectory/2, createJavaDirectory/2, open_java_file/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: filename_push +%% +%% Pushes a file name, can also push ignore in which case means that +%% no files should ever be opened at this scope. Note that empty in +%% the file descriptor entries means that the file just isn't open +%% yet. +%%----------------------------------------------------------------- +filename_push(G, _N, ignore, _) -> + G#genobj{stubfile=[ignore | G#genobj.stubfile], + stubfiled=[ignore | G#genobj.stubfiled], + skelfile=[ignore | G#genobj.skelfile], + skelfiled=[ignore | G#genobj.skelfiled], + includefile=[ignore | G#genobj.includefile], + includefiled=[ignore | G#genobj.includefiled]}; + +filename_push(G, N, X, Lang) -> + Fullname = [ic_forms:get_id2(X) | N], + EName0 = ic_util:to_undersc(Fullname), + + DoGen = ic_genobj:do_gen(G), + + ImplName = find_impl_name(G, Fullname), + + {StubName, EName} = + case Lang of + erlang -> + {join(ic_options:get_opt(G, stubdir), add_dot_erl(EName0)), + EName0}; + erlang_template -> + {join(ic_options:get_opt(G, stubdir), add_dot_erl(ImplName)), + ImplName}; + c -> + {join(ic_options:get_opt(G, stubdir), add_dot_c(EName0)), + EName0}; + c_server -> + {join(ic_options:get_opt(G, stubdir), add_dot_c(EName0++"__s")), + EName0}; + erlang_template_no_gen -> + {undefined, EName0}; + erlang_no_stub -> + {undefined, EName0}; + c_no_stub -> + {undefined, EName0}; + c_server_no_stub -> + {undefined, EName0} + end, + Stub = if DoGen==true -> + case StubName of + undefined -> + ignore; + _ -> + ic_codegen:emit_stub_head(G, open(empty, StubName), EName, Lang) + end; + true -> ignore end, + + HrlName = case Lang of + erlang_template -> + ignore; + erlang_template_no_gen -> + ignore; + erlang -> + ?ifopt2(G, gen_hrl, + join(ic_options:get_opt(G, stubdir), add_dot_hrl(EName)), + ignore); + c -> + ?ifopt2(G, gen_hrl, + join(ic_options:get_opt(G, stubdir), add_dot_h(EName)), + ignore); + c_server -> + ?ifopt2(G, gen_hrl, + join(ic_options:get_opt(G, stubdir), + add_dot_h(EName++"__s")), + ignore); + erlang_no_stub -> + ?ifopt2(G, gen_hrl, + join(ic_options:get_opt(G, stubdir), add_dot_hrl(EName)), + ignore); + c_no_stub -> + ?ifopt2(G, gen_hrl, + join(ic_options:get_opt(G, stubdir), add_dot_h(EName)), + ignore); + c_server_no_stub -> + ?ifopt2(G, gen_hrl, + join(ic_options:get_opt(G, stubdir), + add_dot_h(EName++"__s")), + ignore) + end, + Hrl = if DoGen==true -> + case Lang of + erlang_template -> + ignore; + erlang_template_no_gen -> + ignore; + erlang_no_stub -> + ic_codegen:emit_hrl_head(G, open(empty, HrlName), + EName, erlang); + c_no_stub -> + ic_codegen:emit_hrl_head(G, open(empty, HrlName), + EName, c); + c_server_no_stub -> + ic_codegen:emit_hrl_head(G, open(empty, HrlName), + EName, c_server); + _ -> + ic_codegen:emit_hrl_head(G, open(empty, HrlName), + EName, Lang) + end; + true -> ignore end, + + G#genobj{impl=ImplName, + stubfile=[StubName | G#genobj.stubfile], + stubfiled=[Stub | G#genobj.stubfiled], + includefile=[HrlName | G#genobj.includefile], + includefiled=[Hrl | G#genobj.includefiled]}. + +%%----------------------------------------------------------------- +%% Func: join/2 +%% +%% Special version of filename join. +%%----------------------------------------------------------------- +join([], File) -> + File; +join(Path, File) -> + filename:join(Path, File). + + +%%----------------------------------------------------------------- +%% Func: filename_pop/2 +%%----------------------------------------------------------------- +filename_pop(G, Lang) -> +%% io:format("Popped file names: ~p~n", [hd(G#genobj.stubfile)]), +%% case is_skelfile_open(G) of +%% true -> emit_skel_footer(G); +%% false -> ok end, +%% close(hd(G#genobj.skelfiled)), + close(hd(G#genobj.stubfiled)), + ic_codegen:emit_hrl_foot(G, Lang), + close(hd(G#genobj.includefiled)), + G#genobj{stubfile=tl(G#genobj.stubfile), + stubfiled=tl(G#genobj.stubfiled), +%% skelfile=tl(G#genobj.skelfile), +%% skelfiled=tl(G#genobj.skelfiled), + includefile=tl(G#genobj.includefile), + includefiled=tl(G#genobj.includefiled)}. + + + +%%----------------------------------------------------------------- +%% Func: javaInterfaceFilePush/3 +%%----------------------------------------------------------------- +javaInterfaceFilePush(G, N, X) -> + Name = ic_forms:get_java_id(X), + {InterfaceFd, InterfaceFileName} = open_java_file(G, N, Name), + + StubClassName = "_" ++ Name ++ "Stub", + {StubFd, StubFileName} = open_java_file(G, N, StubClassName), + + SkelClassName = "_" ++ Name ++ "ImplBase", + {SkelFd, SkelFileName} = open_java_file(G, N, SkelClassName), + + HelperClassName = Name ++ "Helper", + {HelperFd, HelperFileName} = open_java_file(G, N, HelperClassName), + + HolderClassName = Name ++ "Holder", + {HolderFd, HolderFileName} = open_java_file(G, N, HolderClassName), + + G#genobj{ + interfacefile=[InterfaceFileName | G#genobj.interfacefile], + interfacefiled=[InterfaceFd | G#genobj.interfacefiled], + stubfile=[StubFileName | G#genobj.stubfile], + stubfiled=[StubFd | G#genobj.stubfiled], + skelfile=[SkelFileName | G#genobj.skelfile], + skelfiled=[SkelFd | G#genobj.skelfiled], + helperfile=[HelperFileName | G#genobj.helperfile], + helperfiled=[HelperFd | G#genobj.helperfiled], + holderfile=[HolderFileName | G#genobj.holderfile], + holderfiled=[HolderFd | G#genobj.holderfiled]}. + + + + + +%%----------------------------------------------------------------- +%% Func: javaInterfaceFilePop/1 +%%----------------------------------------------------------------- +javaInterfaceFilePop(G) -> + close(hd(G#genobj.interfacefiled)), + close(hd(G#genobj.stubfiled)), + close(hd(G#genobj.skelfiled)), + close(hd(G#genobj.helperfiled)), + close(hd(G#genobj.holderfiled)), + G#genobj{ + interfacefile=tl(G#genobj.interfacefile), + interfacefiled=tl(G#genobj.interfacefiled), + stubfile=tl(G#genobj.stubfile), + stubfiled=tl(G#genobj.stubfiled), + skelfile=tl(G#genobj.skelfile), + skelfiled=tl(G#genobj.skelfiled), + helperfile=tl(G#genobj.helperfile), + helperfiled=tl(G#genobj.helperfiled), + holderfile=tl(G#genobj.holderfile), + holderfiled=tl(G#genobj.holderfiled)}. + +%%----------------------------------------------------------------- +%% Func: createDirectory/2 +%%----------------------------------------------------------------- +createDirectory(_G, []) -> + ok; +createDirectory(G, Scope) -> + Path = ic_file:join(ic_options:get_opt(G, stubdir), ic_pragma:slashify(Scope)), + case file:make_dir(Path) of + ok -> + ok; + {error, eexist} -> + ok; + {error, Reason} -> + ic_error:fatal_error(G, {create_dir, Path, Reason}) + end. + + +%%----------------------------------------------------------------- +%% Func: createJavaDirectory/2 +%%----------------------------------------------------------------- +createJavaDirectory(_G, []) -> + ok; +createJavaDirectory(G, Scope) -> + JavaScope = ic_util:adjustScopeToJava(G,Scope), + Path = ic_file:join(ic_options:get_opt(G, stubdir), ic_pragma:slashify(JavaScope)), + case file:make_dir(Path) of + ok -> + ok; + {error, eexist} -> + ok; + {error, Reason} -> + ic_error:fatal_error(G, {create_dir, Path, Reason}) + end. + + + + +%%----------------------------------------------------------------- +%% Func: createJavaFileName/3 +%%----------------------------------------------------------------- +createJavaFileName(G, Scope, FName) -> + JavaScope = ic_util:adjustScopeToJava(G,Scope), + join(ic_options:get_opt(G, stubdir), + ic_pragma:slashify([FName++".java"|JavaScope])). + +%%----------------------------------------------------------------- +%% Func: close/2 (used to be file_close) +%%----------------------------------------------------------------- +close(empty) -> ok; +close(ignore) -> ok; +close(Fd) -> + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: remove_ext/1 +%%----------------------------------------------------------------- +remove_ext(File) -> + filename:rootname(filename:basename(File)). + +%%----------------------------------------------------------------- +%% Func: open/2 (used to be file_open) +%%----------------------------------------------------------------- +open(_, ignore) -> ignore; +open(empty, Name) -> + case file:open(Name, [raw, binary, write]) of + {ok, Fd} -> + Fd; + {error, Reason} -> + exit({error, Reason}) +%% ic_error:fatal_error(G, {open_file, Name, Reason}) + end. + +%%----------------------------------------------------------------- +%% Func: open_java_file/3 +%%----------------------------------------------------------------- +open_java_file(G, N, Name) -> + createJavaDirectory(G, N), + FName = createJavaFileName(G, N, Name), + case file:open(FName, [raw, binary, write]) of + {ok, Fd} -> + ic_codegen:emit_stub_head(G, Fd, Name, java), + emit_package(G, N, Fd), + {Fd, FName}; + {error, Reason} -> + ic_error:fatal_error(G, {open_file, FName, Reason}) + end. + +%%----------------------------------------------------------------- +%% Func: emit_package/3 +%%----------------------------------------------------------------- +emit_package(_G, [], _Fd) -> + ok; +emit_package(G, N, Fd) -> + ic_codegen:emit(Fd, "package ~s;\n", [ic_util:to_dot(G,N)]), + ic_codegen:nl(Fd). + +%%----------------------------------------------------------------- +%% Func: add_dot_erl/1 +%%----------------------------------------------------------------- +add_dot_erl(F) -> + File = ic_util:to_list(F), + F2 = lists:reverse(File), + case F2 of + [$l, $r, $e, $. | _Rest] -> + File; + _ -> + File ++ ".erl" + end. + +%%----------------------------------------------------------------- +%% Func: add_dot_hrl/1 +%%----------------------------------------------------------------- +add_dot_hrl(F) -> + File = ic_util:to_list(F), + F2 = lists:reverse(File), + case F2 of + [$l, $r, $h, $. | _Rest] -> + File; + _ -> + File ++ ".hrl" + end. + +%%----------------------------------------------------------------- +%% Func: add_dot_c/1 +%%----------------------------------------------------------------- +add_dot_c(F) -> + File = ic_util:to_list(F), + F2 = lists:reverse(File), + case F2 of + [$c, $. | _Rest] -> + File; + _ -> + File ++ ".c" + end. + +%%----------------------------------------------------------------- +%% Func: add_dot_h/1 +%%----------------------------------------------------------------- +add_dot_h(F) -> + File = ic_util:to_list(F), + F2 = lists:reverse(File), + case F2 of + [$h, $. | _Rest] -> + File; + _ -> + File ++ ".h" + end. + +%%----------------------------------------------------------------- +%% Func: add_dot_java/1 +%%----------------------------------------------------------------- +add_dot_java(F) -> + File = ic_util:to_list(F), + F2 = lists:reverse(File), + case F2 of + [$a, $v, $a, $j, $. | _Rest] -> + File; + _ -> + File ++ ".java" + end. + +%%----------------------------------------------------------------- +%% Func: add_dot_idl/1 +%%----------------------------------------------------------------- +add_dot_idl(F) -> + File = ic_util:to_list(F), + F2 = lists:reverse(File), + case F2 of + [$l, $d, $i, $. | _Rest] -> + File; + _ -> + File ++ ".idl" + end. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% +%% File handling stuff +%% +%% +%% Shall open a file for writing. Also sets up the generator with +%% usefull bits of information +%% +%%-------------------------------------------------------------------- +find_impl_name(G, Name) -> + N1 = ic_util:to_colon(Name), + N2 = ic_util:to_undersc(Name), + case {ic_options:get_opt(G, {impl, N1}), + ic_options:get_opt(G, {impl, N2})} of + {false, false} -> + case {ic_options:get_opt(G, {impl, "::"++N1}), + ic_options:get_opt(G, {impl, N2})} of + {false, false} -> N2 ++ "_impl"; + {X, _Y} when X /= false -> ic_util:to_list(X); + {_X, Y} when Y /= false -> ic_util:to_list(Y) + end; + {X, _Y} when X /= false -> ic_util:to_list(X); + {_X, Y} when Y /= false -> ic_util:to_list(Y) + end. diff --git a/lib/ic/src/ic_forms.erl b/lib/ic/src/ic_forms.erl new file mode 100644 index 0000000000..7409ddeb7b --- /dev/null +++ b/lib/ic/src/ic_forms.erl @@ -0,0 +1,437 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_forms). + +-include_lib("ic/src/ic.hrl"). +-include_lib("ic/src/icforms.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([get_id/1, get_id2/1, get_java_id/1, get_line/1]). +-export([get_type_code/3, search_tk/2, clean_up_scope/1]). +-export([get_body/1, get_dimension/1, get_idlist/1, get_type/1, get_tk/1, is_oneway/1]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% +%% Generation go-get utilities +%% +%% Feeble attempt at virtual funtions. +%% +%%-------------------------------------------------------------------- + +get_dimension(X) when is_record(X, array) -> + [element(3, L) || L <- X#array.size]. + +%% Should find the name hidden in constructs +get_id( [{'<identifier>', _LineNo, Id}] ) -> Id; +get_id( {'<identifier>', _LineNo, Id} ) -> Id; +get_id(Id) when is_list(Id) andalso is_integer(hd(Id)) -> Id; +get_id(X) when is_record(X, scoped_id) -> X#scoped_id.id; +get_id(X) when is_record(X, array) -> get_id(X#array.id); +get_id( {'<string_literal>', _LineNo, Id} ) -> Id; +get_id( {'<wstring_literal>', _LineNo, Id} ) -> Id. + +get_line([{'<identifier>', LineNo, _Id}]) -> LineNo; +get_line({'<identifier>', LineNo, _Id}) -> LineNo; +get_line(X) when is_record(X, scoped_id) -> X#scoped_id.line; +get_line(X) when is_record(X, module) -> get_line(X#module.id); +get_line(X) when is_record(X, interface) -> get_line(X#interface.id); +get_line(X) when is_record(X, forward) -> get_line(X#forward.id); +get_line(X) when is_record(X, const) -> get_line(X#const.id); +get_line(X) when is_record(X, typedef) -> get_line(X#typedef.id); +get_line(X) when is_record(X, struct) -> get_line(X#struct.id); +get_line(X) when is_record(X, member) -> get_line(X#member.id); +get_line(X) when is_record(X, union) -> get_line(X#union.id); +get_line(X) when is_record(X, case_dcl) -> get_line(X#case_dcl.id); +get_line(X) when is_record(X, enum) -> get_line(X#enum.id); +get_line(X) when is_record(X, enumerator) -> get_line(X#enumerator.id); +get_line(X) when is_record(X, array) -> get_line(X#array.id); +get_line(X) when is_record(X, attr) -> get_line(X#attr.id); +get_line(X) when is_record(X, except) -> get_line(X#except.id); +get_line(X) when is_record(X, op) -> get_line(X#op.id); +get_line(X) when is_record(X, param) -> get_line(X#param.id); +get_line(X) when is_record(X, id_of) -> get_line(X#id_of.id); + +get_line({'or', T1, _T2}) -> get_line(T1); +get_line({'xor', T1, _T2}) -> get_line(T1); +get_line({'and', T1, _T2}) -> get_line(T1); +get_line({'rshift', T1, _T2}) ->get_line(T1); +get_line({'lshift', T1, _T2}) ->get_line(T1); +get_line({'+', T1, _T2}) -> get_line(T1); +get_line({'-', T1, _T2}) -> get_line(T1); +get_line({'*', T1, _T2}) -> get_line(T1); +get_line({'/', T1, _T2}) -> get_line(T1); +get_line({'%', T1, _T2}) -> get_line(T1); +get_line({{'-', _Line}, T}) -> get_line(T); +get_line({{'+', _Line}, T}) -> get_line(T); +get_line({{'~', _Line}, T}) -> get_line(T); +get_line({_, X, _}) when is_integer(X) -> X; +get_line({_A, N}) when is_integer(N) -> N; +get_line(_) -> -1. + + +%%-------------------------------------------------------------------- +%% +%% High level get functions. +%% +%% These are highly polymorphic functions that will get the id, +%% body and type of a record (those records output from the +%% parser). +%% +%% NOTE: The typedef node (the alias) is special, because the type +%% field is a type definition and therefore considered a body, +%% and the type of a typedef is its name. +%% + +get_id2(X) when is_record(X, module) -> get_id(X#module.id); +get_id2(X) when is_record(X, interface) -> get_id(X#interface.id); +get_id2(X) when is_record(X, forward) -> get_id(X#forward.id); +get_id2(X) when is_record(X, const) -> get_id(X#const.id); +get_id2(X) when is_record(X, typedef) -> get_id(hd(X#typedef.id)); +get_id2(X) when is_record(X, struct) -> get_id(X#struct.id); +get_id2(X) when is_record(X, member) -> get_id(hd(X#member.id)); +get_id2(X) when is_record(X, union) -> get_id(X#union.id); +get_id2(X) when is_record(X, case_dcl) -> get_id(X#case_dcl.id); +get_id2(X) when is_record(X, enum) -> get_id(X#enum.id); +get_id2(X) when is_record(X, enumerator) -> get_id(X#enumerator.id); +get_id2(X) when is_record(X, array) -> get_id(X#array.id); +get_id2(X) when is_record(X, attr) -> get_id(X#attr.id); +get_id2(X) when is_record(X, except) -> get_id(X#except.id); +get_id2(X) when is_record(X, op) -> get_id(X#op.id); +get_id2(X) when is_record(X, param) -> get_id(X#param.id); +get_id2(X) when is_record(X, type_dcl) -> get_id2(X#type_dcl.type); +get_id2(X) when is_record(X, scoped_id) -> ic_symtab:scoped_id_strip(X); +get_id2(X) when is_record(X, preproc) -> get_id(X#preproc.id); +get_id2(X) when is_record(X, id_of) -> get_id2(X#id_of.id); +get_id2(X) -> get_id(X). + +get_body(X) when is_record(X, module) -> X#module.body; +get_body(X) when is_record(X, interface) -> X#interface.body; +get_body(X) when is_record(X, struct) -> X#struct.body; +get_body(X) when is_record(X, union) -> X#union.body; +get_body(X) when is_record(X, enum) -> X#enum.body; +get_body(X) when is_record(X, typedef) -> X#typedef.type; % See Note +get_body(X) when is_record(X, except) -> X#except.body. + +get_type(X) when is_record(X, const) -> X#const.type; +get_type(X) when is_record(X, type_dcl) -> X#type_dcl.type; +get_type(X) when is_record(X, typedef) -> X#typedef.id; % See Note +get_type(X) when is_record(X, member) -> X#member.type; +get_type(X) when is_record(X, union) -> X#union.type; +get_type(X) when is_record(X, case_dcl) -> X#case_dcl.type; +get_type(X) when is_record(X, sequence) -> X#sequence.type; +get_type(X) when is_record(X, attr) -> X#attr.type; +get_type(X) when is_record(X, op) -> X#op.type; +get_type(X) when is_record(X, param) -> X#param.type. +%%get_type(X) when record(X, id_of) -> get_type(X#id_of.type). + +%% Temporary place +get_tk(X) when is_record(X, interface) -> X#interface.tk; +get_tk(X) when is_record(X, forward) -> X#forward.tk; +get_tk(X) when is_record(X, const) -> X#const.tk; +get_tk(X) when is_record(X, type_dcl) -> X#type_dcl.tk; +get_tk(X) when is_record(X, typedef) -> X#typedef.tk; +get_tk(X) when is_record(X, struct) -> X#struct.tk; +get_tk(X) when is_record(X, union) -> X#union.tk; +get_tk(X) when is_record(X, enum) -> X#enum.tk; +get_tk(X) when is_record(X, attr) -> X#attr.tk; +get_tk(X) when is_record(X, except) -> X#except.tk; +get_tk(X) when is_record(X, op) -> X#op.tk; +get_tk(X) when is_record(X, id_of) -> X#id_of.tk; +get_tk(X) when is_record(X, param) -> X#param.tk. + + +%% Get idlist returns the list of identifiers found in typedefs, case +%% dcls etc. +get_idlist(X) when is_record(X, typedef) -> X#typedef.id; +get_idlist(X) when is_record(X, member) -> X#member.id; +get_idlist(X) when is_record(X, case_dcl) -> X#case_dcl.label; +get_idlist(X) when is_record(X, attr) -> X#attr.id. + + +is_oneway(X) when is_record(X, op) -> + case X#op.oneway of + {oneway, _} -> true; + _ -> false + end; +is_oneway(_X) -> false. + + + + + +%%------------------------------------------------------------ +%% +%% Analyze the record and seek the correct type code. +%% +%% NOT equal to get_tk, this will always succed ! +%% +%%------------------------------------------------------------ +get_type_code(G, N, X) -> + case get_type_code2(G, N, X) of + undefined -> + %% Remove "Package" suffix from scope + N2 = clean_up_scope(N), + search_tk(G,ictk:get_IR_ID(G, N2, X)); + TC -> + TC + end. + +clean_up_scope(N) -> + clean_up_scope(N,[]). + +clean_up_scope([],N) -> + lists:reverse(N); +clean_up_scope([N|Ns],Found) -> + case lists:suffix("Package",N) of + true -> + Len = length(N), + case Len > 7 of + true -> + N2 = string:substr(N,1,Len-7), + clean_up_scope(Ns,[N2|Found]); + false -> + clean_up_scope(Ns,[N|Found]) + end; + false -> + clean_up_scope(Ns,[N|Found]) + end. + + +get_type_code2(_, _, X) when is_record(X, interface) -> X#interface.tk; +get_type_code2(_, _, X) when is_record(X, forward) -> X#forward.tk; +get_type_code2(_, _, X) when is_record(X, const) -> X#const.tk; +get_type_code2(_, _, X) when is_record(X, type_dcl) -> X#type_dcl.tk; +get_type_code2(_, _, X) when is_record(X, typedef) -> + Id = X#typedef.id, + ET = X#typedef.tk, + if is_list(Id) -> + Head = hd(Id), + if is_tuple(Head) -> + case element(1,Head) of + array -> + get_array_tc(ET, element(3,Head)); + _ -> + ET + end; + true -> + ET + end; + true -> + ET + end; + +get_type_code2(_, _, X) when is_record(X, struct) -> X#struct.tk; +get_type_code2(_, _, X) when is_record(X, union) -> X#union.tk; +get_type_code2(_, _, X) when is_record(X, enum) -> X#enum.tk; +get_type_code2(_, _, X) when is_record(X, attr) -> X#attr.tk; +get_type_code2(_, _, X) when is_record(X, except) -> X#except.tk; +get_type_code2(_, _, X) when is_record(X, op) -> X#op.tk; +get_type_code2(_, _, X) when is_record(X, id_of) -> X#id_of.tk; +get_type_code2(_, _, X) when is_record(X, param) -> X#param.tk; + +get_type_code2(G, N, X) when is_record(X, member) -> + ET = get_type_code(G, N, element(2,X)), + Id = element(3,X), + + if is_list(Id) -> + Head = hd(Id), + if is_tuple(Head) -> + case element(1,Head) of + array -> + get_array_tc(ET, element(3,Head)); + _ -> + ET + end; + true -> + ET + end; + true -> + ET + end; + +get_type_code2(G, N, X) when is_record(X, scoped_id) -> + element(3,ic_symtab:get_full_scoped_name(G, N, X)); + +get_type_code2(G, N, X) when is_record(X, sequence) -> + if is_tuple(X#sequence.length) -> + {tk_sequence, + get_type_code(G, N, X#sequence.type), + list_to_integer(element(3,X#sequence.length))}; + true -> + {tk_sequence, + get_type_code(G, N, X#sequence.type), + X#sequence.length} + end; + +get_type_code2(_G, _N, {unsigned,{short,_}}) -> tk_ushort; + +get_type_code2(_G, _N, {unsigned,{long,_}}) -> tk_ulong; + +get_type_code2(_G, _N, {unsigned,{'long long',_}}) -> tk_ulonglong; + +get_type_code2(_G, _N, X) when is_record(X, fixed) -> + {tk_fixed, X#fixed.digits, X#fixed.scale}; + +get_type_code2(G, N, {X,_}) -> + get_type_code2(G, N, X); + +get_type_code2(_, _, short) -> tk_short; +get_type_code2(_, _, long) -> tk_long; +get_type_code2(_, _, 'long long') -> tk_longlong; +get_type_code2(_, _, float) -> tk_float; +get_type_code2(_, _, double) -> tk_double; +get_type_code2(_, _, boolean) -> tk_boolean; +get_type_code2(_, _, char) -> tk_char; +get_type_code2(_, _, wchar) -> tk_wchar; +get_type_code2(_, _, octet) -> tk_octet; +get_type_code2(_, _, string) -> tk_string; +get_type_code2(_, _, wstring) -> tk_wstring; +get_type_code2(_, _, any) -> tk_any. + + +get_array_tc(ET, []) -> + ET; +get_array_tc(ET, [L|Ls]) -> + {tk_array, + get_array_tc(ET,Ls), + list_to_integer(element(3,L))}. + + + + +%%------------------------------------------------------------ +%% +%% seek type code when not accessible by ic_forms:get_tk/1 ( should be +%% a part of "do_gen" related functions later ) +%% +%%------------------------------------------------------------ +search_tk(G, IR_ID) -> + S = ic_genobj:tktab(G), + case catch search_tk(S,IR_ID,typedef) of + {value,TK} -> + TK; + _ -> %% false / exit + case catch search_tk(S,IR_ID,struct) of + {value,TK} -> + TK; + _ -> %% false / exit + case catch search_tk(S,IR_ID,union) of + {value,TK} -> + TK; + _ -> + undefined + end + end + end. + + +search_tk(S, IR_ID, Type) -> + L = lists:flatten(ets:match(S,{'_',Type,'$1','_'})), + case lists:keysearch(IR_ID,2,L) of + {value,TK} -> + {value,TK}; + false -> + search_inside_tks(L,IR_ID) + end. + + +search_inside_tks([],_IR_ID) -> + false; +search_inside_tks([{tk_array,TK,_}|Xs],IR_ID) -> + case search_included_tk(TK,IR_ID) of + {value,TK} -> + {value,TK}; + false -> + search_inside_tks(Xs,IR_ID) + end. + + +search_included_tk({tk_array,TK,_}, IR_ID) -> + search_included_tk(TK,IR_ID); +search_included_tk({tk_sequence,TK,_}, IR_ID) -> + search_included_tk(TK,IR_ID); +search_included_tk(TK, _IR_ID) when is_atom(TK) -> + false; +search_included_tk(TK, IR_ID) -> + case element(2,TK) == IR_ID of + true -> + {value,TK}; + false -> + false + end. + + + + +%% This is similar to get_id2 but in everything else +%% than a module it will generate an id prefixed +get_java_id(Id) when is_list(Id) -> + case java_keyword_coalition(Id) of + true -> + "_" ++ Id; + false -> + Id + end; +get_java_id(Id_atom) when is_atom(Id_atom) -> + Id = atom_to_list(Id_atom), + case java_keyword_coalition(Id) of + true -> + "_" ++ Id; + false -> + Id + end; +get_java_id(X) -> + Id = get_id2(X), + case java_keyword_coalition(Id) of + true -> + "_" ++ Id; + false -> + Id + end. + +java_keyword_coalition(Id) -> + lists:member(list_to_atom(Id), + [abstract, default, 'if', private, throw, boolean, + do, implements, protected, throws, break, + double, import, public, transient, byte, + else, instanceof, return, 'try', 'case', extends, + int, short, void, 'catch', final, interface, static, + volatile, char, finally, long, super, while, class, + float, native, switch, const, for, new, synchronized, + continue, goto, package, this, true, false]). + + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- diff --git a/lib/ic/src/ic_genobj.erl b/lib/ic/src/ic_genobj.erl new file mode 100644 index 0000000000..afb00eeb19 --- /dev/null +++ b/lib/ic/src/ic_genobj.erl @@ -0,0 +1,244 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_genobj). + + +-include_lib("ic/src/ic.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([new/1, free_table_space/1, process_space/0]). +-export([skelfiled/1, stubfiled/1, hrlfiled/1, includefiled/1]). +-export([interfacefiled/1, helperfiled/1, holderfiled/1]). +-export([is_skelfile_open/1, is_stubfile_open/1, is_hrlfile_open/1]). +-export([include_file/1, include_file_stack/1]). +-export([push_file/2, pop_file/2, sys_file/2]). + +-export([skelscope/1, stubscope/1, impl/1, do_gen/1]). +-export([symtab/1, auxtab/1, tktab/1, pragmatab/1, optiontab/1, typedeftab/1]). +-export([idlfile/1, module/1, set_idlfile/2, set_module/2]). + + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% +%% Initialisation stuff +%% +%% +%% +%%-------------------------------------------------------------------- + + +new(Opts) -> + OptDB = ets:new(options, [set, public]), + Warns = ets:new(warnings, [set, public]), + Aux = ets:new(aux, [set, public]), + Tk = ets:new(tktab, [set, public]), + PragmaTab = ets:new(pragmatab, [bag, public]), + TypeDefTab = ets:new(c_typedeftab, [set, public]), + G = #genobj{options=OptDB, + warnings=Warns, + symtab=ic_symtab:new(), + auxtab=Aux, + tktab=Tk, + pragmatab=PragmaTab, + c_typedeftab=TypeDefTab}, + ic_error:init_errors(G), + ic_options:add_opt(G, default_opts, true), + ic_options:read_cfg(G, Opts), % Read any config files + ic_options:add_opt(G, Opts, true), + ic_symtab:symtab_add_faked_included_types(G), % Add CORBA::<Types> that as if they + % were defined in an included file + case ic_options:get_opt(G, be) of + false -> + DefBE = ic_options:defaultBe(), + case ic_options:get_opt(G, multiple_be) of + false -> + ic_options:add_opt(G, be, DefBE), + G; + List -> + case lists:member(DefBE, List) of + true -> + %% Delete the default be from the list to avoid + %% generating it twice. + NewList = lists:delete(DefBE, List), + ic_options:add_opt(G, multiple_be, NewList), + ic_options:add_opt(G, be, DefBE), + G; + false -> + G + end + end; + _ -> + G + end. + + +%%-------------------------------------------------------------------- +%% +%% Table removal +%% +%% +%% +%%-------------------------------------------------------------------- + + +free_table_space(G) -> + %% Free ets tables + ets:delete(G#genobj.options), + ets:delete(G#genobj.symtab), + ets:delete(G#genobj.warnings), + ets:delete(G#genobj.auxtab), + ets:delete(G#genobj.tktab), + ets:delete(G#genobj.pragmatab), + ets:delete(G#genobj.c_typedeftab), + %% Close file descriptors + close_fd(G#genobj.skelfiled), + close_fd(G#genobj.stubfiled), + close_fd(G#genobj.interfacefiled), + close_fd(G#genobj.helperfiled), + close_fd(G#genobj.holderfiled), + close_fd(G#genobj.includefiled). + +close_fd([]) -> + ok; +close_fd([Fd|Fds]) -> + file_close(Fd), + close_fd(Fds). + +file_close(empty) -> ok; +file_close(ignore) -> ok; +file_close(Fd) -> + file:close(Fd). + + +%%-------------------------------------------------------------------- +%% +%% Process memory usage +%% +%% +%% +%%-------------------------------------------------------------------- + +process_space() -> + Pheap=4*element(2,element(2,lists:keysearch(heap_size,1,process_info(self())))), + Pstack=4*element(2,element(2,lists:keysearch(stack_size,1,process_info(self())))), + io:format("Process current heap = ~p bytes\n",[Pheap]), + io:format("Symbol current stack = ~p bytes\n",[Pstack]), + io:format("-----------------------------------------------\n"), + io:format("Totally used ~p bytes\n\n",[Pheap+Pstack]). + + + + + + +skelfiled(G) -> hd(G#genobj.skelfiled). +stubfiled(G) -> hd(G#genobj.stubfiled). +includefiled(G) -> hd(G#genobj.includefiled). +hrlfiled(G) -> hd(G#genobj.includefiled). +interfacefiled(G) -> hd(G#genobj.interfacefiled). +helperfiled(G) -> hd(G#genobj.helperfiled). +holderfiled(G) -> hd(G#genobj.holderfiled). + +include_file(G) -> hd(G#genobj.includefile). +include_file_stack(G) -> G#genobj.includefile. + +is_skelfile_open(G) -> + if hd(G#genobj.skelfiled) /= empty, hd(G#genobj.skelfiled) /= ignore + -> true; + true -> false + end. +is_stubfile_open(G) -> + if hd(G#genobj.stubfiled) /= empty, hd(G#genobj.stubfiled) /= ignore + -> true; + true -> false + end. + +is_hrlfile_open(G) -> + if hd(G#genobj.includefiled) /= empty, hd(G#genobj.includefiled) /= ignore + -> true; + true -> false + end. + +%%-------------------------------------------------------------------- +%% +%% Handling of pre processor file commands +%% +%%-------------------------------------------------------------------- + +push_file(G, Id) -> + New = G#genobj.filestack+1, + set_idlfile(G, Id), + G#genobj{filestack=New, do_gen=true_or_not(New)}. +pop_file(G, Id) -> + New = G#genobj.filestack-1, + set_idlfile(G, Id), + G#genobj{filestack=New, do_gen=true_or_not(New)}. +sys_file(G, _Id) -> G#genobj{sysfile=true}. + + +do_gen(G) -> G#genobj.do_gen. + +%%-------------------------------------------------------------------- +%% +%% Storage routines +%%i +%% The generator object G is used to store many usefull bits of +%% information so that the information doesn't need to get passed +%% around everywhere. +%% +%%-------------------------------------------------------------------- + + +skelscope(G) -> G#genobj.skelscope. +stubscope(G) -> G#genobj.stubscope. +symtab(G) -> G#genobj.symtab. +auxtab(G) -> G#genobj.auxtab. +tktab(G) -> G#genobj.tktab. +impl(G) -> G#genobj.impl. +pragmatab(G) -> G#genobj.pragmatab. +optiontab(G) -> G#genobj.options. +typedeftab(G) -> G#genobj.c_typedeftab. + +idlfile(G) -> ?lookup(G#genobj.options, idlfile). +module(G) -> ?lookup(G#genobj.options, module). + +set_idlfile(G, X) -> ?insert(G#genobj.options, idlfile, X). +set_module(G, X) -> ?insert(G#genobj.options, module, ic_forms:get_id(X)). + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- +true_or_not(X) when X < 2 -> + true; +true_or_not(_) -> + false. diff --git a/lib/ic/src/ic_java_type.erl b/lib/ic/src/ic_java_type.erl new file mode 100644 index 0000000000..b8979b6dbe --- /dev/null +++ b/lib/ic/src/ic_java_type.erl @@ -0,0 +1,1213 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_java_type). + + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([getType/3, getHolderType/3, + getParamType/4, inlinedTypes/2, + marshalFun/4, unMarshalFun/4, getFullType/4, + getFullType/3, getMarshalType/4, getUnmarshalType/4, + getdim/1]). +-export([isBasicType/3, isBasicType/1]). +-export([isIntegerType/3, isIntegerType/1]). +-export([isTermType/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% Func: getType/3 +%%----------------------------------------------------------------- +getType(G, N, T) when is_record(T, scoped_id) -> + {FullScopedName, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), + BT = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + case BT of + "erlang.pid" -> + ?ICPACKAGE ++ "Pid"; + "erlang.port" -> + ?ICPACKAGE ++ "Port"; + "erlang.ref" -> + ?ICPACKAGE ++ "Ref"; + "erlang.term" -> + ?ICPACKAGE ++ "Term"; + {enum, Type} -> + getType(G, N, Type); + Type -> + case TK of + {tk_array,_,_} -> + tk2type(G,N,T,TK); + {tk_sequence,_,_} -> + tk2type(G,N,T,TK); + tk_any -> + ?ICPACKAGE ++ "Any"; + _ -> + case isBasicType(G,N,TK) of + true -> + tk2type(G,N,T,TK); + false -> + Type %% Other types + end + end + end; + +getType(_G, _N, S) when is_list(S) -> + S; + +getType(_G, _N, T) when is_record(T, string) -> + "java.lang.String"; + +getType(_G, _N, T) when is_record(T, wstring) -> %% WSTRING + "java.lang.String"; + +getType(G, N, T) when is_record(T, struct) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]); + +getType(G, N, T) when is_record(T, union) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]); + +getType(G, N, T) when is_record(T, sequence) -> + getType(G, N, ic_forms:get_type(T)) ++ "[]"; + +getType(G, N, T) when is_record(T, enum) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]); + +%% NOTE i am using the new isJavaElementaryType +%% to avoid members declared as keywords (except +%% all java elementary types) to be used as a +%% class +getType(G, N, T) when is_record(T, member) -> + Type = tk2type(G,N,T,ic_forms:get_type_code(G, N, T)), + case isJavaElementaryType(list_to_atom(Type)) of + true -> + Type; + false -> + Prefix = list_to_atom(lists:flatten(string:tokens(Type,"[]"))), + case isJavaElementaryType(Prefix) of %% Checks if Type is an array + %% of elementary java types + true -> + Type; + false -> + ic_forms:get_java_id(getType(G,N,ic_forms:get_type(T))) ++ + if is_record(hd(T#member.id),array) -> + arrayEmptyDim(hd(T#member.id)); + true -> + "" + end + end + end; + +getType(_G, _N, {boolean, _}) -> + "boolean"; + +getType(_G, _N, {octet, _}) -> + "byte"; + +getType(_G, _N, {void, _}) -> + "void"; + +getType(_G, _N, {unsigned, U}) -> + case U of + {short,_} -> + "short"; + {long,_} -> + "int"; + {'long long',_} -> + "long" + end; + +getType(_G, _N, {char, _}) -> + "char"; + +getType(_G, _N, {wchar, _}) -> %% WCHAR + "char"; + +getType(_G, _N, {short, _}) -> + "short"; + +getType(_G, _N, {long, _}) -> + "int"; + +getType(_G, _N, {'long long', _}) -> + "long"; + +getType(_G, _N, {float, _}) -> + "float"; + +getType(_G, _N, {double, _}) -> + "double"; + +getType(_G, _N, {any, _}) -> + ?ICPACKAGE ++ "Any". + + + + + + +%%----------------------------------------------------------------- +%% Func: getHolderType/3 +%%----------------------------------------------------------------- +getHolderType(G, N, T) when element(1, T) == scoped_id -> + {FullScopedName, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), + BT = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + case BT of + "erlang.pid" -> + ?ICPACKAGE ++ "PidHolder"; + "erlang.port" -> + ?ICPACKAGE ++ "PortHolder"; + "erlang.ref" -> + ?ICPACKAGE ++ "RefHolder"; + "erlang.term" -> + ?ICPACKAGE ++ "TermHolder"; + {enum, Type} -> + getHolderType(G, N, Type); + + Type -> + case TK of + {'tk_struct', _, _, _} -> + Type ++ "Holder"; + + {'tk_union', _, _, _, _, _} -> + Type ++ "Holder"; + + {'tk_array', _ , _} -> + Type ++ "Holder"; + + {'tk_sequence', _ , _} -> + Type ++ "Holder"; + + {'tk_string', _} -> + ?ICPACKAGE ++ "StringHolder"; + + {'tk_wstring', _} -> %% WSTRING + ?ICPACKAGE ++ "StringHolder"; + + {'tk_enum', _, _, _} -> + Type ++ "Holder"; + + 'tk_boolean' -> + ?ICPACKAGE ++ "BooleanHolder"; + + 'tk_octet' -> + ?ICPACKAGE ++ "ByteHolder"; + + 'tk_ushort' -> + ?ICPACKAGE ++ "ShortHolder"; + + 'tk_ulong' -> + ?ICPACKAGE ++ "IntHolder"; + + 'tk_ulonglong' -> %% ULLONG + ?ICPACKAGE ++ "LongHolder"; + + 'tk_short' -> + ?ICPACKAGE ++ "ShortHolder"; + + 'tk_long' -> + ?ICPACKAGE ++ "IntHolder"; + + 'tk_longlong' -> + ?ICPACKAGE ++ "LongHolder"; %% LLONG + + 'tk_float' -> + ?ICPACKAGE ++ "FloatHolder"; + + 'tk_double' -> + ?ICPACKAGE ++ "DoubleHolder"; + + 'tk_char' -> + ?ICPACKAGE ++ "CharHolder"; + + 'tk_wchar' -> %% WCHAR + ?ICPACKAGE ++ "CharHolder"; + + 'tk_any' -> + ?ICPACKAGE ++ "AnyHolder"; + + _ -> + case isBasicType(G,N,TK) of + true -> + %% Faked the type ! + getHolderType(G, N, {list_to_atom(tk2type(G,N,T,TK)), -1}); + false -> + %%io:format("TK = ~p, Type = ~p\n",[TK,Type]), + ic_util:to_dot(G,FullScopedName) ++ "Holder" + end + end + end; + +getHolderType(G, N, S) when is_list(S) -> + ic_util:to_dot(G,[S|N]) ++ "Holder"; + +getHolderType(_G, _N, T) when is_record(T, string) -> + ?ICPACKAGE ++"StringHolder"; + +getHolderType(_G, _N, T) when is_record(T, wstring) -> %% WSTRING + ?ICPACKAGE ++"StringHolder"; + +getHolderType(G, N, T) when is_record(T, struct) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Holder"; + +getHolderType(G, N, T) when is_record(T, union) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Holder"; + +getHolderType(G, N, T) when is_record(T, array) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Holder"; + +getHolderType(G, N, T) when is_record(T, sequence) -> + getType(G, N, ic_forms:get_type(T)) ++ "Holder[]"; + +getHolderType(G, N, T) when is_record(T, enum) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Holder"; + +getHolderType(_G, _N, {boolean, _}) -> + ?ICPACKAGE ++"BooleanHolder"; + +getHolderType(_G, _N, {octet, _}) -> + ?ICPACKAGE ++"ByteHolder"; + +getHolderType(_G, _N, {void, _}) -> + "void"; + +getHolderType(_G, _N, {unsigned, U}) -> + case U of + {short,_} -> + ?ICPACKAGE ++"ShortHolder"; + {long,_} -> + ?ICPACKAGE ++"IntHolder"; + {'long long',_} -> + ?ICPACKAGE ++"LongHolder" + end; + +getHolderType(_G, _N, {char, _}) -> + ?ICPACKAGE ++"CharHolder"; + +getHolderType(_G, _N, {wchar, _}) -> %% WCHAR + ?ICPACKAGE ++"CharHolder"; + +getHolderType(_G, _N, {short, _}) -> + ?ICPACKAGE ++"ShortHolder"; + +getHolderType(_G, _N, {long, _}) -> + ?ICPACKAGE ++"IntHolder"; + +getHolderType(_G, _N, {'long long', _}) -> + ?ICPACKAGE ++"LongHolder"; + +getHolderType(_G, _N, {float, _}) -> + ?ICPACKAGE ++"FloatHolder"; + +getHolderType(_G, _N, {double, _}) -> + ?ICPACKAGE ++"DoubleHolder"; + +getHolderType(_G, _N, {any,_}) -> + ?ICPACKAGE ++ "AnyHolder". + + +%%----------------------------------------------------------------- +%% Func: getParamType/4 +%%----------------------------------------------------------------- +getParamType(G, N, S, in) -> + getType(G, N, S); +getParamType(G, N, S, ret) -> + getType(G, N, S); +getParamType(G, N, S, out) -> + getHolderType(G, N, S); +getParamType(G, N, S, inout) -> + getHolderType(G, N, S). + + +%%----------------------------------------------------------------- +%% Func: getUnmarshalType/4 +%%----------------------------------------------------------------- +getUnmarshalType(G, N, X, T) when element(1, T) == scoped_id -> + {FullScopedName, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), + BT = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + case BT of + "erlang.pid" -> + ?ICPACKAGE ++ "PidHelper"; + "erlang.port" -> + ?ICPACKAGE ++ "PortHelper"; + "erlang.ref" -> + ?ICPACKAGE ++ "RefHelper"; + "erlang.term" -> + ?ICPACKAGE ++ "TermHelper"; + {enum, Type} -> + getUnmarshalType(G, N, X, Type); + Type -> + case TK of + {'tk_struct', _, _, _} -> + Type ++ "Helper"; + + {'tk_union', _, _, _, _, _} -> + Type ++ "Helper"; + + {'tk_sequence', _ , _} -> + Type ++ "Helper"; + + {'tk_array', _ , _} -> + Type ++ "Helper"; + + {'tk_enum', _, _, _} -> + Type ++ "Helper"; + + {'tk_string',_} -> + ?ERLANGPACKAGE ++ "OtpErlangString"; + + {'tk_wstring',_} -> %% WSTRING + ?ERLANGPACKAGE ++ "OtpErlangString"; + + 'tk_char' -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_wchar' -> %% WCHAR + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_octet' -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_ushort' -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_ulong' -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_ulonglong' -> %% ULLONG + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_short' -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_long' -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_longlong' -> %% LLONG + ?ERLANGPACKAGE ++ "OtpErlangLong"; + + 'tk_float' -> + ?ERLANGPACKAGE ++ "OtpErlangDouble"; + + 'tk_double' -> + ?ERLANGPACKAGE ++ "OtpErlangDouble"; + + 'tk_boolean' -> + ?ERLANGPACKAGE ++ "OtpErlangAtom"; + + 'tk_void' -> + ?ERLANGPACKAGE ++ "OtpErlangAtom"; + + 'tk_any' -> + ?ICPACKAGE ++ "AnyHelper"; + + _ -> + case isBasicType(G,N,TK) of + true -> + %% Faked the type ! + getUnmarshalType(G, N, X, {list_to_atom(tk2type(G,N,T,TK)), -1}); + false -> + ic_util:to_dot(G,FullScopedName) ++ "Helper" + end + end + end; + +getUnmarshalType(_G, _N, _X, S) when is_list(S) -> + S ++ "Helper"; + +getUnmarshalType(_G, _N, _X, T) when is_record(T, string) -> + ?ERLANGPACKAGE ++ "OtpErlangString"; + +getUnmarshalType(_G, _N, _X, T) when is_record(T, wstring) -> %% WSTRING + ?ERLANGPACKAGE ++ "OtpErlangString"; + +getUnmarshalType(G, N, _X, T) when is_record(T, struct) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Helper"; + +getUnmarshalType(G, N, _X, T) when is_record(T, union) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Helper"; + +getUnmarshalType(G, N, X, T) when is_record(T, sequence) andalso + is_record(X, member) -> + ic_util:to_dot(G,[ic_forms:get_id2(X)|N]) ++ "Helper"; + +getUnmarshalType(G, N, X, T) when is_record(T, sequence) andalso + is_record(X, case_dcl) -> + ic_util:to_dot(G,[ic_forms:get_id2(X)|N]) ++ "Helper"; + +getUnmarshalType(G, N, X, T) when is_record(T, sequence) -> + getUnmarshalType(G, N, X, ic_forms:get_type(T)) ++ "Helper"; + +getUnmarshalType(G, N, X, T) when is_record(T, array) andalso + is_record(X, case_dcl) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ "Helper"; + +getUnmarshalType(G, N, _X, T) when is_record(T, enum) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ + "Helper"; + +getUnmarshalType(_G, _N, _X, {boolean, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangAtom"; + +getUnmarshalType(_G, _N, _X, {octet, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + +getUnmarshalType(_G, _N, _X, {void, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangAtom"; + +getUnmarshalType(_G, _N, _X, {unsigned, U}) -> + case U of + {short,_} -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + {long,_} -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + {'long long',_} -> + ?ERLANGPACKAGE ++ "OtpErlangLong" + end; + +getUnmarshalType(_G, _N, _X, {char, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + +getUnmarshalType(_G, _N, _X, {wchar, _}) -> %% WCHAR + ?ERLANGPACKAGE ++ "OtpErlangLong"; + +getUnmarshalType(_G, _N, _X, {short, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + +getUnmarshalType(_G, _N, _X, {long, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + +getUnmarshalType(_G, _N, _X, {'long long', _}) -> + ?ERLANGPACKAGE ++ "OtpErlangLong"; + +getUnmarshalType(_G, _N, _X, {float, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangDouble"; + +getUnmarshalType(_G, _N, _X, {double, _}) -> + ?ERLANGPACKAGE ++ "OtpErlangDouble"; + +getUnmarshalType(_G, _N, _X, {any, _}) -> + ?ICPACKAGE ++ "AnyHelper". + +%%----------------------------------------------------------------- +%% Func: getMarshalType/4 +%%----------------------------------------------------------------- +getMarshalType(G, N, X, T) when element(1, T) == scoped_id -> + {FullScopedName, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), + BT = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + case BT of + "erlang.pid" -> + ?ICPACKAGE ++ "PidHelper"; + "erlang.port" -> + ?ICPACKAGE ++ "PortHelper"; + "erlang.ref" -> + ?ICPACKAGE ++ "RefHelper"; + "erlang.term" -> + ?ICPACKAGE ++ "TermHelper"; + {enum, Type} -> + getMarshalType(G, N, X, Type); + Type -> + case TK of + {'tk_struct', _, _, _} -> + Type ++ "Helper"; + + {'tk_union', _, _, _, _, _} -> + Type ++ "Helper"; + + {'tk_array', _ , _} -> + Type ++ "Helper"; + + {'tk_sequence', _ , _} -> + Type ++ "Helper"; + + {'tk_enum', _, _, _} -> + Type ++ "Helper"; + + {'tk_string',_} -> + "string"; + + {'tk_wstring',_} -> %% WSTRING + "string"; + + 'tk_char' -> + "char"; + + 'tk_wchar' -> %% WCHAR + "char"; + + 'tk_octet' -> + "byte"; + + 'tk_ushort' -> + "ushort"; + + 'tk_ulong' -> + "uint"; + + 'tk_ulonglong' -> %% ULLONG + "ulong"; + + 'tk_short' -> + "short"; + + 'tk_long' -> + "int"; + + 'tk_longlong' -> %% LLONG + "long"; + + 'tk_float' -> + "float"; + + 'tk_double' -> + "double"; + + 'tk_boolean' -> + "boolean"; + + 'tk_void' -> + "atom"; + + 'tk_any' -> + ?ICPACKAGE ++ "AnyHelper"; + + _ -> + case isBasicType(G,N,TK) of + true -> + %% Faked the type ! + getMarshalType(G, N, X, {list_to_atom(tk2type(G,N,T,TK)), -1}); + false -> + ic_util:to_dot(G,FullScopedName) ++ "Helper" + end + end + end; + +getMarshalType(_G, _N, _X, S) when is_list(S) -> + S ++ "Helper"; + +getMarshalType(_G, _N, _X, T) when is_record(T, string) -> + "string"; + +getMarshalType(_G, _N, _X, T) when is_record(T, wstring) -> %% WSTRING + "string"; + +getMarshalType(G, N, _X, T) when is_record(T, struct) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ + "Helper"; + +getMarshalType(G, N, _X, T) when is_record(T, union) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ + "Helper"; + +getMarshalType(G, N, X, T) when is_record(T, array) andalso + is_record(X, case_dcl) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ + "Helper"; + +getMarshalType(G, N, X, T) when is_record(T, sequence) andalso + is_record(X, member) -> + ic_util:to_dot(G,[ic_forms:get_id2(X)|N]) ++ + "Helper"; + +getMarshalType(G, N, _X, T) when is_record(T, sequence) -> + getType(G, N, ic_forms:get_type(T)) ++ + "Helper"; + +getMarshalType(G, N, _X, T) when is_record(T, enum) -> + ic_util:to_dot(G,[ic_forms:get_id2(T)|N]) ++ + "Helper"; + +getMarshalType(_G, _N, _X, {boolean, _}) -> + "boolean"; + +getMarshalType(_G, _N, _X, {octet, _}) -> + "byte"; + +getMarshalType(_G, _N, _X, {void, _}) -> + ""; % <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +getMarshalType(_G, _N, _X, {unsigned, U}) -> + case U of + {short,_} -> + "ushort"; + {long,_} -> + "uint"; + {'long long',_} -> + "ulong" + end; + +getMarshalType(_G, _N, _X, {short, _}) -> + "short"; +getMarshalType(_G, _N, _X, {long, _}) -> + "int"; +getMarshalType(_G, _N, _X, {'long long', _}) -> + "long"; +getMarshalType(_G, _N, _X, {float, _}) -> + "float"; +getMarshalType(_G, _N, _X, {double, _}) -> + "double"; +getMarshalType(_G, _N, _X, {char, _}) -> + "char"; +getMarshalType(_G, _N, _X, {wchar, _}) -> %% WCHAR + "char"; +getMarshalType(_G, _N, _X, {any, _}) -> + ?ICPACKAGE ++ "AnyHelper". + + + + +%%----------------------------------------------------------------- +%% Func: unMarshalFun/4 +%%----------------------------------------------------------------- +unMarshalFun(G, N, X, T) when element(1, T) == scoped_id -> + {FullScopedName, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), + BT = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + case BT of + "erlang.pid" -> + ".read_pid()"; + "erlang.port" -> + ".read_port()"; + "erlang.ref" -> + ".read_ref()"; + "erlang.term" -> + ".read_term()"; + {enum, Type} -> + unMarshalFun(G, N, X, Type); + _Type -> + case isBasicType(G,N,TK) of + true -> + case TK of + {'tk_string',_} -> + ".read_string()"; + + {'tk_wstring',_} -> %% WSTRING + ".read_string()"; + + 'tk_boolean' -> + ".read_boolean()"; + + 'tk_octet' -> + ".read_byte()"; + + 'tk_ushort' -> + ".read_ushort()"; + + 'tk_ulong' -> + ".read_uint()"; + + 'tk_ulonglong' -> %% ULLONG + ".read_ulong()"; + + 'tk_short' -> + ".read_short()"; + + 'tk_long' -> + ".read_int()"; + + 'tk_longlong' -> %% LLONG + ".read_long()"; + + 'tk_float' -> + ".read_float()"; + + 'tk_double' -> + ".read_double()"; + + 'tk_char' -> + ".read_char()"; + + 'tk_wchar' -> %% WCHAR + ".read_char()"; + + _ -> + %% Faked the type ! + unMarshalFun(G, N, X, {list_to_atom(tk2type(G,N,X,TK)), -1}) + end; + false -> + ".unmarshal()" + end + end; + +unMarshalFun(_G, _N, _X, S) when is_list(S) -> + ".unmarshal()"; + +unMarshalFun(_G, _N, _X, T) when is_record(T, string) -> + ".read_string()"; + +unMarshalFun(_G, _N, _X, T) when is_record(T, wstring) -> %% WSTRING + ".read_string()"; + +unMarshalFun(_G, _N, _X, T) when is_record(T, struct) -> + ".unmarshal((" ++ ?ERLANGPACKAGE ++ "OtpErlangTuple)"; + +unMarshalFun(_G, _N, _X, T) when is_record(T, union) -> + ".unmarshal((" ++ ?ERLANGPACKAGE ++ "OtpErlangTuple)"; + +unMarshalFun(_G, _N, _X, T) when is_record(T, sequence) -> + ".unmarshal((" ++ ?ERLANGPACKAGE ++ "OtpErlanglist)"; + +unMarshalFun(_G, _N, _X, T) when is_record(T, enum) -> + ".unmarshal((" ++ ?ERLANGPACKAGE ++ "OtpErlangAtom)"; + +unMarshalFun(_G, _N, _X, {boolean, _}) -> + ".read_boolean()"; + +unMarshalFun(_G, _N, _X, {octet, _}) -> + ".read_byte()"; + +unMarshalFun(_G, _N, _X, {void, _}) -> + ""; + +unMarshalFun(_G, _N, _X, {unsigned, U}) -> + case U of + {short,_} -> + ".read_ushort()"; + {long,_} -> + ".read_uint()"; + {'long long',_} -> + ".read_ulong()" + end; + +unMarshalFun(_G, _N, _X, {short, _}) -> + ".read_short()"; +unMarshalFun(_G, _N, _X, {long, _}) -> + ".read_int()"; +unMarshalFun(_G, _N, _X, {'long long', _}) -> + ".read_long()"; +unMarshalFun(_G, _N, _X, {float, _}) -> + ".read_float()"; +unMarshalFun(_G, _N, _X, {double, _}) -> + ".read_double()"; +unMarshalFun(_G, _N, _X, {char, _}) -> + ".read_char()"; +unMarshalFun(_G, _N, _X, {wchar, _}) -> %% WCHAR + ".read_char()". + + + + + +%%----------------------------------------------------------------- +%% Func: getFullType/4 - /3 +%% +%% Note : Similar to the getType/3 with the major difference +%% thet on arrays and sequences it will also declare +%% their sizes. Used for "new" declarations +%% +%%----------------------------------------------------------------- + + +getFullType(G, N, X, T) when is_record(X, typedef) andalso is_record(T, array) -> + FullDim = + tk2FullType(G,N,X,ic_forms:get_tk(X)) ++ + getFullDim(G,N,T#array.size), + fixArrayDims(FullDim); + +getFullType(G, N, X, T) when is_record(X, member) andalso is_record(T, array) -> + FullDim = + getFullType(G, N, ic_forms:get_type(X)) ++ + getFullDim(G,N,T#array.size), + fixArrayDims(FullDim); + +getFullType(G, N, X, T) when is_record(X, case_dcl) andalso is_record(T, array) -> + FullDim = + getFullType(G, N, ic_forms:get_type(X)) ++ + getFullDim(G,N,T#array.size), + fixArrayDims(FullDim); + +getFullType(G, N, _X, T) -> + getFullType(G, N, T). + + + +getFullType(G, N, T) when is_record(T, scoped_id) -> + {FullScopedName, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), + case TK of + {tk_array,_,_} -> + tk2FullType(G,N,T,TK); + {tk_sequence,_,_} -> + tk2FullType(G,N,T,TK); + _ -> + case isBasicType(G,N,TK) of + true -> + tk2FullType(G,N,T,TK); + false -> + %% Other types + ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)) + end + end; + +getFullType(G, N, T) when is_record(T, sequence) -> + fixSeqDims(getType(G,N,T),"_length"); + +getFullType(G, N, T) -> + getType(G, N, T). + + + +%% In order to make a legal declaration +%% of an assignable array, the dimensions +%% of empty array sequences are swifted to +%% the end of the type +fixArrayDims(Cs) -> + fixArrayDims(Cs,[],[]). + +fixArrayDims([],Fulls,Emptys) -> + lists:reverse(Fulls) ++ Emptys; +fixArrayDims([91,93|Rest],Fulls,Emptys) -> + fixArrayDims(Rest,Fulls,[91,93|Emptys]); +fixArrayDims([C|Rest],Fulls,Emptys) -> + fixArrayDims(Rest,[C|Fulls],Emptys). + + +%% In order to make a legal declaration +%% of an assignable array, the dimensions +%% of empty array of sequences are swifted +%% to the end of the type +fixSeqDims(Cs,Length) -> + fixSeqDims(Cs,Length,[]). + +fixSeqDims([],_Length,Found) -> + lists:reverse(Found); +fixSeqDims([91,93|Rest],Length,Found) when is_list(Length) -> + lists:reverse([93|lists:reverse(Length)] ++ + [91|Found]) ++ Rest; +fixSeqDims([C|Rest],Length,Found) -> + fixSeqDims(Rest,Length,[C|Found]). + + + +%%----------------------------------------------------------------- +%% Func: inlinedTypes/2 +%%----------------------------------------------------------------- +inlinedTypes(PkgName, Type) when is_record(Type, struct) -> + "_" ++ PkgName ++ "."; +inlinedTypes(PkgName, Type) when is_record(Type, union) -> + "_" ++ PkgName ++ "."; +inlinedTypes(PkgName, Type) when is_record(Type, enum) -> + "_" ++ PkgName ++ "."; +inlinedTypes(_, _) -> + "". + +%%----------------------------------------------------------------- +%% Func: marshalFun/4 +%%----------------------------------------------------------------- +marshalFun(G, N, X, Type) -> + case isBasicType(G, N, Type) of + true -> + ".write_" ++ getMarshalType(G, N, X, Type); + _ -> + getMarshalType(G, N, X, Type) ++ ".marshal" + end. + + +%%----------------------------------------------------------------- +%% Func: isBasicType/3 +%%----------------------------------------------------------------- +isBasicType(G, N, S) when element(1, S) == scoped_id -> + {_, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + isBasicType(ictype:fetchType(TK)); + +isBasicType(G, N, X) when is_record(X, member) -> + if is_record(hd(element(3,X)), array) -> + false; + true -> + isBasicType(G, N, element(2,X)) + end; + +isBasicType(_G, _N, {unsigned, {long, _}} ) -> + true; + +isBasicType(_G, _N, {unsigned, {short, _}} ) -> + true; + +isBasicType(_G, _N, {unsigned, {'long long', _}} ) -> + true; + +isBasicType(_G, _N, {'long long', _} ) -> + true; + +isBasicType(_G, _N, {Type, _} ) -> + isBasicType(Type); + +isBasicType(_G, _N, Type) -> + isBasicType(Type). + + +%%----------------------------------------------------------------- +%% Func: isBasicType/1 +%%----------------------------------------------------------------- + +isBasicType( Type ) -> + lists:member(Type, + [tk_short,short, + tk_long,long, + tk_longlong,longlong, %% LLONG + tk_ushort,ushort, + tk_ulong,ulong, + tk_ulonglong,ulonglong, %% ULLONG + tk_float,float, + tk_double,double, + tk_boolean,boolean, + tk_char,char, + tk_wchar,wchar, %% WCHAR + tk_octet,octet, + tk_wstring,wstring, %% WSTRING + tk_string,string]). + +%% returns true if the Type is a java elementary type +isJavaElementaryType( Type ) -> + lists:member(Type, + [byte, char, wchar, boolean, + int, short, long, 'long long', float, double]). + +%%----------------------------------------------------------------- +%% Func: isIntegerType/3 +%%----------------------------------------------------------------- +isIntegerType(G, N, S) when element(1, S) == scoped_id -> + {_, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + isIntegerType(ictype:fetchType(TK)); +isIntegerType(_G, _N, {unsigned, {long, _}} ) -> + true; +isIntegerType(_G, _N, {unsigned, {short, _}} ) -> + true; +isIntegerType(_G, _N, {unsigned, {'long long', _}} ) -> + true; +isIntegerType(_G, _N, {'long long', _} ) -> + true; +isIntegerType(_G, _N, {Type, _} ) -> + isIntegerType(Type); +isIntegerType(_G, _N, Type) -> + isIntegerType(Type). + +%%----------------------------------------------------------------- +%% Func: isIntegerType/1 +%%----------------------------------------------------------------- + +isIntegerType( Type ) -> + lists:member(Type, + [tk_short,short, + tk_long,long, + tk_longlong,longlong, %% LLONG + tk_ushort,ushort, + tk_ulong,ulong, + tk_ulonglong,ulonglong, %% ULLONG + tk_char,char, + tk_wchar,wchar, %% WCHAR + tk_octet,octet]). + + + +%%----------------------------------------------------------------- +%% Func: isTerm/3 +%%----------------------------------------------------------------- +isTermType(G, N, T) -> + case getType(G,N,T) of + "com.ericsson.otp.ic.Term" -> + true; + _ -> + false + end. + + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + + +%% Changes the typecode to the +%% corresponding "basic" type +tk2type(_G,_N,_X,{'tk_struct', _IFRId, "port", _ElementList}) -> + ?ICPACKAGE ++ "Port"; +tk2type(_G,_N,_X,{'tk_struct', _IFRId, "pid", _ElementList}) -> + ?ICPACKAGE ++ "Pid"; +tk2type(_G,_N,_X,{'tk_struct', _IFRId, "ref", _ElementList}) -> + ?ICPACKAGE ++ "Ref"; +tk2type(_G,_N,_X,{'tk_struct', _IFRId, "term", _ElementList}) -> + ?ICPACKAGE ++ "Term"; +tk2type(_G,_N,_X,{'tk_string', _}) -> + "java.lang.String"; +tk2type(_G,_N,_X,{'tk_wstring', _}) -> %% WSTRING + "java.lang.String"; +tk2type(G,N,X,{'tk_array', ElemTC, Dim}) -> + tkarr2decl(G,N,X,{'tk_array', ElemTC, Dim}); +tk2type(G,N,X,{'tk_sequence', ElemTC, MaxLsextractength}) -> + tkseq2decl(G,N,X,{'tk_sequence', ElemTC, MaxLsextractength}); +tk2type(G,N,_X,{'tk_struct', IFRId, Name, _ElementList}) -> + ScopedId= + lists:reverse(string:tokens(lists:nth(2,string:tokens(IFRId,":")),"/")), + + case ic_forms:clean_up_scope([Name|N]) of + ScopedId -> + %% Right path, use N instead + ic_util:to_dot(G,[Name|N]); + _ -> + %% Ugly work arround + ic_util:to_dot(G,ScopedId) + end; +tk2type(G,N,_X,{'tk_union', IFRId, Name, _, _, _ElementList}) -> + ScopedId= + lists:reverse(string:tokens(lists:nth(2,string:tokens(IFRId,":")),"/")), + + case ic_forms:clean_up_scope([Name|N]) of + ScopedId -> + %% Right path, use N instead + ic_util:to_dot(G,[Name|N]); + _ -> + %% Ugly work arround + ic_util:to_dot(G,ScopedId) + end; +tk2type(_G,_N,_X,{'tk_enum', _Id, Name, _ElementList}) -> + Name; +tk2type(_G,_N,_X,tk_void) -> + "void"; +tk2type(_G,_N,_X,tk_long) -> + "int"; +tk2type(_G,_N,_X,tk_longlong) -> %% LLONG + "long"; +tk2type(_G,_N,_X,tk_short) -> + "short"; +tk2type(_G,_N,_X,tk_ulong) -> + "int"; +tk2type(_G,_N,_X,tk_ulonglong) -> %% ULLONG + "long"; +tk2type(_G,_N,_X,tk_ushort) -> + "short"; +tk2type(_G,_N,_X,tk_float) -> + "float"; +tk2type(_G,_N,_X,tk_double) -> + "double"; +tk2type(_G,_N,_X,tk_boolean) -> + "boolean"; +tk2type(_G,_N,_X,tk_char) -> + "char"; +tk2type(_G,_N,_X,tk_wchar) -> %% WCHAR + "char"; +tk2type(_G,_N,_X,tk_octet) -> + "byte"; +tk2type(_G,_N,_X,tk_string) -> + "java.lang.String"; +tk2type(_G,_N,_X,tk_wstring) -> %% WSTRING + "java.lang.String"; +tk2type(_G,_N,_X,tk_any) -> + ?ICPACKAGE ++ "Any"; +tk2type(_G,_N,_X,tk_term) -> %% Term + ?ICPACKAGE ++ "Term". + +%% Changes the sequence typecode to the +%% corresponding "basic" structure +tkseq2decl(G,N,X,TKSeq) -> + tkseq2decl2(G,N,X,TKSeq,[],[]). + +tkseq2decl2(G,N,X,{tk_sequence,E,D},[],Ds) -> + tkseq2decl2(G,N,X,E,[],[D|Ds]); +tkseq2decl2(G,N,X,TkEl,[],Ds) -> + ElName = tk2type(G,N,X,TkEl), + ElName ++ getdim(Ds). + +%% Changes the array typecode to the +%% corresponding "basic" structure +tkarr2decl(G,N,X,TKArr) -> + tkarr2decl2(G,N,X,TKArr,[],[]). + +tkarr2decl2(G,N,X,{tk_array,E,D},[],Ds) -> + tkarr2decl2(G,N,X,E,[],[D|Ds]); +tkarr2decl2(G,N,X,TkEl,[],Ds) -> + ElName = tk2type(G,N,X,TkEl), + ElName ++ getdim(Ds). + +getdim([]) -> + ""; +getdim([_D|Ds]) -> + getdim(Ds) ++ "[]". + + + +%% Changes the typecode to the corresponding "basic" type +%% used for variable declarations where arrays and sequences +%% are declared with there full dimensions +tk2FullType(G,N,X,{'tk_array', ElemTC, Dim}) -> + tkarr2FullDecl(G,N,X,{'tk_array', ElemTC, Dim}); +tk2FullType(G,N,X,{'tk_sequence', ElemTC, MaxLsextractength}) -> + tkseq2FullDecl(G,N,X,{'tk_sequence', ElemTC, MaxLsextractength}); +tk2FullType(G,N,X,TK) -> + tk2type(G,N,X,TK). + + +%% Changes the sequence typecode to the +%% corresponding "basic" structure here +%% arrays and sequences are declared with +%% their full dimensions +tkseq2FullDecl(G,N,X,TKSeq) -> + tkseq2FullDecl2(G,N,X,TKSeq,[],[]). + +tkseq2FullDecl2(G,N,X,{tk_sequence,E,D},[],Ds) -> + tkseq2FullDecl2(G,N,X,E,[],[D|Ds]); +tkseq2FullDecl2(G,N,X,TkEl,[],Ds) -> + ElName = tk2FullType(G,N,X,TkEl), + ElName ++ getdim(Ds). + +%% Changes the array typecode to the +%% corresponding "basic" structure +tkarr2FullDecl(G,N,X,TKArr) -> + tkarr2FullDecl2(G,N,X,TKArr,[],[]). + +tkarr2FullDecl2(G,N,X,{tk_array,E,D},[],Ds) -> + tkarr2FullDecl2(G,N,X,E,[],[D|Ds]); +tkarr2FullDecl2(G,N,X,TkEl,[],Ds) -> + ElName = tk2FullType(G,N,X,TkEl), + ElName ++ getFullDim(G,N,Ds). + +getFullDim(_G,_N,[]) -> + ""; +getFullDim(G,N,[D|Ds]) when is_record(D,scoped_id) -> + {FSN, _, _, _} = ic_symtab:get_full_scoped_name(G, N, D), + "[" ++ ic_util:to_dot(G,FSN) ++ "]" ++ getFullDim(G,N,Ds); +getFullDim(G,N,[D|Ds]) when is_integer(D) -> + "[" ++ integer_to_list(D) ++ "]" ++ getFullDim(G,N,Ds); +getFullDim(G,N,[D|Ds]) when is_tuple(D) -> + "[" ++ ic_util:eval_java(G,N,D) ++ "]" ++ getFullDim(G,N,Ds). + + + +%% Constructs an array empty dimension string +%% used for array variable declaration +arrayEmptyDim(X) -> + arrayEmptyDim2(X#array.size). + +arrayEmptyDim2([_D]) -> + "[]"; +arrayEmptyDim2([_D |Ds]) -> + "[]" ++ arrayEmptyDim2(Ds). + + + diff --git a/lib/ic/src/ic_jbe.erl b/lib/ic/src/ic_jbe.erl new file mode 100644 index 0000000000..81798d0429 --- /dev/null +++ b/lib/ic/src/ic_jbe.erl @@ -0,0 +1,1487 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + + +-module(ic_jbe). + + +-export([do_gen/3, gen/3, emit_type_function/4]). + + + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +-include_lib("stdlib/include/erl_compile.hrl"). + + + +%%------------------------------------------------------------ +%% +%% Entry point +%% +%%------------------------------------------------------------ + +do_gen(G, _File, Form) -> + gen(G, [], Form). + + +%%------------------------------------------------------------ +%% +%% Generate the client side C stubs. +%% +%% Each module is generated to a separate file. +%% +%% Each function needs to generate a function head and +%% a body. IDL parameters must be converted into C parameters. +%% +%%------------------------------------------------------------ + +gen(G, N, [X|Xs]) when is_record(X, preproc) -> + NewG = handle_preproc(G, N, X#preproc.cat, X), + gen(NewG, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, module) -> + gen_module(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, interface) -> + gen_interface(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, const) -> + ic_constant_java:gen(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, op) -> + 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) -> + gen_exception(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, enum) -> + ic_enum_java:gen(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, struct) -> + ic_struct_java:gen(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, union) -> + ic_union_java:gen(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, typedef) -> + gen_typedef(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, member) -> + %%?PRINTDEBUG2("gen member: ~p\n",[ic_forms:get_type(X)]), + gen_member(G, N, X), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, case_dcl) -> + %%?PRINTDEBUG2("gen case decl: ~p\n",[ic_forms:get_type(X)]), + gen(G, N, [ic_forms:get_type(X)]), + gen(G, N, Xs); + +gen(G, N, [_|Xs]) -> + gen(G, N, Xs); + +gen(_G, _N, []) -> + ok. + + +%%%-------------------------------------------- +%%% +%%% Just generates the directory to host +%%% the module files +%%% +%%%-------------------------------------------- + +gen_module(G, N, X) -> + case ic_genobj:do_gen(G) of + + true -> %% Generate & register + N1 = [ic_forms:get_id2(X) | N], + %% Create directory + ic_file:createJavaDirectory(G, N1), + gen(G, N1, ic_forms:get_body(X)); + + false -> %% Register only + N1 = [ic_forms:get_id2(X) | N], + reg(G, N1, ic_forms:get_body(X)) + end. + +reg(G, N, [X|_Xs]) when is_record(X, module) -> + reg(G, [ic_forms:get_id2(X) | N], ic_forms:get_body(X)); + +reg(G, N, [X|_Xs]) when is_record(X, interface) -> + reg(G, [ic_forms:get_id2(X) | N], ic_forms:get_body(X)); + +reg(G, N, [X|Xs]) when is_record(X, typedef) -> + Name = ic_util:to_dot(G,[ic_forms:get_java_id(X) | N]), + case X#typedef.type of + {scoped_id,_,_,_} -> + {FullScopedName, _, _, _} = + ic_symtab:get_full_scoped_name(G, N, X#typedef.type), + Type = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + ic_code:insert_typedef(G, Name, Type); + _ -> + ok + end, + reg(G, N, Xs); + +reg(G, N, [_|Xs]) -> + reg(G, N, Xs); + +reg(_G, _N, []) -> + ok. + + + + +%%%---------------------------------------------- +%%% +%%% Generates the interface code +%%% +%%%---------------------------------------------- + +gen_interface(G, N, X) -> + case ic_genobj:do_gen(G) of + true -> + G1 = ic_file:javaInterfaceFilePush(G, N, X), + + %% Generate Interface file + InterfaceFd = ic_genobj:interfacefiled(G1), + emit_interface(G1, N, X, InterfaceFd), + + %% Generate Helper file + HelperFd = ic_genobj:helperfiled(G1), + emit_helper(G1, N, X, HelperFd), + + %% Generate Holder file + HolderFd = ic_genobj:holderfiled(G1), + emit_holder(G1, N, X, HolderFd), + + %% Generate Stub file + StubFd = ic_genobj:stubfiled(G1), + emit_stub(G1,N,X,StubFd), %<--------------------------------------------------- 1 + + %% Generate Skeleton file + SkelFd = ic_genobj:skelfiled(G1), + emit_skel(G1, N, X, SkelFd), + + ic_file:javaInterfaceFilePop(G1); + false -> + ok + end. + + + + +%%%-------------------------------------------- +%%% +%%% Typedef redirection +%%% +%%%-------------------------------------------- + +gen_typedef(G, N, X) -> + Name = ic_util:to_dot(G,[ic_forms:get_java_id(X) | N]), + case X#typedef.type of + {scoped_id,_,_,_} -> + {FullScopedName, _, _, _} = + ic_symtab:get_full_scoped_name(G, N, X#typedef.type), + Type = ic_code:get_basetype(G, ic_util:to_dot(G,FullScopedName)), + ic_code:insert_typedef(G, Name, Type); + _ -> + ok + end, + gen_typedef_1(G, N, X, ic_forms:get_body(X)). + +gen_typedef_1(G, N, X, Type) when is_record(Type, sequence) -> + ic_sequence_java:gen(G, N, Type, ic_forms:get_java_id(X)); +gen_typedef_1(G, N, X, Type) when is_record(Type, array) -> + ic_array_java:gen(G, N, X, Type); +gen_typedef_1(G, N, X, _Type) -> + gen_typedef_2(G, N, X, X#typedef.id), + ok. + +gen_typedef_2(G, N, X, Type) when is_record(Type, array) -> + gen_typedef_1(G, N, X, Type); +gen_typedef_2(G, N, X, Type) when is_list(Type) -> + case Type of + [] -> + ok; + _ -> + gen_typedef_2(G, N, X, hd(Type)), + gen_typedef_2(G, N, X, tl(Type)) + end; +%gen_typedef_2(G, N, X, Type) -> %% Generating Helpers for typedef +% %% Stoped due to compatibility problems +% %% with erl_genserv backend +% case ic_java_type:isBasicType(G,N,X#typedef.type) of +% true -> +% ok; +% false -> +% case ic_forms:get_type_code(G,N,X#typedef.type) of +% {'tk_struct', _, _, _} -> +% ic_struct_java:gen(G, N, X); +% {'tk_sequence',_,_} -> +% ic_sequence_java:gen(G, N, X, ic_forms:get_java_id(X)), +% ok; +% _ -> +% ok +% end +% end; +gen_typedef_2(_G, _N, _X, _Type) -> + ok. + + + +%%%-------------------------------------------- +%%% +%%% Member redirection +%%% +%%%-------------------------------------------- + +gen_member(G, N, X) -> + gen_member_1(G, N, X, [X#member.type]), + gen_member_2(G, N, X, X#member.id). + + +gen_member_1(_G, _N, _X, []) -> + ok; + +gen_member_1(G, N, X, [T|Ts]) when is_record(T, sequence) -> + ic_sequence_java:gen(G, N, T, ic_forms:get_java_id(X)), + gen_member_1(G, N, X, Ts); + +gen_member_1(G, N, X, [T|Ts]) -> + gen(G,N,[T]), + gen_member_1(G,N,X,Ts). + + +gen_member_2(_G, _N, _X, []) -> + ok; + +gen_member_2(G, N, X, [T|Ts]) when is_record(T, array) -> %% BUG ! + ic_array_java:gen(G, N, X, T), + gen_member_2(G, N, X, Ts); + +gen_member_2(G, N, X, [_T|Ts]) -> + gen_member_2(G, N, X, Ts). + + + +gen_exception(_G, N, X) -> + io:format("Warning : Exceptions not supported for java mapping, ~p ignored\n", + [ic_util:to_colon([ic_forms:get_java_id(X)|N])]), + ok. + + + +%%%----------------------------------------------------- +%%% +%%% Interface file generation +%%% +%%%----------------------------------------------------- + +emit_interface(G, N, X, Fd) -> + Interface = ic_forms:get_java_id(X), %% Java Interface Name + IFCName = ic_forms:get_id2(X), %% Internal Interface Name + + ic_codegen:emit(Fd, "public interface ~s {\n\n",[Interface]), + Body = ic_forms:get_body(X), + + %% Generate type declarations inside interface + gen(G, [IFCName |N], Body), + + lists:foreach(fun({_Name, Body1}) -> + emit_interface_prototypes(G, [IFCName|N], Body1, Fd) end, + [{x, Body} | X#interface.inherit_body]), + + ic_codegen:emit(Fd, "}\n\n"). + + +emit_interface_prototypes(G, N, [X |Xs], Fd) when is_record(X, op) -> + + {_, ArgNames, TypeList} = extract_info(G, N, X), + {R, ParameterTypes, _} = TypeList, + + OpName = ic_forms:get_java_id(X), + RT = ic_java_type:getParamType(G,N,R,ret), + PL = ic_util:mk_list(gen_par_list(G, N, X, ParameterTypes,ArgNames)), + + ic_codegen:emit(Fd, "/*\n"), + ic_codegen:emit(Fd, " * Operation ~p interface functions \n", [ic_util:to_colon([OpName|N])]), + ic_codegen:emit(Fd, " */\n\n"), + + ic_codegen:emit(Fd, "~s ~s(~s)\n",[RT, OpName, PL]), + ic_codegen:emit(Fd, " throws java.lang.Exception;\n\n\n"), + + emit_interface_prototypes(G, N, Xs, Fd); +emit_interface_prototypes(G, N, [X |Xs], Fd) when is_record(X, attr) -> + ic_attribute_java:emit_attribute_prototype(G, N, X, Fd), + emit_interface_prototypes(G, N, Xs, Fd); +emit_interface_prototypes(G, N, [_X|Xs], Fd) -> + emit_interface_prototypes(G, N, Xs, Fd); +emit_interface_prototypes(_G, _N, [], _Fd) -> ok. + + + + +%%%----------------------------------------------------- +%%% +%%% Holder file generation +%%% +%%%----------------------------------------------------- + +emit_holder(_G, N, X, Fd) -> + InterfaceName = ic_forms:get_java_id(X), + FullInterfaceName = ic_util:to_dot([InterfaceName|N]), + + ic_codegen:emit(Fd, "public final class ~sHolder {\n\n",[InterfaceName]), + + ic_codegen:emit(Fd, " // Instance variable\n"), + ic_codegen:emit(Fd, " public ~s value;\n\n",[FullInterfaceName]), + + ic_codegen:emit(Fd, " // Constructors\n"), + ic_codegen:emit(Fd, " public ~sHolder() {\n",[InterfaceName]), + ic_codegen:emit(Fd, " this(null);\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public ~sHolder(~s _arg) {\n",[InterfaceName, FullInterfaceName]), + ic_codegen:emit(Fd, " value = _arg;\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public void _marshal() {\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public void _unmarshal() {\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, "}\n\n"). + + + + +%%%----------------------------------------------------- +%%% +%%% Helper file generation +%%% +%%%----------------------------------------------------- +emit_helper(G, N, X, Fd) -> + InterfaceName = ic_forms:get_java_id(X), + FullInterfaceName = ic_util:to_dot([InterfaceName|N]), + + ic_codegen:emit(Fd, "public final class ~sHelper {\n\n",[InterfaceName]), + + ic_codegen:emit(Fd, " // Constructor\n"), + ic_codegen:emit(Fd, " public ~sHelper() {\n",[InterfaceName]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static void _marshal() {\n"), + ic_codegen:emit(Fd, " // Writing the object to the message\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static ~s _unmarshal() {\n",[FullInterfaceName]), + ic_codegen:emit(Fd, " // Reading the object from the message\n"), + ic_codegen:emit(Fd, " return null;\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static java.lang.String id() {\n"), + ic_codegen:emit(Fd, " return ~p;\n",[ictk:get_IR_ID(G, N, X)]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, "}\n\n"). + + + + +%%%----------------------------------------------------- +%%% +%%% Stub file generation +%%% +%%%----------------------------------------------------- + +emit_stub(G, N, X, Fd) -> + InterfaceName = ic_forms:get_java_id(X), %% Java Interface Name + IFCName = ic_forms:get_id2(X), %% Internal Interface Name + + FullInterfaceName = ic_util:to_dot([InterfaceName|N]), + Body = ic_forms:get_body(X), + + ic_codegen:emit(Fd, "public class _~sStub implements ~s {\n\n", + [InterfaceName,FullInterfaceName]), + + ic_codegen:emit(Fd, " // Client data\n"), + ic_codegen:emit(Fd, " public ~sEnvironment _env;\n\n",[?ICPACKAGE]), + + ic_codegen:emit(Fd, " // Constructors\n"), + ic_codegen:emit(Fd, " public _~sStub(~sOtpSelf _self,\n",[InterfaceName,?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " ~sOtpPeer _peer,\n",[?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " java.lang.Object _server) throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " _env =\n"), + ic_codegen:emit(Fd, " new ~sEnvironment(_self, _peer, _server);\n",[?ICPACKAGE]), + ic_codegen:emit(Fd, " _env.connect();\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public _~sStub(java.lang.String _selfN,\n",[InterfaceName]), + ic_codegen:emit(Fd, " java.lang.String _peerN,\n"), + ic_codegen:emit(Fd, " java.lang.String _cookie,\n"), + ic_codegen:emit(Fd, " java.lang.Object _server) throws java.lang.Exception {\n\n"), + ic_codegen:emit(Fd, " _env =\n"), + ic_codegen:emit(Fd, " new ~sEnvironment(_selfN, _peerN, _cookie, _server);\n",[?ICPACKAGE]), + ic_codegen:emit(Fd, " _env.connect();\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public _~sStub(~sOtpConnection _connection,\n",[InterfaceName, ?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " java.lang.Object _server) throws java.lang.Exception {\n\n"), + ic_codegen:emit(Fd, " _env =\n"), + ic_codegen:emit(Fd, " new ~sEnvironment(_connection, _server);\n",[?ICPACKAGE]), + ic_codegen:emit(Fd, " _env.connect();\n"), + ic_codegen:emit(Fd, " }\n\n"), + + emit_message_reference_extraction(Fd), + + emit_servers_object_access(Fd), + + emit_client_connection_close(Fd), + + emit_client_connection_reconnect(Fd), + + emit_client_destroy(Fd), + + lists:foreach(fun({_Name, Body1}) -> + emit_op_implementation(G, [IFCName|N], Body1, Fd) end, + [{x, Body} | X#interface.inherit_body]), + + ic_codegen:emit(Fd, "}\n\n"). + + +emit_op_implementation(G, N, [X |Xs], Fd) when is_record(X, op) -> + + WireOpName = ic_forms:get_id2(X), + OpName = ic_forms:get_java_id(WireOpName), + {_, ArgNames, TypeList} = extract_info(G, N, X), + {R, ParamTypes, _} = TypeList, + + RT = ic_java_type:getParamType(G,N,R,ret), + PL = ic_util:mk_list(gen_par_list(G, N, X, ParamTypes, ArgNames)), + CMCPL = ic_util:mk_list(gen_client_marshal_call_par_list(ArgNames)), + + ic_codegen:emit(Fd, " // Operation ~p implementation\n", [ic_util:to_colon([WireOpName|N])]), + ic_codegen:emit(Fd, " public ~s ~s(~s)\n", [RT, OpName, PL]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + %% Function marshal call + ic_codegen:emit(Fd, " // Calling the marshal function\n"), + + case CMCPL of + "" -> + ic_codegen:emit(Fd, " _~s_marshal(_env);\n\n",[OpName]); + _ -> + ic_codegen:emit(Fd, " _~s_marshal(_env, ~s);\n\n",[OpName, CMCPL]) + end, + + %% Sending call + ic_codegen:emit(Fd, " // Message send\n"), + ic_codegen:emit(Fd, " _env.send();\n\n"), + + case ic_forms:is_oneway(X) of + true -> + ok; + false -> + %% Receiving return values + ic_codegen:emit(Fd, " // Message receive\n"), + ic_codegen:emit(Fd, " _env.receive();\n\n"), + + %% Function unmarshal call + case RT of + "void" -> + case ic_util:mk_list(gen_client_unmarshal_call_par_list(ArgNames)) of + "" -> + ic_codegen:emit(Fd, " // Calling the unmarshal function\n"), + ic_codegen:emit(Fd, " _~s_unmarshal(_env);\n", + [OpName]); + UMCPL -> + ic_codegen:emit(Fd, " // Calling the unmarshal function\n"), + ic_codegen:emit(Fd, " _~s_unmarshal(_env, ~s);\n", + [OpName,UMCPL]) + end; + _ -> + ic_codegen:emit(Fd, " // Calling the unmarshal function\n"), + case ic_util:mk_list(gen_client_unmarshal_call_par_list(ArgNames)) of + "" -> + ic_codegen:emit(Fd, " return _~s_unmarshal(_env);\n", + [OpName]); + UMCPL -> + ic_codegen:emit(Fd, " return _~s_unmarshal(_env, ~s);\n", + [OpName,UMCPL]) + end + end + end, + ic_codegen:emit(Fd, " }\n\n"), + + %% Marshalling + emit_op_marshal(G, N, X, Fd), + + %% UnMarshalling + emit_op_unmarshal(G, N, X, Fd), + ic_codegen:emit(Fd, "\n"), + + emit_op_implementation(G, N, Xs, Fd); +emit_op_implementation(G, N, [X |Xs], Fd) when is_record(X, attr) -> + ic_attribute_java:emit_attribute_stub_code(G, N, X, Fd), + emit_op_implementation(G, N, Xs, Fd); +emit_op_implementation(G, N, [_X|Xs], Fd) -> + emit_op_implementation(G, N, Xs, Fd); +emit_op_implementation(_G, _N, [], _Fd) -> ok. + + + + + +%%--------------------------------------- +%% +%% Marshal operation generation +%% +%%--------------------------------------- + +emit_op_marshal(G, N, X, Fd) -> + WireOpName = ic_forms:get_id2(X), + OpName = ic_forms:get_java_id(WireOpName), + {_, ArgNames, TypeList} = extract_info(G, N, X), + {_R, ParamTypes, _} = TypeList, + + PL = ic_util:mk_list(gen_marshal_par_list(G, N, X, ParamTypes, ArgNames)), + + ic_codegen:emit(Fd, " // Marshal operation for ~p\n", [OpName]), + case PL of + "" -> + ic_codegen:emit(Fd, " public static void _~s_marshal(~sEnvironment __env)\n", + [OpName, ?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"); + _ -> + ic_codegen:emit(Fd, " public static void _~s_marshal(~sEnvironment __env, ~s)\n", + [OpName, ?ICPACKAGE, PL]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n") + end, + %% Message encoding + emit_op_encode(G, N, X, OpName, WireOpName, ParamTypes, ArgNames, Fd), + + ic_codegen:emit(Fd, " }\n\n"). + + +emit_op_encode(G, N, X, _OpN, WOpN, ParamTypes, ArgNames, Fd) -> + + OpCallName = case ic_options:get_opt(G, scoped_op_calls) of + true -> + ic_util:to_undersc([WOpN|N]); + false -> + WOpN + end, + + SendParamNr = count_client_send(ArgNames), + + ic_codegen:emit(Fd, " ~sOtpOutputStream __os = __env.getOs();\n\n", + [?ERLANGPACKAGE]), + + case ic_forms:is_oneway(X) of + true -> + %% Initiating call tuple + ic_codegen:emit(Fd, " // Message header assembly\n"), + ic_codegen:emit(Fd, " __os.reset();\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_atom(\"$gen_cast\");\n\n"); + false -> + %% Initiating call tuple + ic_codegen:emit(Fd, " // Message header assembly\n"), + ic_codegen:emit(Fd, " __os.reset();\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(3);\n"), + ic_codegen:emit(Fd, " __os.write_atom(\"$gen_call\");\n\n"), + + %% Initiating call identity tuple + ic_codegen:emit(Fd, " // Message identity part creation\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __env.write_client_pid();\n"), + ic_codegen:emit(Fd, " __env.write_client_ref();\n\n") + end, + + %% Operation part initializations + case SendParamNr > 0 of + true -> + ic_codegen:emit(Fd, " // Operation attribute creation\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(~p);\n", [SendParamNr+1]), + ic_codegen:emit(Fd, " __os.write_atom(~p);\n", [OpCallName]), + emit_op_encode_loop(G, N, X, ParamTypes, ArgNames, 1, Fd); + false -> %% No in/inout paramaters + ic_codegen:emit(Fd, " __os.write_atom(~p);\n", [OpCallName]) + end. + + + +emit_op_encode_loop(_,_,_,_,[],_,_Fd) -> + ok; +emit_op_encode_loop(G, N, X, [_Type|Types],[{out, _Arg}|Args], Counter, Fd) -> + emit_op_encode_loop(G, N, X, Types, Args, Counter, Fd); +emit_op_encode_loop(G, N, X, [Type|Types], [{inout, Arg}|Args], Counter, Fd) -> + case ic_java_type:isBasicType(G, N, Type) of + true -> + ic_codegen:emit(Fd, " __os~s(~s.value);\n", + [ic_java_type:marshalFun(G, N, X, Type),Arg]); + false -> + ic_codegen:emit(Fd, " ~s(__os, ~s.value);\n", + [ic_java_type:marshalFun(G, N, X, Type),Arg]) + end, + emit_op_encode_loop(G, N, X, Types, Args, Counter+1, Fd); +emit_op_encode_loop(G, N, X, [Type|Types], [{in, Arg}|Args], Counter, Fd) -> + case ic_java_type:isBasicType(G, N, Type) of + true -> + ic_codegen:emit(Fd, " __os~s(~s);\n", + [ic_java_type:marshalFun(G, N, X, Type),Arg]); + false -> + ic_codegen:emit(Fd, " ~s(__os, ~s);\n", + [ic_java_type:marshalFun(G, N, X, Type),Arg]) + end, + emit_op_encode_loop(G, N, X, Types, Args, Counter+1, Fd). + + + + + + +%%------------------------------------- +%% +%% UnMarshal operation generation +%% +%%------------------------------------- + +emit_op_unmarshal(G, N, X, Fd) -> + case ic_forms:is_oneway(X) of + true -> + ok; + false -> + OpName = ic_forms:get_java_id(X), + {_, ArgNames, TypeList} = extract_info(G, N, X), + {R, ParamTypes, _} = TypeList, + + RT = ic_java_type:getParamType(G,N,R,ret), + PL = ic_util:mk_list(gen_unmarshal_par_list(G, N, X, ParamTypes, ArgNames)), + + case PL of + "" -> + case RT of + "void" -> + ic_codegen:emit(Fd, " // Unmarshal operation for ~p\n", [OpName]), + ic_codegen:emit(Fd, " public static void _~s_unmarshal(~sEnvironment __env)\n", + [OpName, ?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + ic_codegen:emit(Fd, " __env.getIs().read_atom();\n"), + ic_codegen:emit(Fd, " }\n\n"); + _ -> + ic_codegen:emit(Fd, " // Unmarshal operation for ~p\n", [OpName]), + ic_codegen:emit(Fd, " public static ~s _~s_unmarshal(~sEnvironment __env)\n", + [RT, OpName, ?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Get input stream\n"), + ic_codegen:emit(Fd, " ~sOtpInputStream __is = __env.getIs();\n\n", + [?ERLANGPACKAGE]), + + emit_op_decode(G, N, X, R, RT, ParamTypes, ArgNames, Fd), + ic_codegen:emit(Fd, " }\n\n") + end; + _ -> + ic_codegen:emit(Fd, " // Unmarshal operation for ~p\n", [OpName]), + ic_codegen:emit(Fd, " public static ~s _~s_unmarshal(~sEnvironment __env, ~s)\n", + [RT, OpName, ?ICPACKAGE, PL]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Get input stream\n"), + ic_codegen:emit(Fd, " ~sOtpInputStream __is = __env.getIs();\n\n", + [?ERLANGPACKAGE]), + + emit_op_decode(G, N, X, R, RT, ParamTypes, ArgNames, Fd), + ic_codegen:emit(Fd, " }\n\n") + end + end. + + +emit_op_decode(G, N, X, R, RT, ParamTypes, ArgNames, Fd) -> + ReceiveNr = count_client_receive(ArgNames), + + case RT of + "void" -> + case ReceiveNr > 0 of + true -> + ic_codegen:emit(Fd, " // Extracting output values\n"), + ic_codegen:emit(Fd, " __is.read_tuple_head();\n"), + ic_codegen:emit(Fd, " __is.read_atom();\n"), + emit_op_decode_loop(G, N, X, ParamTypes, ArgNames, 1, Fd); + false -> + ic_codegen:emit(Fd, " __is.read_atom();\n") + end; + _ -> + case ReceiveNr > 0 of + true -> + ic_codegen:emit(Fd, " // Extracting return/output values\n"), + ic_codegen:emit(Fd, " __is.read_tuple_head();\n"), + case ic_java_type:isBasicType(G,N,R) of + true -> + ic_codegen:emit(Fd, " ~s _result = __is~s;\n", + [RT,ic_java_type:unMarshalFun(G, N, X, R)]); + false -> + ic_codegen:emit(Fd, " ~s _result = ~s.unmarshal(__is);\n", + [RT, ic_java_type:getUnmarshalType(G,N,X,R)]) + end, + emit_op_decode_loop(G, N, X, ParamTypes, ArgNames, 1, Fd), + + ic_codegen:nl(Fd), + ic_codegen:emit(Fd, " return _result;\n"); + false -> + ic_codegen:emit(Fd, " // Extracting return value\n"), + case ic_java_type:isBasicType(G,N,R) of + true -> + ic_codegen:emit(Fd, " return __is~s;\n", + [ic_java_type:unMarshalFun(G, N, X, R)]); + false -> + ic_codegen:emit(Fd, " return ~s.unmarshal(__is);\n", + [ic_java_type:getUnmarshalType(G,N,X,R)]) + end + end + end. + +emit_op_decode_loop(_,_,_,_,[],_,_Fd) -> + ok; +emit_op_decode_loop(G, N, X, [_Type|Types], [{in, _Arg}|Args], Counter, Fd) -> + emit_op_decode_loop(G, N, X, Types, Args, Counter, Fd); +emit_op_decode_loop(G, N, X, [Type|Types], [{_, Arg}|Args], Counter, Fd) -> + case ic_java_type:isBasicType(G,N,Type) of + true -> + ic_codegen:emit(Fd, " ~s.value = __is~s;\n", + [Arg, + ic_java_type:unMarshalFun(G, N, X, Type)]); + false -> + ic_codegen:emit(Fd, " ~s.value = ~s.unmarshal(__is);\n", + [Arg, + ic_java_type:getUnmarshalType(G, N, X, Type)]) + end, + emit_op_decode_loop(G, N, X, Types, Args, Counter+1, Fd). + + + +emit_message_reference_extraction(Fd) -> + ic_codegen:emit(Fd, " // Returns call reference\n"), + ic_codegen:emit(Fd, " public ~sOtpErlangRef __getRef()\n", + [?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n"), + ic_codegen:emit(Fd, " return _env.received_ref();\n"), + ic_codegen:emit(Fd, " }\n\n"). + +emit_servers_object_access(Fd) -> + ic_codegen:emit(Fd, " // Returns the server\n"), + ic_codegen:emit(Fd, " public java.lang.Object __server() {\n"), + ic_codegen:emit(Fd, " return _env.server();\n"), + ic_codegen:emit(Fd, " }\n\n"). + +emit_client_connection_close(Fd) -> + ic_codegen:emit(Fd, " // Closes connection\n"), + ic_codegen:emit(Fd, " public void __disconnect() {\n"), + ic_codegen:emit(Fd, " _env.disconnect();\n"), + ic_codegen:emit(Fd, " }\n\n"). + +emit_client_connection_reconnect(Fd) -> + ic_codegen:emit(Fd, " // Reconnects client\n"), + ic_codegen:emit(Fd, " public void __reconnect()\n"), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n"), + ic_codegen:emit(Fd, " _env.reconnect();\n"), + ic_codegen:emit(Fd, " }\n\n"). + +emit_client_destroy(Fd) -> + ic_codegen:emit(Fd, " // Destroy server\n"), + ic_codegen:emit(Fd, " public void __stop()\n"), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n"), + ic_codegen:emit(Fd, " _env.client_stop_server();\n"), + ic_codegen:emit(Fd, " }\n\n"). + + + +%%%---------------------------------------------------- +%%% +%%% Generates the server code +%%% +%%%---------------------------------------------------- + +emit_skel(G, N, X, Fd) -> + InterfaceName = ic_forms:get_java_id(X), + FullInterfaceName = ic_util:to_dot([InterfaceName|N]), + + ic_codegen:emit(Fd, "public abstract class _~sImplBase implements ~s {\n\n", + [InterfaceName,FullInterfaceName]), + + ic_codegen:emit(Fd, " // Server data\n"), + ic_codegen:emit(Fd, " protected ~sEnvironment _env = null;\n\n",[?ICPACKAGE]), + + ic_codegen:emit(Fd, " // Constructors\n"), + ic_codegen:emit(Fd, " public _~sImplBase() {\n",[InterfaceName]), + ic_codegen:emit(Fd, " }\n\n"), + + emit_caller_pid(G, N, X, Fd), + + %% Emit operation dictionary + emit_dictionary(G, N, X, Fd), + + %% Emit server switch + emit_server_switch(G, N, X, Fd), + + ic_codegen:emit(Fd, "}\n"). + + +emit_server_switch(G, N, X, Fd) -> + + IFCName = ic_forms:get_id2(X), %% Internal Interface Name + Body = ic_forms:get_body(X), + Counter = 0, + + ic_codegen:emit(Fd, " // Operation invokation\n"), + ic_codegen:emit(Fd, " public ~sOtpOutputStream invoke(~sOtpInputStream _in)\n", + [?ERLANGPACKAGE,?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Create a new environment if needed\n"), + ic_codegen:emit(Fd, " if (_env == null)\n"), + ic_codegen:emit(Fd, " _env = new com.ericsson.otp.ic.Environment();\n\n"), + + ic_codegen:emit(Fd, " // Unmarshal head\n"), + ic_codegen:emit(Fd, " _env.uHead(_in);\n\n"), + + ic_codegen:emit(Fd, " // Switch over operation\n"), + ic_codegen:emit(Fd, " return __switch(_env);\n"), + + ic_codegen:emit(Fd, " }\n\n"), + + + ic_codegen:emit(Fd, " // Operation switch\n"), + ic_codegen:emit(Fd, " public ~sOtpOutputStream __switch(~sEnvironment __env)\n", [?ERLANGPACKAGE,?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " // Setup streams and operation label\n"), + ic_codegen:emit(Fd, " ~sOtpOutputStream __os = __env.getOs();\n",[?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " __os.reset();\n"), + ic_codegen:emit(Fd, " int __label = __env.uLabel(__operations);\n\n"), + + ic_codegen:emit(Fd, " // Switch over operation\n"), + ic_codegen:emit(Fd, " switch(__label) {\n\n"), + + OpNr = emit_server_op_switch_loop(G, + [IFCName|N], + [{x, Body} | X#interface.inherit_body], + Counter, + Fd), + + ic_codegen:emit(Fd, " case ~p: { // Standard stop operation\n\n",[OpNr]), + ic_codegen:emit(Fd, " __env.server_stop_server();\n\n"), + ic_codegen:emit(Fd, " } break;\n\n"), + + ic_codegen:emit(Fd, " default: // It will never come down here \n"), + ic_codegen:emit(Fd, " throw new java.lang.Exception(\"BAD OPERATION\");\n\n", []), + + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " if(__os.count() > 0)\n"), + ic_codegen:emit(Fd, " return __os;\n\n"), + + ic_codegen:emit(Fd, " return null;\n"), + ic_codegen:emit(Fd, " }\n\n"). + + + +emit_server_op_switch_loop(_G, _N, [], C, _Fd) -> + C; +emit_server_op_switch_loop(G, N, [{_,X}|Xs], C, Fd) -> + C1 = emit_server_op_switch(G, N, X, C, Fd), + emit_server_op_switch_loop(G, N, Xs, C1, Fd). + + +emit_server_op_switch(G, N, [X|Xs], C, Fd) when is_record(X, op) -> + + OpName = ic_forms:get_java_id(X), + + ic_codegen:emit(Fd, " case ~p: { // Operation ~s\n\n",[C,ic_util:to_dot([OpName|N])]), + + emit_invoke(G, N, X, Fd), + + ic_codegen:emit(Fd, " } break;\n\n"), + + emit_server_op_switch(G, N, Xs, C+1, Fd); +emit_server_op_switch(G, N, [X |Xs], C, Fd) when is_record(X, attr) -> + C1 = ic_attribute_java:emit_attribute_switch_case(G,N,X,Fd,C), + emit_server_op_switch(G, N, Xs, C1, Fd); +emit_server_op_switch(G, N, [_X|Xs], C, Fd) -> + emit_server_op_switch(G, N, Xs, C, Fd); +emit_server_op_switch(_G, _N, [], C, _Fd) -> + C. + + +emit_caller_pid(_G, _N, _X, Fd) -> + ic_codegen:emit(Fd, " // Extracts caller identity\n"), + ic_codegen:emit(Fd, " public ~sOtpErlangPid __getCallerPid() {\n", [?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " return _env.getScaller();\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public ~sOtpErlangPid __getCallerPid(~sEnvironment __env) {\n", + [?ERLANGPACKAGE, ?ICPACKAGE]), + ic_codegen:emit(Fd, " return __env.getScaller();\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public boolean __isStopped() {\n"), + ic_codegen:emit(Fd, " return _env.isStopped();\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public boolean __isStopped(~sEnvironment __env) {\n", + [?ICPACKAGE]), + ic_codegen:emit(Fd, " return __env.isStopped();\n"), + ic_codegen:emit(Fd, " }\n\n"). + + + +%% Creates an operation dictionary +emit_dictionary(G, N, X, Fd) -> + + Counter = 0, + Body = ic_forms:get_body(X), + + ic_codegen:emit(Fd, " // Operation dictionary\n"), + ic_codegen:emit(Fd, " private static java.util.Dictionary __operations = new java.util.Hashtable();\n"), + ic_codegen:emit(Fd, " static {\n"), + + emit_dictionary_loop(G, + [ic_forms:get_id2(X)|N], + [{x, Body} | X#interface.inherit_body], + Counter, + Fd), + + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " // Operation dictionary access\n"), + ic_codegen:emit(Fd, " public static java.util.Dictionary __operations() {\n"), + ic_codegen:emit(Fd, " return __operations;\n"), + ic_codegen:emit(Fd, " }\n\n"). + + + + +emit_dictionary_loop(_G, _N, [], C, Fd) -> + ic_codegen:emit(Fd, " __operations.put(~p, new java.lang.Integer(~p));\n", + ["stop",C]); +emit_dictionary_loop(G, N, [{_,X}|Xs], C, Fd) -> + C1 = emit_dictionary(G, N, X, C, Fd), + emit_dictionary_loop(G, N, Xs, C1, Fd). + + +emit_dictionary(G, N, [X|Xs], C, Fd) when is_record(X, op) -> + + OpName = case ic_options:get_opt(G, scoped_op_calls) of + true -> + ic_util:to_undersc([ic_forms:get_id2(X)|N]); + false -> + ic_forms:get_id2(X) + end, + + ic_codegen:emit(Fd, " __operations.put(~p, new java.lang.Integer(~p));\n", + [OpName,C]), + emit_dictionary(G, N, Xs, C+1, Fd); + +emit_dictionary(G, N, [X |Xs], C, Fd) when is_record(X, attr) -> + C1 = ic_attribute_java:emit_atrribute_on_dictionary(G, N, X, Fd, C), + emit_dictionary(G, N, Xs, C1, Fd); + +emit_dictionary(G, N, [_X|Xs], C, Fd) -> + emit_dictionary(G, N, Xs, C, Fd); + +emit_dictionary(_G, _N, [], C, _Fd) -> + C. + + + +emit_invoke(G, N, X, Fd) -> + + {_, ArgNames, TypeList} = extract_info(G, N, X), + {R, ParamTypes, _} = TypeList, + OpName = ic_forms:get_java_id(X), + RT = ic_java_type:getParamType(G,N,R,ret), + PL = ic_util:mk_list(gen_cb_arg_list(ArgNames)), + OutParamNr = count_server_send(ArgNames), + + case count_server_receive(ArgNames) of + 0 -> + ok; + _C -> + ic_codegen:emit(Fd, " // Preparing input\n"), + ic_codegen:emit(Fd, " ~sOtpInputStream __is = __env.getIs();\n", + [?ERLANGPACKAGE]), + emit_server_unmarshal_loop(G, N, X, ParamTypes, ArgNames, 1, Fd) + end, + + ic_codegen:emit(Fd, " // Calling implementation function\n"), + case RT of + "void" -> + ic_codegen:emit(Fd, " this.~s(~s);\n\n", + [OpName,PL]); + _ -> + ic_codegen:emit(Fd, " ~s _result = this.~s(~s);\n\n", + [RT, OpName, PL]) + end, + + case ic_forms:is_oneway(X) of + true -> + ok; + false -> + ic_codegen:emit(Fd, " // Marshaling output\n"), + ic_codegen:emit(Fd, " ~sOtpErlangRef __ref = __env.getSref();\n",[?ERLANGPACKAGE]), + + case RT of + "void" -> + case OutParamNr > 0 of + true -> + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_ref(__ref.node(),__ref.ids(),__ref.creation()); // Call reference\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(~p);\n",[OutParamNr+1]), + ic_codegen:emit(Fd, " __os.write_atom(\"ok\");\n"), + emit_server_marshal_loop(G, N, X, ParamTypes,ArgNames,1,Fd); + false -> + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_ref(__ref.node(),__ref.ids(),__ref.creation()); // Call reference\n"), + ic_codegen:emit(Fd, " __os.write_atom(\"ok\");\n\n") + end; + _ -> + case OutParamNr > 0 of + true -> + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_ref(__ref.node(),__ref.ids(),__ref.creation()); // Call reference\n"), + ic_codegen:emit(Fd, " __os.write_tuple_head(~p);\n",[OutParamNr+1]), + + case ic_java_type:isBasicType(G,N,R) of + true -> + ic_codegen:emit(Fd, " __os~s(_result); // Return value\n", + [ic_java_type:marshalFun(G,N,X,R)]); + false -> + ic_codegen:emit(Fd, " ~s(__os,_result); // Return value\n", + [ic_java_type:marshalFun(G,N,X,R)]) + end, + emit_server_marshal_loop(G, N, X, ParamTypes,ArgNames,1,Fd); + false -> + ic_codegen:emit(Fd, " __os.write_tuple_head(2);\n"), + ic_codegen:emit(Fd, " __os.write_ref(__ref.node(),__ref.ids(),__ref.creation()); // Call reference\n"), + + case ic_java_type:isBasicType(G,N,R) of + true -> + ic_codegen:emit(Fd, " __os~s(_result); // Return value\n\n", + [ic_java_type:marshalFun(G,N,X,R)]); + false -> + ic_codegen:emit(Fd, " ~s(__os,_result); // Return value\n\n", + [ic_java_type:marshalFun(G,N,X,R)]) + end + end + end, + ic_codegen:nl(Fd) + end. + + +emit_server_unmarshal_loop(_,_,_,_,[],_,Fd) -> + ic_codegen:nl(Fd); +emit_server_unmarshal_loop(G, N, X, [Type|Types], [{in, Arg}|Args], Counter, Fd) -> + case ic_java_type:isBasicType(G,N,Type) of + true -> + ic_codegen:emit(Fd, " ~s ~s = __is~s; // In value\n", + [ic_java_type:getType(G,N,Type), + Arg, + ic_java_type:unMarshalFun(G,N,X,Type)]); + false -> + ic_codegen:emit(Fd, " ~s ~s = ~s.unmarshal(__is); // In value\n", + [ic_java_type:getType(G,N,Type), + Arg, + ic_java_type:getUnmarshalType(G,N,X,Type)]) + end, + emit_server_unmarshal_loop(G, N, X, Types, Args, Counter+1, Fd); +emit_server_unmarshal_loop(G, N, X, [Type|Types],[{inout, Arg}|Args], Counter, Fd) -> + Holder = ic_java_type:getHolderType(G,N,Type), + case ic_java_type:isBasicType(G,N,Type) of + true -> +% OtpEncVar = ic_java_type:getUnmarshalType(G,N,X,Type), + ic_codegen:emit(Fd, " ~s _~s = __is~s;\n", + [ic_java_type:getType(G,N,Type), + Arg, + ic_java_type:unMarshalFun(G,N,X,Type)]), + ic_codegen:emit(Fd, " ~s ~s = new ~s(_~s); // InOut value\n", + [Holder, + Arg, + Holder, + Arg]); + false -> + ic_codegen:emit(Fd, " ~s ~s = new ~s(); // InOut value\n", + [Holder, + Arg, + Holder]), + ic_codegen:emit(Fd, " ~s._unmarshal(__is);\n", + [Arg]) + end, + emit_server_unmarshal_loop(G, N, X, Types, Args, Counter+1, Fd); +emit_server_unmarshal_loop(G, N, X, [Type|Types],[{out, Arg}|Args], Counter, Fd) -> + Holder = ic_java_type:getHolderType(G,N,Type), + ic_codegen:emit(Fd, " ~s ~s = new ~s(); // Out value\n", [Holder, Arg, Holder]), + emit_server_unmarshal_loop(G, N, X, Types, Args, Counter, Fd). + + +emit_server_marshal_loop(_,_,_,_,[],_,_Fd) -> + ok; +emit_server_marshal_loop(G, N, X, [_Type|Types],[{in, _Arg}|Args], Counter, Fd) -> + emit_server_marshal_loop(G, N, X, Types, Args, Counter, Fd); +emit_server_marshal_loop(G, N, X, [Type|Types],[{_, Arg}|Args], Counter, Fd) -> +% Holder = ic_java_type:getHolderType(G,N,Type), + case ic_java_type:isBasicType(G,N,Type) of + true -> + ic_codegen:emit(Fd, " __os~s(~s.value); // Out/InOut value\n", + [ic_java_type:marshalFun(G,N,X,Type),Arg]); + false -> + ic_codegen:emit(Fd, " ~s._marshal(__os); // Out/InOut value\n", + [Arg]) + end, + emit_server_marshal_loop(G, N, X, Types, Args, Counter+1, Fd). + + + + + +%%%---------------------------------------------------- +%%% +%%% Utilities +%%% +%%%---------------------------------------------------- + +extract_info(_G, N, X) when is_record(X, op) -> + Name = ic_util:to_undersc([ic_forms:get_id2(X) | N]), + Args = X#op.params, + ArgNames = mk_c_vars(Args), + TypeList = {ic_forms:get_type(X), + lists:map(fun(Y) -> ic_forms:get_type(Y) end, Args), + [] + }, + {Name, ArgNames, TypeList}; +extract_info(_G, N, X) -> + Name = ic_util:to_undersc([ic_forms:get_id2(X) | N]), + {Name, [], []}. + +%% Input is a list of parameters (in parse form) and output is a list +%% of parameter attribute and variable names. +mk_c_vars(Params) -> + lists:map(fun(P) -> {A, _} = P#param.inout, + {A, ic_forms:get_id(P#param.id)} + end, + Params). + +%% +handle_preproc(G, _N, line_nr, X) -> + Id = ic_forms:get_java_id(X), + Flags = X#preproc.aux, + case Flags of + [] -> ic_genobj:push_file(G, Id); + _ -> + lists:foldr(fun({_, _, "1"}, Gprim) -> ic_genobj:push_file(Gprim, Id); + ({_, _, "2"}, Gprim) -> ic_genobj:pop_file(Gprim, Id); + ({_, _, "3"}, Gprim) -> ic_genobj:sys_file(Gprim, Id) end, + G, Flags) + end; +handle_preproc(G, _N, _Other, _X) -> + G. + + +%% +gen_par_list(_, _, _, [], []) -> + []; +gen_par_list(G, N, X, [Type |Types], [{Attr, Arg}|Args]) -> + JType = ic_java_type:getParamType(G, N, Type, Attr), + [JType ++ " " ++ Arg | + gen_par_list(G, N, X, Types, Args)]. + + +gen_marshal_par_list(_, _, _, [], []) -> + []; +gen_marshal_par_list(G, N, X, [_Type |Types], [{out, _Arg}|Args]) -> + gen_marshal_par_list(G, N, X, Types, Args); +gen_marshal_par_list(G, N, X, [Type |Types], [{Attr, Arg}|Args]) -> + JType = ic_java_type:getParamType(G, N, Type, Attr), + [JType ++ " " ++ Arg | + gen_marshal_par_list(G, N, X, Types, Args)]. + + +gen_unmarshal_par_list(_, _, _, [], []) -> + []; +gen_unmarshal_par_list(G, N, X, [_Type |Types], [{in, _Arg}|Args]) -> + gen_unmarshal_par_list(G, N, X, Types, Args); +gen_unmarshal_par_list(G, N, X, [Type |Types], [{Attr, Arg}|Args]) -> + JType = ic_java_type:getParamType(G, N, Type, Attr), + [JType ++ " " ++ Arg | + gen_unmarshal_par_list(G, N, X, Types, Args)]. + + +%% +gen_client_marshal_call_par_list([]) -> + []; +gen_client_marshal_call_par_list([{out, _Arg}|Args]) -> + gen_client_marshal_call_par_list(Args); +gen_client_marshal_call_par_list([{_Attr, Arg}|Args]) -> + [Arg | gen_client_marshal_call_par_list(Args)]. + + +gen_client_unmarshal_call_par_list([]) -> + []; +gen_client_unmarshal_call_par_list([{in, _Arg}|Args]) -> + gen_client_unmarshal_call_par_list(Args); +gen_client_unmarshal_call_par_list([{_Attr, Arg}|Args]) -> + [Arg | gen_client_unmarshal_call_par_list(Args)]. + + + +count_client_receive(ArgNames) -> + count_client_receive(ArgNames,0). + +count_client_receive([],C) -> + C; +count_client_receive([{in, _Arg}|Args],C) -> + count_client_receive(Args,C); +count_client_receive([_|Args],C) -> + count_client_receive(Args,C+1). + + + +count_client_send(ArgNames) -> + count_client_send(ArgNames,0). + +count_client_send([],C) -> + C; +count_client_send([{out, _Arg}|Args],C) -> + count_client_send(Args,C); +count_client_send([_|Args],C) -> + count_client_send(Args,C+1). + + +gen_cb_arg_list([]) -> + []; +gen_cb_arg_list([{_Attr, Arg}|Args]) -> + [Arg | gen_cb_arg_list(Args)]. + + +count_server_receive(ArgNames) -> + count_server_receive(ArgNames,0). + +count_server_receive([],C) -> + C; +count_server_receive([_|Args],C) -> + count_server_receive(Args,C+1). + + +count_server_send(ArgNames) -> + count_server_send(ArgNames,0). + +count_server_send([],C) -> + C; +count_server_send([{in, _Arg}|Args],C) -> + count_server_send(Args,C); +count_server_send([_|Args],C) -> + count_server_send(Args,C+1). + + + + + +%%%------------------------------------------------------- + + +emit_type_function(G, N, X, Fd) -> + + TC = ic_forms:get_type_code(G, N, X), + + %%io:format("X = ~p\nTC = ~p\n",[X,TC]), + + ic_codegen:emit(Fd, " private static ~sTypeCode _tc;\n",[?ICPACKAGE]), + ic_codegen:emit(Fd, " synchronized public static ~sTypeCode type() {\n\n",[?ICPACKAGE]), + + ic_codegen:emit(Fd, " if (_tc != null)\n"), + ic_codegen:emit(Fd, " return _tc;\n\n"), + + emit_type_function(TC, 0, Fd), + + ic_codegen:emit(Fd, "\n _tc = _tc0;\n"), + + ic_codegen:emit(Fd, "\n return _tc0;\n"), + ic_codegen:emit(Fd, " }\n\n"). + + + +emit_type_function({tk_struct, ID, Name, ML}, C, Fd) -> %% struct + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_struct);\n", [C,?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.id(~p);\n", [C,ID]), + ic_codegen:emit(Fd, " _tc~p.name(~p);\n", [C,Name]), + ic_codegen:emit(Fd, " _tc~p.member_count(~p);\n", [C,length(ML)]), + emit_struct_members(ML, C, C+1, 0, Fd); + +emit_type_function({tk_enum, ID, Name, MNames}, C, Fd) -> %% enum + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_enum);\n", [C,?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.id(~p);\n", [C,ID]), + ic_codegen:emit(Fd, " _tc~p.name(~p);\n", [C,Name]), + ic_codegen:emit(Fd, " _tc~p.member_count(~p);\n", [C,length(MNames)]), + emit_enum_members(MNames, C, 0, Fd), + C+1; + +emit_type_function({tk_array, ET, L}, C, Fd) -> %% array + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_array);\n", [C,?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.id(id());\n",[C]), + ic_codegen:emit(Fd, " _tc~p.length(~p);\n", [C,L]), + C1 = C+1, + C2 = emit_type_function(ET, C1, Fd), + ic_codegen:emit(Fd, " _tc~p.content_type(_tc~p);\n", [C,C1]), + C2; + +emit_type_function({tk_sequence, ET, L}, C, Fd) -> %% sequence + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_sequence);\n", [C,?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.id(id());\n",[C]), + ic_codegen:emit(Fd, " _tc~p.length(~p);\n", [C,L]), + C1 = C+1, + C2 = emit_type_function(ET, C1, Fd), + ic_codegen:emit(Fd, " _tc~p.content_type(_tc~p);\n", [C,C1]), + C2; + +emit_type_function({tk_string, L}, C, Fd) -> %% string + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_string);\n", [C,?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.length(~p);\n", [C,L]), + C+1; + +emit_type_function({tk_union, ID, Name, DT, DI, LL}, C, Fd) -> %% union + + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_union);\n", [C,?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.id(~p);\n", [C,ID]), + ic_codegen:emit(Fd, " _tc~p.name(~p);\n", [C,Name]), + + C1 = C+1, + C2 = emit_type_function(DT, C1, Fd), + + ic_codegen:emit(Fd, " _tc~p.discriminator_type(_tc~p);\n", [C,C1]), + ic_codegen:emit(Fd, " _tc~p.default_index(~p);\n", [C,DI]), + ic_codegen:emit(Fd, " _tc~p.member_count(~p);\n", [C,length(LL)]), + + emit_union_labels(LL, C, DT, C2, 0, Fd); + +emit_type_function(tk_term, C, Fd) -> %% term, must change it to tk_any + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.tk_any);\n", [C,?ICPACKAGE]), + C+1; + +emit_type_function(TC, C, Fd) -> %% other + ic_codegen:emit(Fd, " ~sTypeCode _tc~p =\n",[?ICPACKAGE,C]), + ic_codegen:emit(Fd, " new ~sTypeCode();\n", [?ICPACKAGE]), + ic_codegen:emit(Fd, " _tc~p.kind(~sTCKind.~p);\n", [C,?ICPACKAGE,TC]), + C+1. + + + +emit_struct_members([], _, TCtr, _, _Fd) -> + TCtr; +emit_struct_members([{Name,MT}|Rest], BTCtr, TCtr, I, Fd) -> + ic_codegen:emit(Fd, " _tc~p.member_name(~p,~p);\n", [BTCtr,I,Name]), + TCtr2 = emit_type_function(MT, TCtr, Fd), + ic_codegen:emit(Fd, " _tc~p.member_type(~p,_tc~p);\n", [BTCtr,I,TCtr]), + emit_struct_members(Rest, BTCtr, TCtr2, I+1, Fd). + +emit_enum_members([], _, _, _Fd) -> + ok; +emit_enum_members([Name|Names], BTCtr, I, Fd) -> + ic_codegen:emit(Fd, " _tc~p.member_name(~p,~p);\n", [BTCtr,I,Name]), + emit_enum_members(Names, BTCtr, I+1, Fd). + + +emit_union_labels([], _, _, TCtr, _, _) -> + TCtr; +emit_union_labels([{L, LN, LT}|Rest], BTCtr, DT, TCtr, I, Fd) -> + ic_codegen:emit(Fd, " ~sAny _any~p =\n",[?ICPACKAGE,TCtr]), + ic_codegen:emit(Fd, " new ~sAny();\n", [?ICPACKAGE]), + TCtr1 = TCtr+1, + TCtr2 = emit_type_function(LT, TCtr1,Fd), + ic_codegen:emit(Fd, " _any~p.type(_tc~p);\n",[TCtr,TCtr1]), + + case L of + default -> + ic_codegen:emit(Fd, " _any~p.insert_atom(\"default\");\n", [TCtr]); + _ -> + case DT of + tk_boolean -> + ic_codegen:emit(Fd, " _any~p.insert_boolean(~p);\n",[TCtr,L]); + tk_char -> + Default = if is_integer(L) -> + [L]; + true -> + L + end, + ic_codegen:emit(Fd, " _any~p.insert_char('~s');\n",[TCtr,Default]); + tk_ushort -> + ic_codegen:emit(Fd, " _any~p.insert_ushort(~p);\n",[TCtr,L]); + tk_ulong -> + ic_codegen:emit(Fd, " _any~p.insert_ulong(~p);\n",[TCtr,L]); + tk_short -> + ic_codegen:emit(Fd, " _any~p.insert_short(~p);\n",[TCtr,L]); + tk_long -> + ic_codegen:emit(Fd, " _any~p.insert_long(~p);\n",[TCtr,L]); + _ -> + ic_codegen:emit(Fd, " _any~p.insert_string(~p);\n", [TCtr,L]) + end + end, + ic_codegen:emit(Fd, " _tc~p.member_label(~p,_any~p);\n", [BTCtr,I,TCtr]), + ic_codegen:emit(Fd, " _tc~p.member_name(~p,~p);\n", [BTCtr,I,LN]), + TCtr3 = emit_type_function(LT, TCtr2, Fd), + ic_codegen:emit(Fd, " _tc~p.member_type(~p,_tc~p);\n", [BTCtr,I,TCtr2]), + emit_union_labels(Rest, BTCtr, DT, TCtr3, I+1, Fd). + + + + + + + + diff --git a/lib/ic/src/ic_noc.erl b/lib/ic/src/ic_noc.erl new file mode 100644 index 0000000000..d43d550a52 --- /dev/null +++ b/lib/ic/src/ic_noc.erl @@ -0,0 +1,1113 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_noc). + + +-export([do_gen/3]). +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ + +-export([unfold/1, mk_attr_func_names/2]). + + +-import(ic_util, [mk_name/2, mk_var/1, mk_oe_name/2, to_atom/1, to_list/1]). +-import(ic_forms, [get_id/1, get_id2/1, get_body/1, is_oneway/1]). +-import(ic_codegen, [emit/2, emit/3, nl/1]). +-import(ic_options, [get_opt/2]). + + +-import(lists, [foreach/2, foldr/3, map/2]). + + +-include("icforms.hrl"). +-include("ic.hrl"). + + + + +%%------------------------------------------------------------ +%% +%% Generate the client side Erlang stubs. +%% +%% Each module is generated to a separate file. +%% +%% Export declarations for all interface functions must be +%% generated. Each function then needs to generate a function head and +%% a body. IDL parameters must be converted into Erlang parameters +%% (variables, capitalised) and a type signature list must be +%% generated (for later encode/decode). +%% +%%------------------------------------------------------------ + + +do_gen(G, File, Form) -> + G2 = ic_file:filename_push(G, [], mk_oe_name(G, + ic_file:remove_ext(to_list(File))), + erlang), + gen_head(G2, [], Form), + exportDependency(G2), + %% Loop through form and adds inheritence data + ic_pragma:preproc(G2, [], Form), + gen(G2, [], Form), + genDependency(G2), + ic_file:filename_pop(G2, erlang), + ok. + + +gen(G, N, [X|Xs]) when is_record(X, preproc) -> + NewG = ic:handle_preproc(G, N, X#preproc.cat, X), + gen(NewG, 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 = [get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, 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, erlang), + N2 = [get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, get_body(X)), + foreach(fun({_Name, Body}) -> gen(G2, N2, Body) end, + X#interface.inherit_body), + gen_serv(G2, N, X), + G3 = ic_file:filename_pop(G2, erlang), + gen(G3, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, const) -> +% N2 = [get_id2(X) | N], + emit_constant_func(G, X#const.id, X#const.val), + gen(G, N, Xs); %% N2 or N? + +gen(G, N, [X|Xs]) when is_record(X, op) -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + + case getNocType(G,X,N) of + transparent -> + emit_transparent_func(G, N, X, Name, ArgNames, TypeList, OutArgs); + multiple -> + mark_not_transparent(G,N), + emit_transparent_func(G, N, X, Name, ArgNames, TypeList, OutArgs); + _XTuple -> + mark_not_transparent(G,N), + emit_stub_func(G, N, X, Name, ArgNames, TypeList, OutArgs) + end, + + gen(G, N, Xs); + + +gen(G, N, [X|Xs]) when is_record(X, attr) -> + emit_attr(G, N, X, fun emit_stub_func/7), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, except) -> + icstruct:except_gen(G, N, X, erlang), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) -> + case may_contain_structs(X) of + true -> icstruct:struct_gen(G, N, X, erlang); + false -> ok + end, + gen(G, N, Xs); + +gen(_G, _N, []) -> ok. + + +may_contain_structs(X) when is_record(X, typedef) -> true; +may_contain_structs(X) when is_record(X, struct) -> true; +may_contain_structs(X) when is_record(X, union) -> true; +may_contain_structs(_X) -> false. + + + +%%-------------------------------------------------------------------- +%% +%% Generate the server side (handle_call and handle_cast) +%% + +gen_serv(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + emit_serv_std(G, N, X), + N2 = [get_id2(X) | N], + gen_calls(G, N2, get_body(X)), + lists:foreach(fun({_Name, Body}) -> + gen_calls(G, N2, Body) end, + X#interface.inherit_body), + get_if_gen(G, N2, X), + gen_end_of_call(G, N, X), % Note N instead of N2 + + gen_casts(G, N2, get_body(X)), + lists:foreach(fun({_Name, Body}) -> + gen_casts(G, N2, Body) end, + X#interface.inherit_body), + gen_end_of_cast(G, N, X), % Note N instead of N2 + emit_skel_footer(G, N, X); % Note N instead of N2 + false -> + ok + end. + +gen_calls(G, N, [X|Xs]) when is_record(X, op) -> + case is_oneway(X) of + false -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + emit_skel_func(G, N, X, Name, ArgNames, TypeList, OutArgs), + gen_calls(G, N, Xs); + true -> + gen_calls(G, N, Xs) + end; + +gen_calls(G, N, [X|Xs]) when is_record(X, attr) -> + emit_attr(G, N, X, fun emit_skel_func/7), + gen_calls(G, N, Xs); + +gen_calls(G, N, [_X|Xs]) -> gen_calls(G, N, Xs); +gen_calls(_G, _N, []) -> ok. + +gen_casts(G, N, [X|Xs]) when is_record(X, op) -> + case is_oneway(X) of + true -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + emit_skel_func(G, N, X, Name, ArgNames, TypeList, OutArgs), + gen_casts(G, N, Xs); + false -> + gen_casts(G, N, Xs) + end; + +gen_casts(G, N, [_X|Xs]) -> gen_casts(G, N, Xs); +gen_casts(_G, _N, []) -> ok. + +emit_attr(G, N, X, F) -> + XX = #id_of{type=X}, + {GetType, SetType} = mk_attr_func_types(N, X), + lists:foreach(fun(Id) -> + X2 = XX#id_of{id=Id}, + {Get, Set} = mk_attr_func_names(N, get_id(Id)), + F(G, N, X2, Get, [], GetType, []), + case X#attr.readonly of + {readonly, _} -> ok; + _ -> + F(G, N, X2, Set, [mk_name(G, "Value")], + SetType, []) + end end, ic_forms:get_idlist(X)). + + +extract_info(G, _N, X) when is_record(X, op) -> + Name = get_id2(X), + InArgs = ic:filter_params([in,inout], X#op.params), + OutArgs = ic:filter_params([out,inout], X#op.params), + ArgNames = mk_erl_vars(G, InArgs), + TypeList = {ic_forms:get_tk(X), + map(fun(Y) -> ic_forms:get_tk(Y) end, InArgs), + map(fun(Y) -> ic_forms:get_tk(Y) end, OutArgs) + }, + {Name, ArgNames, TypeList, OutArgs}. + + + + +emit_serv_std(G, N, X) -> + Fd = ic_genobj:stubfiled(G), + case transparent(G) of + true -> + true; + _XTupleORMultiple -> + Impl = getImplMod(G,X,[get_id2(X)|N]), + TypeID = ictk:get_IR_ID(G, N, X), + + nl(Fd), nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Server implementation."]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Function for fetching the interface type ID."]), + nl(Fd), + emit(Fd, "typeID() ->\n"), + emit(Fd, " \"~s\".\n", [TypeID]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Server creation functions."]), + nl(Fd), + emit(Fd, "oe_create() ->\n"), + emit(Fd, " start([], []).\n", []), + nl(Fd), + emit(Fd, "oe_create_link() ->\n"), + emit(Fd, " start_link([], []).\n", []), + nl(Fd), + emit(Fd, "oe_create(Env) ->\n"), + emit(Fd, " start(Env, []).\n", []), + nl(Fd), + emit(Fd, "oe_create_link(Env) ->\n"), + emit(Fd, " start_link(Env, []).\n", []), + nl(Fd), + emit(Fd, "oe_create(Env, RegName) ->\n"), + emit(Fd, " start(RegName, Env, []).\n", []), + nl(Fd), + emit(Fd, "oe_create_link(Env, RegName) ->\n"), + emit(Fd, " start_link(RegName, Env, []).\n", []), + nl(Fd), + ic_codegen:mcomment(Fd, ["Start functions."]), + nl(Fd), + emit(Fd, "start(Env, Opt) ->\n"), + emit(Fd, " gen_server:start(?MODULE, Env, Opt).\n"), + nl(Fd), + emit(Fd, "start_link(Env, Opt) ->\n"), + emit(Fd, " gen_server:start_link(?MODULE, Env, Opt).\n"), + nl(Fd), + emit(Fd, "start(RegName, Env, Opt) ->\n"), + emit(Fd, " gen_server:start(RegName, ?MODULE, Env, Opt).\n"), + nl(Fd), + emit(Fd, "start_link(RegName, Env, Opt) ->\n"), + emit(Fd, " gen_server:start_link(RegName, ?MODULE, Env, Opt).\n"), + nl(Fd), + ic_codegen:comment(Fd, "Call to implementation init"), + emit(Fd, "init(Env) ->\n"), + emit(Fd, " ~p:~p(Env).\n", [Impl, init]), + nl(Fd), + emit(Fd, "terminate(Reason, State) ->\n"), + emit(Fd, " ~p:~p(Reason, State).\n", + [Impl, terminate]), + nl(Fd), nl(Fd) + end, + Fd. + + + + +gen_end_of_call(G, _N, _X) -> + case transparent(G) of + true -> + true; + _XTuple -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server call handle"]), + emit(Fd, "handle_call(stop, From, State) ->\n"), + emit(Fd, " {stop, normal, ok, State}"), + case get_opt(G, serv_last_call) of + exception -> + emit(Fd, ";\n"), + nl(Fd), + emit(Fd, "handle_call(Req, From, State) ->\n"), + emit(Fd, " {reply, ~p, State}.\n",[getCallErr()]); + exit -> + emit(Fd, ".\n"), + nl(Fd), + nl(Fd) + end + end, + ok. + + +gen_end_of_cast(G, _N, _X) -> + case transparent(G) of + true -> + true; + _XTuple -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server cast handle"]), + emit(Fd, "handle_cast(stop, State) ->\n"), + emit(Fd, " {stop, normal, State}"), + case get_opt(G, serv_last_call) of + exception -> + emit(Fd, ";\n"), + nl(Fd), + emit(Fd, "handle_cast(Req, State) ->\n"), + emit(Fd, " {reply, ~p, State}.\n",[getCastErr()]); + exit -> + emit(Fd, ".\n"), + nl(Fd), nl(Fd) + end + end, + ok. + + +emit_skel_footer(G, N, X) -> + case transparent(G) of + true -> + true; + _XTuple -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + ic_codegen:mcomment_light(Fd, ["Standard gen_server handles"]), + emit(Fd, "handle_info(X, State) ->\n"), + case use_impl_handle_info(G, N, X) of + true -> + emit(Fd, " ~p:handle_info(X, State).\n\n", + [list_to_atom(ic_genobj:impl(G))]); + false -> + emit(Fd, " {reply, ~p, State}.\n\n",[getInfoErr()]) + end + end, + ok. + + +use_impl_handle_info(G, N, X) -> + FullName = ic_util:to_colon([get_id2(X) | N]), + case {get_opt(G, {handle_info, true}), get_opt(G, {handle_info, FullName})} of + {_, force_false} -> false; + {false, false} -> false; + _ -> true + end. + + +use_timeout(G, N, _X) -> + FullName = ic_util:to_colon(N), + case {get_opt(G, {timeout, true}), get_opt(G, {timeout, FullName})} of + {_, force_false} -> false; + {false, false} -> false; + _ -> true + end. + + +get_if_name(G) -> mk_oe_name(G, "get_interface"). + + +%% Generates the get_interface function (for Lars) +get_if_gen(G, N, X) -> + case transparent(G) of + true -> + ok; + _XTuple -> + case ic_genobj:is_stubfile_open(G) of + true -> + IFC_TKS = tk_interface_data(G,N,X), + Fd = ic_genobj:stubfiled(G), + Name = to_atom(get_if_name(G)), + + ic_codegen:mcomment_light(Fd, + [io_lib:format("Standard Operation: ~p", + [Name])]), + + emit(Fd, "handle_call({~s, ~p, []}, From, State) ->~n", + [mk_name(G, "Ref"), Name]), + + emit(Fd, " {reply, ~p, State};~n", [IFC_TKS]), + nl(Fd), + ok; + + false -> ok + end + end. + + +get_if(G,N,[X|Rest]) when is_record(X, op) -> + R = ic_forms:get_tk(X), + IN = lists:map(fun(P) -> ic_forms:get_tk(P) end, + ic:filter_params([in, inout], X#op.params)), + OUT = lists:map(fun(P) -> ic_forms:get_tk(P) end, + ic:filter_params([out, inout], X#op.params)), + case print_tk(G,N,X) of + true -> + [{get_id2(X), {R, IN, OUT}} | get_if(G,N,Rest)]; + false -> + get_if(G,N,Rest) + end; + +get_if(G,N,[X|Rest]) when is_record(X, attr) -> %% Attributes not handled so far <<<<<<<<<<<<<<<<<<<<<<<< + {GetT, SetT} = mk_attr_func_types([], X), + AList = lists:map(fun(Id) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + case X#attr.readonly of + {readonly, _} -> + {Get, GetT}; + _ -> + [{Set, SetT}, {Get, GetT}] + end end, ic_forms:get_idlist(X)), + lists:flatten(AList) ++ get_if(G,N,Rest); + +get_if(G,N,[_X|Rest]) -> get_if(G,N,Rest); +get_if(_,_,[]) -> []. + + + + +%%------------------------------------------------------------ +%% +%% Export stuff +%% +%% Gathering of all names that should be exported from a stub +%% file. +%% + + +gen_head_special(G, N, X) when is_record(X, interface) -> + Fd = ic_genobj:stubfiled(G), + NocType = getNocType(G,X,N), + + foreach(fun({Name, Body}) -> + ic_codegen:comment(Fd, "Exports from ~p", + [ic_util:to_colon(Name)]), + ic_codegen:export(Fd, exp_top(G, N, Body, NocType, [])), + nl(Fd) + end, X#interface.inherit_body), + + case transparent(G) of + true -> + nl(Fd), nl(Fd); + _XTuple -> + ic_codegen:comment(Fd, "Type identification function"), + ic_codegen:export(Fd, [{typeID, 0}]), + nl(Fd), + ic_codegen:comment(Fd, "Used to start server"), + ic_codegen:export(Fd, [{start, 2},{start_link, 3}]), + ic_codegen:export(Fd, [{oe_create, 0}, {oe_create_link, 0}, {oe_create, 1}, + {oe_create_link, 1},{oe_create, 2}, {oe_create_link, 2}]), + nl(Fd), + ic_codegen:comment(Fd, "gen server export stuff"), + emit(Fd, "-behaviour(gen_server).\n"), + ic_codegen:export(Fd, [{init, 1}, {terminate, 2}, {handle_call, 3}, + {handle_cast, 2}, {handle_info, 2}]), + nl(Fd), nl(Fd), + ic_codegen:mcomment(Fd, ["Object interface functions."]), + nl(Fd), nl(Fd), nl(Fd) + end, + Fd; + + +gen_head_special(_G, _N, _X) -> ok. + + + +%% Shall generate all export declarations +gen_head(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + F = ic_genobj:stubfiled(G), + ic_codegen:comment(F, "Interface functions"), + ic_codegen:export(F, exp_top(G, N, X, getNocType(G,X,N), [])), + nl(F), + gen_head_special(G, N, X); + false -> ok + end. + +exp_top(_G, _N, X, _NT, Acc) when element(1, X) == preproc -> + Acc; +exp_top(G, N, L, NT, Acc) when is_list(L) -> + exp_list(G, N, L, NT, Acc); +exp_top(G, N, M, NT, Acc) when is_record(M, module) -> + exp_list(G, N, get_body(M), NT, Acc); +exp_top(G, N, I, NT, Acc) when is_record(I, interface) -> + exp_list(G, N, get_body(I), NT, Acc); +exp_top(G, N, X, NT, Acc) -> + exp3(G, N, X, NT, Acc). + +exp3(_G, _N, C, _NT, Acc) when is_record(C, const) -> + [{get_id(C#const.id), 0} | Acc]; + +exp3(G, N, Op, NocType, Acc) when is_record(Op, op) -> + FuncName = get_id(Op#op.id), + + TA = case use_timeout(G,N,Op) of + true -> + 1; + false -> + 0 + end, + + case NocType of + transparent -> + Arity = length(ic:filter_params([in, inout], Op#op.params)) + TA + 1, + [{FuncName, Arity} | Acc]; + multiple -> + case getModType(G, Op, N) of + dt -> + Arity = length(ic:filter_params([in, inout], Op#op.params)) + TA + 1, + [{FuncName, Arity} | Acc]; + do -> + Arity = length(ic:filter_params([in, inout], Op#op.params)) + TA + 1, + [{FuncName, Arity} | Acc]; + spt -> + Arity = length(ic:filter_params([in, inout], Op#op.params)) + TA + 1, + [{FuncName, Arity} | Acc]; + spo -> + Arity = length(ic:filter_params([in, inout], Op#op.params)) + TA + 1, + [{FuncName, Arity} | Acc] + end; + _ -> + Arity = length(ic:filter_params([in, inout], Op#op.params)) + TA + 1, + [{FuncName, Arity} | Acc] + end; +exp3(_G, _N, A, _NT, Acc) when is_record(A, attr) -> + lists:foldr(fun(Id, Acc2) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + case A#attr.readonly of + {readonly, _} -> [{Get, 1} | Acc2]; + _ -> [{Get, 1}, {Set, 2} | Acc2] + end end, Acc, ic_forms:get_idlist(A)); + +exp3(_G, _N, _X, _NT, Acc) -> Acc. + +exp_list(G, N, L, NT, OrigAcc) -> + lists:foldr(fun(X, Acc) -> exp3(G, N, X, NT, Acc) end, OrigAcc, L). + + + + +%%------------------------------------------------------------ +%% +%% Emit stuff +%% +%% Low level generation primitives +%% + +emit_stub_func(G, N, X, Name, ArgNames, TypeList, _OutArgs) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + StubName = list_to_atom(Name), + This = mk_name(G, "Ref"), + XTuple = getNocType(G,X,N), + CallOrCast = + case is_oneway(X) of + true -> ?CAST; + _ -> ?CALL + end, + + %% Type expand operation on comments + ic_code:type_expand_op(G,N,X,Fd), + + case use_timeout(G,N,X) of + true -> + Timeout = mk_name(G,"Timeout"), + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This, Timeout| ArgNames])]), + emit(Fd, " ~p:~s(~s, ~s, ?MODULE, ~p, ~p, [~s], ~p).\n\n", + [getImplMod(G,X,N), + CallOrCast, + This, + Timeout, + XTuple, + StubName, + mk_list(ArgNames), + tk_operation_data(G, N, X, TypeList)]); + false -> + emit(Fd, "~p(~s) ->\n", + [StubName, mk_list([This | ArgNames])]), + + emit(Fd, " ~p:~s(~s, ~p, ?MODULE, ~p, [~s], ~p).\n\n", + [getImplMod(G,X,N), + CallOrCast, + This, + XTuple, + StubName, + mk_list(ArgNames), + tk_operation_data(G, N, X, TypeList)]) + end + end. + + +emit_transparent_func(G, N, X, Name, ArgNames, _TypeList, _OutArgs) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + OpName = list_to_atom(Name), + + ArgList = case use_timeout(G,N,X) of + true -> + mk_list([mk_name(G,"Ref"),mk_name(G,"Timeout")|ArgNames]); + false -> + mk_list([mk_name(G,"Ref")|ArgNames]) + end, + + %% Type expand operation on comments + ic_code:type_expand_op(G,N,X,Fd), + + emit(Fd, "~p(~s) ->\n", [OpName,ArgList]), + emit(Fd, " ~p:~s(~s).\n\n", [getImplMod(G,X,N), OpName, ArgList]) + end. + + + + + + +emit_skel_func(G, N, X, OpName, ArgNames, _TypeList, _OutArgs) -> + case getNocType(G,X,N) of + transparent -> + true; + multiple -> + true; + XTuple -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + Name = list_to_atom(OpName), + This = mk_name(G, "Ref"), + From = mk_name(G, "From"), + State = mk_name(G, "State"), + + %% Type expand handle operation on comments + ic_code:type_expand_handle_op(G,N,X,Fd), + + case is_oneway(X) of + true -> + emit(Fd, "handle_cast({~s, ~p, OE_Module, ~p, [~s]}, ~s) ->\n", + [This, XTuple, Name, mk_list(ArgNames), State]), + emit(Fd, " ~p:handle_cast({~s, ~p, OE_Module, ~p, [~s]}, ~s);\n\n", + [getImplMod(G,X,N), This, XTuple, Name, mk_list(ArgNames), State]); + false -> + emit(Fd, "handle_call({~s, ~p, OE_Module, ~p, [~s]}, ~s, ~s) ->\n", + [This, XTuple, Name, mk_list(ArgNames), From, State]), + emit(Fd, " ~p:handle_call({~s, ~p, OE_Module, ~p, [~s]}, ~s, ~s);\n\n", + [getImplMod(G,X,N), This, XTuple, Name, mk_list(ArgNames), From, State]) + end + end + end. + + + +emit_constant_func(G, Id, Val) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + N = list_to_atom(get_id(Id)), + emit_const_comment(G, Fd, Id, N), + emit(Fd, "~p() -> ~p.\n\n", [N, Val]) + end. + + +emit_const_comment(_G, F, _X, Name) -> + ic_codegen:mcomment_light(F, + [io_lib:format("Constant: ~p", [Name])]). + +%%------------------------------------------------------------ +%% +%% Utilities +%% +%% Convenient little go-get functions +%% +%%------------------------------------------------------------ + +%% The automaticly generated get and set operation names for an +%% attribute. +mk_attr_func_names(_Scope, Name) -> + {"_get_" ++ Name, "_set_" ++ Name}. + +%% Returns TK of the Get and Set attribute functions. +mk_attr_func_types(_N, X) -> + TK = ic_forms:get_tk(X), + {{TK, [], []}, {tk_void, [TK], []}}. + + + +%%------------------------------------------------------------ +%% +%% Generation utilities and common stuff +%% +%% Convenient stuff for generation +%% +%%------------------------------------------------------------ + + +%% Input is a list of parameters (in parse form) and output is a list +%% of capitalised variable names. mk_var is in icgen +mk_erl_vars(_G, Params) -> + map(fun(P) -> mk_var(get_id(P#param.id)) end, Params). + + +%% mk_list produces a nice comma separated string of variable names +mk_list([]) -> []; +mk_list([Arg | Args]) -> + Arg ++ mk_list2(Args). +mk_list2([Arg | Args]) -> + ", " ++ Arg ++ mk_list2(Args); +mk_list2([]) -> []. + + +%%------------------------------------------------------------ +%% +%% Parser utilities +%% +%% Called from the yecc parser. Expands the identifier list of an +%% attribute so that the attribute generator never has to handle +%% lists. +%% +%%------------------------------------------------------------ + + +%% Unfold identifier lists or nested lists. Note that many records +%% contain an entry named id that is a list before unfold and a single +%% id afterwards. +unfold(L) when is_list(L) -> + lists:flatten(map(fun(X) -> unfold2(X) end, L)); +unfold(X) -> unfold2(X). + +unfold2(A) when is_record(A, attr) -> + map(fun(Id) -> A#attr{id=Id} end, A#attr.id); +unfold2(M) when is_record(M, member) -> + map(fun(Id) -> M#member{id=Id} end, M#member.id); +unfold2(M) when is_record(M, case_dcl) -> + map(fun(Id) -> M#case_dcl{label=Id} end, M#case_dcl.label); +unfold2(T) when is_record(T, typedef) -> + map(fun(Id) -> T#typedef{id=Id} end, T#typedef.id ). + + + + + + +%% Export code produce for dependency function +exportDependency(G) -> + Fd = ic_genobj:stubfiled(G), + ic_codegen:export(Fd, [{oe_dependency, 0}]), + nl(Fd). + +%% Code produce for dependency function +genDependency(G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd),nl(Fd), + ic_codegen:comment(Fd, "Idl file dependency list function"), + emit(Fd, "oe_dependency() ->\n", []), + emit(Fd, " ~p.\n\n", [ic_pragma:get_dependencies(G)]). + + + + + +%%%%%% + + +getImplMod(G,X,Scope) -> %% to_atom(ic_genobj:impl(G)) | ChoicedModuleName + + %% Get actual pragma appliance scope + SpecScope = getActualScope(G,X,Scope), + + %% The "broker" option is passed + %% only by pragmas, seek for module. + case ic_pragma:getBrokerData(G,X,SpecScope) of + {Module,_Type} -> + Module; + _List -> + element(1,ic_pragma:defaultBrokerData(G)) + end. + + +getNocType(G,X,Scope) when is_record(X, interface) -> %% default | specified + OpList = getAllOperationScopes(G,Scope), + getNocType2(G,X,OpList); +getNocType(G,X,Scope) -> %% transparent | {extraarg1,....,extraargN} + getNocType3(G,X,Scope). + +getNocType2(G,X,List) -> + getNocType2(G,X,List,[]). + +getNocType2(_,_,[],Found) -> + selectTypeFromList(Found); +getNocType2(G,X,[OpScope|OpScopes],Found) -> + getNocType2(G,X,OpScopes,[getNocType3(G,X,OpScope)|Found]). + +getNocType3(G,X,Scope) -> %% transparent | {extraarg1,....,extraargN} + + %% Get actual pragma appliance scope + SpecScope = getActualScope(G,X,Scope), + + %% The "broker" option is passed + %% only by pragmas, seek for type. + case ic_pragma:getBrokerData(G,X,SpecScope) of + {_Module,Type} -> + Type; + List -> + selectTypeFromList(List) %%transparent/multiple + end. + + +getModType(G,X,Scope) -> %% default | specified + + %% Get actual pragma appliance scope + SpecScope = getActualScope(G,X,Scope), + + %% The "broker" option is passed + %% only by pragmas, seek for brokerdata. + case ic_pragma:getBrokerData(G,X,SpecScope) of + {Module,Type} -> + case Module == ic_genobj:impl(G) of + true -> + case Type of + transparent -> + dt; %% default + transparent + _ -> + do %% default + opaque + end; + false -> + case Type of + transparent -> + spt; %% specified + transparent + _ -> + spo %% specified + opaque + end + end; + _List -> + dt + end. + + + +%%%% +%% +%% Returns a list of ALL operation full +%% scoped names local and inherited +%% from other interfaces +%% + +getAllOperationScopes(G,Scope) -> + getOperationScopes(G,Scope) ++ + getInhOperationScopes(G,Scope). + + +getOperationScopes(G,Scope) -> + getOpScopes(G, + Scope, + ets:match(ic_genobj:pragmatab(G),{op,'$0',Scope,'_','_'}), + []). + +getOpScopes(_,_,[],OpScopes) -> + OpScopes; +getOpScopes(G,Scope,[[Name]|Names],Found) -> + getOpScopes(G,Scope,Names,[[Name|Scope]|Found]). + + +getInhOperationScopes(G,Scope) -> + getInhOpScopes1(G, + Scope, + ets:match(ic_genobj:pragmatab(G),{inherits,Scope,'$1'}), + []). + +getInhOpScopes1(G,_Scope,[],OpScopes) -> + getInhOpScopes2(G,OpScopes); +getInhOpScopes1(G,Scope,[[SC]|SCs],Found) -> + getInhOpScopes1(G,Scope,SCs,[SC|Found]). + + +getInhOpScopes2(G,Scopes) -> + getInhOpScopes2(G,Scopes,[]). + +getInhOpScopes2(_G,[],Found) -> + Found; +getInhOpScopes2(G,[SC|SCs],Found) -> + getOperationScopes(G,SC) ++ getInhOpScopes2(G,SCs,Found). + +%% +%% +%%%% + + + +%%%% +%% +%% +%% Seek the actual operation scope : +%% +%% * if the operation is inherited, get the real scope for it +%% +%% * if the operation has a specific pragma, apply the real +%% scope, otherwise return the including scope +%% +getActualScope(G, X, Scope) when is_record(X, op) -> + OpScope = getRealOpScope(G,X,Scope), + case ets:match(ic_genobj:pragmatab(G),{codeopt_specific,OpScope}) of + [[]] -> + OpScope; + _ -> + Scope + end; +getActualScope(_G, _X, N) -> + N. + +%% +%% Just seek and return the scope for the operation +%% where it were originaly defined +%% +getRealOpScope(G,X,N) when is_record(X, op) -> + Ptab = ic_genobj:pragmatab(G), + Id = get_id2(X), + + case ets:match(Ptab,{op,Id,N,'_','_'}) of + [[]] -> + [Id|N]; + _ -> + getRealOpScope(G, Ptab, X, N, Id, ets:match(Ptab,{inherits,N,'$1'})) + end; +getRealOpScope(_G,_X,N) -> + N. + +getRealOpScope(_G, _S, _X, N, Id, []) -> + [Id|N]; +getRealOpScope(G, S, X, N, Id, [[OS]|OSs]) -> + case ets:match(S,{op,Id,OS,'_','_'}) of + [[]] -> + [Id|OS]; + _ -> + getRealOpScope(G, S, X, N, Id, OSs) + end. + +selectTypeFromList([]) -> + transparent; +selectTypeFromList([{_,transparent}|Rest]) -> + selectTypeFromList(Rest); +selectTypeFromList([transparent|Rest]) -> + selectTypeFromList(Rest); +selectTypeFromList([_|_Rest]) -> + multiple. + + + +getCallErr() -> + {'ERROR' ,"Bad Operation -- handle call"}. + +getCastErr() -> + {'ERROR' ,"Bad Operation -- handle cast"}. + +getInfoErr() -> + {'ERROR' ,"Bad Operation -- handle info"}. + + + + + + +%% +%% Type code access utilities +%% + +tk_operation_data(G, N, X, TL) -> + case print_tk(G,N,X) of + true -> + TL; + false -> + no_tk + end. + +tk_interface_data(G, N, X) -> + InfoList = + foldr(fun({_Name, Body}, Acc) -> + get_if(G,N,Body)++Acc end, + get_if(G,N,get_body(X)), + X#interface.inherit_body), + case InfoList of + [] -> + no_tk; %%%%%%%% Should be changed to [] <<<<<<<<<<<<<<<<<<<<<<<<<<< Warning ! + _ -> + InfoList + end. + + +print_tk(G, N, X) when is_record(X, op)-> %% operation + case getNocType(G,X,N) of + transparent -> + false; + multiple -> + false; + _XTuple -> %%check if there are any USETK pragmas + operation_usetk(G,N,X) + end; +print_tk(_G, _N, _X) -> %% error + false. + + +operation_usetk(G,N,X) -> + PTab = ic_genobj:pragmatab(G), + OTab = ic_genobj:optiontab(G), + OpName = get_id2(X), +% SID = ic_util:to_colon(N), + Res = case use_tk(OTab,[N]) of + {ok,N} -> + true; + false -> + %% Look if there is an operation with that name + %% which can be found in an included file. + case ets:match(PTab,{file_data_included,'_','_',op,'$3',OpName,'_','_','_'}) of + [] -> + false; + ScopeList -> + case use_tk(OTab,ScopeList) of + %% There is an operation with that name, + %% look if it is inherited by interface "N" + {ok,FoundScope} -> + ic_pragma:is_inherited_by(FoundScope,N,PTab); + false -> + false + end + end + end, + Res. + + +use_tk(_,[]) -> + false; +use_tk(OTab,[[Scope]|Scopes]) -> + SID = ic_util:to_colon(Scope), + case ets:match(OTab,{{option,{use_tk,SID}},true}) of + [] -> + case ets:match(OTab,{{option,{use_tk,"::"++SID}},true}) of + [] -> + use_tk(OTab,Scopes); + _ -> + {ok,Scope} + end; + _ -> + {ok,Scope} + end; +use_tk(OTab,[Scope|Scopes]) -> + SID = ic_util:to_colon(Scope), + case ets:match(OTab,{{option,{use_tk,SID}},true}) of + [] -> + case ets:match(OTab,{{option,{use_tk,"::"++SID}},true}) of + [] -> + use_tk(OTab,Scopes); + _ -> + {ok,Scope} + end; + _ -> + {ok,Scope} + end. + + + + + +mark_not_transparent(G,N) -> + + %% Mark that there are multiple + %% functions in interface + S = ic_genobj:pragmatab(G), + ets:insert(S,{no_transparent,N}). + + +transparent(G) -> + + S = ic_genobj:pragmatab(G), + case ets:match_object(S,{no_transparent,'$0'}) of + [] -> + true; + _ -> + false + end. + diff --git a/lib/ic/src/ic_options.erl b/lib/ic/src/ic_options.erl new file mode 100644 index 0000000000..8d17fc1753 --- /dev/null +++ b/lib/ic/src/ic_options.erl @@ -0,0 +1,363 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_options). + +-include_lib("ic/src/ic.hrl"). +-include_lib("kernel/include/file.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([defaultBe/0, float_to_version/1, get_opt/2, add_opt/3, + read_cfg/2, which_opts/1, allowed_opt/2]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% +%% Option handling +%% +%% Valid options are: (those with * is NotYetImpl) +%% +%% pedantic - makes the compiler really nitty-gritty about its input +%% +%% Wall - those warning options that we feel an IDL programmer should +%% care about. Not as picky as pedantic +%% +%% warn_multi_mod - warn if several modules are declared in the same +%% IDL file +%% +%% warn_nested_mod - warn if there are nested modules. This is not a +%% problem but it breakes the rule that modules are put into one file +%% each. +%% +%% warn_name_shadow - warn if identifiers are shadow through inherited +%% interfaces. Default is true. +%% +%% warn_quoted_atom - warn if atoms needs quote, this makes Erlang +%% code less nice but is certainly no error. +%% +%% nowarn - suppress all warning messages. Will still output warnings +%% if silent2 option is used +%% +%% always_outargs - force object server implementation return the +%% tuple {RetVal, OutArgs, NewState} even if there are no OutArgs. If +%% this option is not set then such an operation implementation is +%% assumed to return {RetVal, NewState} +%% +%% use_proc_dict - use the process dictionary in the client +%% stubs. This means that client stubs return RetVal instead of {ok, +%% RetVal, OutArgs} and that corba:get_outargs() returns OutArgs. The +%% out arguments are stored with the key '$corba_outargs'. +%% +%% module_group - use the top module as file name for both skeletons +%% and stubs. Default value is false which means that each interface +%% is put in a separate file. +%% +%% skel_module_group - group all interfaces in a module in one +%% skeleton file as opposed to one skeleton file for each +%% interface. Defaults to false. +%% +%% stub_module_group - group all interface stubs from a module in one +%% stub file as opposed to one stub file for each interface. Default +%% is false. +%% +%% *help - prints a small summary of the compiler usage +%% +%% silent - suppresses all messages from the compiler +%% +%% silent2 - suppresses all messages from the compiler and returns all +%% warnings or errors as lists. Returns {ok, WarnList} or {error, +%% WarnList, ErrList} +%% +%% *noexec - runs the compiler but does not open files or write to +%% files. +%% +%% {serv, <ModName>} - sets the name of the implementation skeleton +%% file. This defaults to ModName_skel. +%% +%% {impl, <ModName>} - sets the name of the interface server +%% implementation module name. This defaults to InterfaceName_impl +%% +%% {outdir, Dir} - use Dir as the directory to put all generated +%% files. +%% +%% {servdir, Dir} - put all generated skel files in the directory Dir. +%% +%% {stubdir, Dir} - put all generated stub files in the directory Dir. +%% +%% {this, InterfaceOrOpName} - puts the OE_THIS parameter into the +%% impl. call. This option can be used both on whole interfaces an on +%% distinct operations. Fullscoped names must be used (as in {this, +%% "M1::I1::Op"}). The option can be given in 3 ways: {this, Name} +%% means this will be added to all matching Name or as {{this, Name}, +%% true} or this can explicitly be asked to be left out as in {{this, +%% Name}, false} which enables OE_THIS to be passed to all ops of an +%% interface except those set by the false flag. +%% +%% cfgfile - sets the name of the config file that is read at +%% startup. The order of the different ways to set options is: default +%% setting, configuration file, options given when generator is +%% called. Default name for this file is .ic_config +%% +%% serv_last_call - tells what the last handle_call clause should +%% do. It can have the values exception, which makes the last clause +%% return a CORBA exception and exit which does not generate a last clause +%% (which will make the server crash on an unknown call) +%% +%% +%% -- UNDOCUMENTED -- +%% +%% debug - prints debug information +%% +%% tokens - prints the tokens from the tokenizer and then exit +%% +%% form - prints the form from the parser and then exit +%% +%% tform - form returned from type check +%% +%% time - if true then time is measured during compilation +%% +%% +%%-------------------------------------------------------------------- +allowed_opt(default_opts, _V) -> true; +allowed_opt(debug, V) -> is_bool(V); +allowed_opt(tokens, V) -> is_bool(V); +allowed_opt(form, V) -> is_bool(V); +allowed_opt(tform, V) -> is_bool(V); +allowed_opt(time, V) -> is_bool(V); +allowed_opt(maxerrs, V) -> is_intorinfinity(V); +allowed_opt(maxwarns, V) -> is_intorinfinity(V); +allowed_opt(nowarn, V) -> is_bool(V); +allowed_opt(show_opts, V) -> is_bool(V); + +allowed_opt(help, V) -> is_bool(V); +allowed_opt('Wall', V) -> is_bool(V); +allowed_opt(warn_multi_mod, V) -> is_bool(V); +allowed_opt(warn_quoted_atom, V) -> is_bool(V); +allowed_opt(warn_nested_mod, V) -> is_bool(V); +allowed_opt(warn_name_shadow, V) -> is_bool(V); +allowed_opt(module_group, V) -> is_bool(V); +allowed_opt(skel_module_group, V) -> is_bool(V); +allowed_opt(stub_module_group, V) -> is_bool(V); +allowed_opt(always_outargs, V) -> is_bool(V); +allowed_opt(pedantic, V) -> is_bool(V); +%%allowed_opt(gen_serv, V) -> is_bool(V); +%%allowed_opt(gen_stub, V) -> is_bool(V); +allowed_opt(gen_hrl, V) -> is_bool(V); +allowed_opt(serv_last_call, exception) -> true; +allowed_opt(serv_last_call, exit) -> true; +allowed_opt(silent, V) -> is_bool(V); +allowed_opt(silent2, V) -> is_bool(V); +allowed_opt({serv, _}, _V) -> true; +allowed_opt({impl, _}, _V) -> true; +allowed_opt(outdir, _V) -> true; +allowed_opt(servdir, _V) -> true; +allowed_opt(stubdir, _V) -> true; +allowed_opt(cfgfile, _V) -> true; +allowed_opt(use_preproc, V) -> is_bool(V); +allowed_opt(preproc_cmd, _V) -> true; +allowed_opt(preproc_flags, _V) -> true; +allowed_opt(this, _V) -> true; +allowed_opt({this, _}, V) -> is_bool(V); +allowed_opt(from, _V) -> true; +allowed_opt({from, _}, V) -> is_bool(V); +allowed_opt(handle_info, _V) -> true; +allowed_opt({handle_info, _}, V) -> is_bool(V); +allowed_opt(timeout, _V) -> true; +allowed_opt({timeout, _}, V) -> is_bool(V); +allowed_opt(c_timeout, {V1, V2}) -> is_int(V1) and is_int(V2); +allowed_opt(c_timeout, V) -> is_int(V); +allowed_opt(c_report, V) -> is_bool(V); +allowed_opt(scoped_op_calls, V) -> is_bool(V); +% Compatibility option (semantic check limitation) +allowed_opt(scl, V) -> is_bool(V); +% Added switches for non corba generation +allowed_opt(flags, V) -> is_int(V); +allowed_opt(be, erl_corba) -> true; +allowed_opt(be, erl_template) -> true; +allowed_opt(be, erl_genserv) -> true; +allowed_opt(be, c_genserv) -> true; +allowed_opt(be, erl_plain) -> true; +allowed_opt(be, c_server) -> true; +allowed_opt(be, c_client) -> true; +allowed_opt(be, java) -> true; +% Noc backend +allowed_opt(be, noc) -> true; +allowed_opt({broker,_},{_,transparent}) -> true; +allowed_opt({broker,_},{_,Term}) -> is_term(Term); +allowed_opt({use_tk,_},V) -> is_bool(V); +% +% Multiple be +allowed_opt(multiple_be, _List) -> true; +% +allowed_opt(precond, {_M, _F}) -> true; +allowed_opt({precond, _}, {_M, _F}) -> true; +allowed_opt(postcond, {_M, _F}) -> true; +allowed_opt({postcond, _}, {_M, _F}) -> true; +allowed_opt(no_codechange, V) -> is_bool(V); +allowed_opt(user_protocol, _V) -> true; +allowed_opt(light_ifr, V) -> is_bool(V); +allowed_opt(_, _) -> false. + + +-define(DEFAULTCFGFILE, ".ic_config"). + +which_opts(G) -> + ets:match(G#genobj.options, {{option, '$1'}, '$2'}). + +add_opt(G, KList, Val) when is_list(KList) -> + lists:foreach(fun({K, V}) -> add_opt(G, K, V); + (K) -> add_opt(G, K, Val) end, + KList); + +add_opt(G, servdir, V) -> + do_add_opt(G, servdir, assure_directory(G, ic_util:to_list(V))); +add_opt(G, stubdir, V) -> + do_add_opt(G, stubdir, assure_directory(G, ic_util:to_list(V))); +add_opt(G, K, V) -> + do_add_opt(G, K, V). + + +assure_directory(_G, Dir) -> + Dirs = filename:split(Dir), + check_dirs(Dirs, [], filename:pathtype(Dir)). + +check_dirs([X | Xs], SoFar, Type) -> + New = if SoFar == [], Type /= absolute -> + X; + true -> + filename:join(SoFar, X) + end, + assert_dir(New), + check_dirs(Xs, New, Type); +check_dirs([], SoFar, _Type) -> + SoFar. + +assert_dir(D) -> + case file:read_file_info(D) of + {ok, X} when X#file_info.type == directory -> ok; + _ -> case file:make_dir(D) of + ok -> ok; + _ -> exit({could_not_create, D}) + end + end. + +do_add_opt(G, handle_info, V) -> + ?insert(G#genobj.options, {option, {handle_info, V}}, true); +do_add_opt(G, {handle_info, V}, false) -> + ?insert(G#genobj.options, {option, {handle_info, V}}, force_false); +do_add_opt(G, timeout, V) -> + ?insert(G#genobj.options, {option, {timeout, V}}, true); +do_add_opt(G, {timeout, V}, false) -> + ?insert(G#genobj.options, {option, {timeout, V}}, force_false); +do_add_opt(G, this, V) -> + ?insert(G#genobj.options, {option, {this, V}}, true); +do_add_opt(G, {this, V}, false) -> + ?insert(G#genobj.options, {option, {this, V}}, force_false); +do_add_opt(G, from, V) -> + ?insert(G#genobj.options, {option, {from, V}}, true); +do_add_opt(G, {from, V}, false) -> + ?insert(G#genobj.options, {option, {from, V}}, force_false); +do_add_opt(G, scoped_op_calls, V) when V /= true, V /= false -> + ?insert(G#genobj.options, {option, {scoped_op_calls, V}}, false); +do_add_opt(G, K, V) -> + case allowed_opt(K, V) of + true -> + case expand_opt(K) of + L when is_list(L) -> + add_opt(G, L, V); + _ -> + %%io:format("Add opt: ~p ~p~n", [K, V]), + ?insert(G#genobj.options, {option, K}, V) + end; + _ -> + ic_error:warn(G, {illegal_opt, K}) + end. + +get_opt(G, K) -> + case ets:lookup(G#genobj.options, {option, K}) of + [] -> false; + [{{_, K}, V}] -> V + end. + +expand_opt(pedantic) -> [warn_multi_mod, warn_quoted_atom, always_outargs]; +expand_opt(module_group) -> [skel_module_group, stub_module_group]; +expand_opt('Wall') -> [warn_multi_mod, warn_nested_mod, warn_name_shadow]; +expand_opt(outdir) -> [servdir, stubdir]; +expand_opt(default_opts) -> + ['Wall', gen_hrl, {serv_last_call, exception}, + {outdir, []}, use_preproc, {preproc_cmd, "erl"}, + {preproc_flags, ""}, {maxerrs, 10}, {maxwarns, infinity}]; +%% gcc preproc command {preproc_cmd, "gcc -x c++ -E"} +expand_opt(Opt) -> Opt. + + +%% Use this if user not provide +%% a backend. +defaultBe() -> erl_corba. + + +%% +%% Read any config file +read_cfg(G, Opts) -> + Name = case lists:keysearch(cfgfile, 1, Opts) of + {value, {_, N}} -> ic_util:to_list(N); + _ -> ?DEFAULTCFGFILE + end, + case file:consult(Name) of + {ok, OptList} -> + add_opt(G, OptList, true); + _X when Name == ?DEFAULTCFGFILE -> ok; +%% {error, X} -> +%% ic_error:warn(G, {cfg_open, X, Name}); + X -> ic_error:warn(G, {cfg_open, X, Name}) + end. + + +float_to_version({_,_,Str}) -> Str. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- +is_bool(true) -> true; +is_bool(false) -> true; +is_bool(_) -> false. + +is_int(V) when is_integer(V) -> true; +is_int(_) -> false. + +is_intorinfinity(X) when is_integer(X) -> true; +is_intorinfinity(infinity) -> true; +is_intorinfinity(_X) -> false. + + +is_term(Term) when is_tuple(Term) -> true; +is_term(_NoTerm) -> false. + diff --git a/lib/ic/src/ic_plainbe.erl b/lib/ic/src/ic_plainbe.erl new file mode 100644 index 0000000000..7b3e3dc859 --- /dev/null +++ b/lib/ic/src/ic_plainbe.erl @@ -0,0 +1,355 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_plainbe). + + +-export([do_gen/3]). +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ + +-import(ic_util, [mk_var/1, mk_oe_name/2, to_atom/1, to_list/1]). +-import(ic_forms, [get_id/1, get_id2/1, get_body/1]). +-import(ic_codegen, [emit/3, nl/1]). + +-import(lists, [foreach/2, map/2]). + +-include("icforms.hrl"). +-include("ic.hrl"). + +%%------------------------------------------------------------ +%% +%% Generate the client side Erlang stubs. +%% +%% Each module is generated to a separate file. +%% +%% Export declarations for all interface functions must be +%% generated. Each function then needs to generate a function head and +%% a body. IDL parameters must be converted into Erlang parameters +%% (variables, capitalised) and a type signature list must be +%% generated (for later encode/decode). +%% +%%------------------------------------------------------------ + + +do_gen(G, File, Form) -> + G2 = ic_file:filename_push(G, [], mk_oe_name(G, + ic_file:remove_ext(to_list(File))), + erlang), + gen_head(G2, [], Form), + exportDependency(G2), + gen(G2, [], Form), + genDependency(G2), + ic_file:filename_pop(G2, erlang), + ok. + + +gen(G, N, [X|Xs]) when is_record(X, preproc) -> + NewG = ic:handle_preproc(G, N, X#preproc.cat, X), + gen(NewG, 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 = [get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, get_body(X)), + G3 = ic_file:filename_pop(G2, CD), + gen(G3, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, interface) -> + %% Add inheritence data to pragmatab + ic_pragma:add_inh_data(G,N,X), + G2 = ic_file:filename_push(G, N, X, erlang), + N2 = [get_id2(X) | N], + gen_head(G2, N2, X), + gen(G2, N2, get_body(X)), + foreach(fun({_Name, Body}) -> gen(G2, N2, Body) end, + X#interface.inherit_body), + G3 = ic_file:filename_pop(G2, erlang), + gen(G3, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, const) -> +% N2 = [get_id2(X) | N], + emit_constant_func(G, X#const.id, X#const.val), + gen(G, N, Xs); %% N or N2? + +gen(G, N, [X|Xs]) when is_record(X, op) -> + {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X), + emit_func(G, N, X, Name, ArgNames, TypeList, OutArgs), + gen(G, N, Xs); + + +gen(G, N, [X|Xs]) when is_record(X, attr) -> + emit_attr(G, N, X, fun emit_func/7), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) when is_record(X, except) -> + icstruct:except_gen(G, N, X, erlang), + gen(G, N, Xs); + +gen(G, N, [X|Xs]) -> + case may_contain_structs(X) of + true -> icstruct:struct_gen(G, N, X, erlang); + false -> ok + end, + gen(G, N, Xs); + +gen(_G, _N, []) -> ok. + + +may_contain_structs(X) when is_record(X, typedef) -> true; +may_contain_structs(X) when is_record(X, struct) -> true; +may_contain_structs(X) when is_record(X, union) -> true; +may_contain_structs(_X) -> false. + + +%%------------------------------------------------------------ +%% +%% Export stuff +%% +%% Gathering of all names that should be exported from a stub +%% file. +%% + + +gen_head_special(G, N, X) when is_record(X, interface) -> + Fd = ic_genobj:stubfiled(G), + + foreach(fun({Name, Body}) -> + ic_codegen:comment(Fd, "Exports from ~p", + [ic_util:to_colon(Name)]), + ic_codegen:export(Fd, exp_top(G, N, Body, [])), + nl(Fd) + end, X#interface.inherit_body), + Fd; +gen_head_special(_G, _N, _X) -> ok. + + + +%% Shall generate all export declarations +gen_head(G, N, X) -> + case ic_genobj:is_stubfile_open(G) of + true -> + F = ic_genobj:stubfiled(G), + ic_codegen:comment(F, "Interface functions"), + ic_codegen:export(F, exp_top(G, N, X, [])), + nl(F), + gen_head_special(G, N, X); + false -> ok + end. + +exp_top(_G, _N, X, Acc) when element(1, X) == preproc -> + Acc; +exp_top(G, N, L, Acc) when is_list(L) -> + exp_list(G, N, L, Acc); +exp_top(G, N, M, Acc) when is_record(M, module) -> + exp_list(G, N, get_body(M), Acc); +exp_top(G, N, I, Acc) when is_record(I, interface) -> + exp_list(G, N, get_body(I), Acc); +exp_top(G, N, X, Acc) -> + exp3(G, N, X, Acc). + +exp3(_G, _N, C, Acc) when is_record(C, const) -> + [{get_id(C#const.id), 0} | Acc]; + +exp3(_G, _N, Op, Acc) when is_record(Op, op) -> + FuncName = get_id(Op#op.id), + Arity = length(ic:filter_params([in, inout], Op#op.params)), + [{FuncName, Arity} | Acc]; + +exp3(_G, _N, A, Acc) when is_record(A, attr) -> + lists:foldr(fun(Id, Acc2) -> + {Get, Set} = mk_attr_func_names([], get_id(Id)), + case A#attr.readonly of + {readonly, _} -> [{Get, 1} | Acc2]; + _ -> [{Get, 1}, {Set, 2} | Acc2] + end end, Acc, ic_forms:get_idlist(A)); + +exp3(_G, _N, _X, Acc) -> Acc. + +exp_list(G, N, L, OrigAcc) -> + lists:foldr(fun(X, Acc) -> exp3(G, N, X, Acc) end, OrigAcc, L). + + + + +%%------------------------------------------------------------ +%% +%% Emit stuff +%% +%% Low level generation primitives +%% + + +emit_func(G, _N, X, Name, ArgNames, _TypeList, OutArgs) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + OpName = list_to_atom(Name), + ArgList = mk_list(ArgNames), + emit_op_comment(G, Fd, X, OpName, ArgNames, OutArgs), + emit(Fd, "~p(~s) ->\n", [OpName,ArgList]), + emit(Fd, " ~p:~p(~s).\n\n", [to_atom(ic_genobj:impl(G)), OpName, ArgList]) + end. + +emit_attr(G, N, X, F) -> + XX = #id_of{type=X}, + {GetType, SetType} = mk_attr_func_types(N, X), + lists:foreach(fun(Id) -> + X2 = XX#id_of{id=Id}, + {Get, Set} = mk_attr_func_names(N, get_id(Id)), + F(G, N, X2, Get, [], GetType, []), + case X#attr.readonly of + {readonly, _} -> ok; + _ -> + F(G, N, X2, Set, [ic_util:mk_name(G, "Value")], + SetType, []) + end end, ic_forms:get_idlist(X)). + +emit_constant_func(G, Id, Val) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + N = list_to_atom(get_id(Id)), + emit_const_comment(G, Fd, Id, N), + emit(Fd, "~p() -> ~p.\n\n", [N, Val]) + end. + + +emit_const_comment(_G, F, _X, Name) -> + ic_codegen:mcomment_light(F, + [io_lib:format("Constant: ~p", [Name])]). + + +emit_op_comment(G, F, X, Name, InP, OutP) -> + ic_codegen:mcomment_light(F, + [io_lib:format("~s: ~p", [get_title(X), Name]), + "", + get_returns(G, X, InP, OutP) | + get_raises(X)]). + +get_title(X) when is_record(X, attr) -> "Attribute Operation"; +get_title(_X) -> "Operation". + +get_raises(X) when is_record(X, op) -> + if X#op.raises == [] -> []; + true -> + [" Raises: " ++ + mk_list(lists:map(fun(E) -> ic_util:to_colon(E) end, X#op.raises))] + end; +get_raises(_X) -> []. + +get_returns(_G, _X, _InP, []) -> + " Returns: RetVal"; +get_returns(G, _X, _InP, OutP) -> + " Returns: "++mk_list(["RetVal" | mk_erl_vars(G, OutP)]). + + + + +%%------------------------------------------------------------ +%% +%% Utilities +%% +%% Convenient little go-get functions +%% +%%------------------------------------------------------------ + +%% The automaticly generated get and set operation names for an +%% attribute. +mk_attr_func_names(_Scope, Name) -> + {"_get_" ++ Name, "_set_" ++ Name}. + +%% Returns TK of the Get and Set attribute functions. +mk_attr_func_types(_N, X) -> + TK = ic_forms:get_tk(X), + {{TK, [], []}, {tk_void, [TK], []}}. + + + +%%------------------------------------------------------------ +%% +%% Generation utilities and common stuff +%% +%% Convenient stuff for generation +%% +%%------------------------------------------------------------ + + +%% Input is a list of parameters (in parse form) and output is a list +%% of capitalised variable names. mk_var is in icgen +mk_erl_vars(_G, Params) -> + map(fun(P) -> mk_var(get_id(P#param.id)) end, Params). + + +%% mk_list produces a nice comma separated string of variable names +mk_list([]) -> []; +mk_list([Arg | Args]) -> + Arg ++ mk_list2(Args). +mk_list2([Arg | Args]) -> + ", " ++ Arg ++ mk_list2(Args); +mk_list2([]) -> []. + + +%%------------------------------------------------------------ +%% +%% Parser utilities +%% +%% Called from the yecc parser. Expands the identifier list of an +%% attribute so that the attribute generator never has to handle +%% lists. +%% +%%------------------------------------------------------------ + + + + +%% Export code produce for dependency function +exportDependency(G) -> + Fd = ic_genobj:stubfiled(G), + ic_codegen:export(Fd, [{oe_dependency, 0}]), + nl(Fd). + +%% Code produce for dependency function +genDependency(G) -> + Fd = ic_genobj:stubfiled(G), + nl(Fd),nl(Fd), + ic_codegen:comment(Fd, "Idl file dependency list function"), + emit(Fd, "oe_dependency() ->\n", []), + emit(Fd, " ~p.\n\n", [ic_pragma:get_dependencies(G)]). + + + + +extract_info(G, _N, X) when is_record(X, op) -> + Name = get_id2(X), + InArgs = ic:filter_params([in,inout], X#op.params), + OutArgs = ic:filter_params([out,inout], X#op.params), + ArgNames = mk_erl_vars(G, InArgs), + TypeList = {ic_forms:get_tk(X), + map(fun(Y) -> ic_forms:get_tk(Y) end, InArgs), + map(fun(Y) -> ic_forms:get_tk(Y) end, OutArgs) + }, + {Name, ArgNames, TypeList, OutArgs}. diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl new file mode 100644 index 0000000000..db06118d32 --- /dev/null +++ b/lib/ic/src/ic_pp.erl @@ -0,0 +1,2139 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_pp). + +-export([run/2]). + +-define(is_number(X), X >= $0, X =< $9). +-define(is_upper(X), X >= $A, X =< $Z). +-define(is_lower(X), X >= $a, X =< $z). +-define(is_underline(X), X == $_). +-define(is_tab(X), X == 9). +-define(is_space(X), X == 32). +-define(tab, 9). +-define(space, 32). + + +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +%% Preprocessor +%% +%% This preprocessor is equivalent to the gcc-preprocessor. It takes a file name and +%% a list of preprocessor flags as an input and returns a processed text file. +%% +%% The processing is done in two phases. +%% In the first phase the input file is tokenised into a list where all comments are +%% replaced by a space and all "backslash-newline" sequences are removed. +%% +%% In the second phase all macros are expanded. + +%% %% %% NOTE: #if, #else, and #elif are not yet implemented. +%% Only '#if 0' is implemented to be possible to keep old code as a comment for +%% future refence by putting '#if 0' before it and '#endif' after it. +%% +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== + + +%%====================================================================================== +%% Variables which are used throughout the program: +%% ------------------------------------------------ +%% +%% Command A preprocessor command +%% Current Temporary variable used when tokenising the file +%% Defs The currently valid macro definitions +%% Err The current list of errors = [{file, line number, error text}, ...] +%% File The tokenised file (or what remains of it when expanding the macros) +%% Flags The preprocessor flags +%% FN or FileName Tbe name of the current file +%% IfCou Used for ifdef/ifndef/endif values: check_all | {endif, Endif, IfLine} +%% Endif = number of matching endif's yet to be found +%% Ifline = the line number for the the first found ifdef/ifndef +%% IncDir Directories to be searched for included files +%% IncFile Stack of included files +%% IncLine The line numer of an include +%% L The current line number +%% Name Name of a macro +%% Nl Number of encountered newlines +%% No_of_para Numer of parameters of the currently expanded macro +%% Out The result of the second step +%% Parameters The parameters of the currently expanded macro +%% PrevFile The name of the "parent" file which includes the currently expanded file +%% Rem Remaining of the file currently being expanded +%% Removed The tokens removed, used when removing tokens to the end of a line +%% Result The current result of something +%% SelfRef List of variables which shoud not be expanded at the rescan to avoid +%% endless loops due to self referencing +%% Str Temporary string +%% Text A variable used for string handling, e.g at error handling +%% Tokens Temoprary list when tokenising +%% War The current list of warnings = [{file, line number, warning text}, ...] +%% X Temporary variable used when the value is not important +%% Y Temporary variable used when the value is not important +%% +%%====================================================================================== + + + + +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +%% The main entry for the preprocessor +%% +%% +%% Output {ok, Out, War} | {error, Err} +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +run(FileName, Flags) when is_atom(FileName) -> + run(atom_to_list(FileName), Flags); + +run(FileName, Flags) -> + IncDir = include_dir(Flags), + + case catch file:read_file(FileName) of + {ok, Bin} -> + FileList = binary_to_list(Bin), + run(FileList, FileName, IncDir, Flags); + {error, _} -> + Text = "No such file or directory", + {error, [FileName ++ ": " ++ Text]} + end. + + +run(FileList, FileName, IncDir, Flags) -> + %%---------------------------------------------------------- + %% Run the first phase, i.e tokenise the file + %%---------------------------------------------------------- + File = tokenise(FileList, FileName), + + %%---------------------------------------------------------- + %% Run the second phase, i.e expand macros + %%---------------------------------------------------------- + {Out, Err, War, _Defs, IfCou} = expand(File, FileName, IncDir, Flags), + + %%---------------------------------------------------------- + %% Check if all #if #ifdef #ifndef have a matching #endif + %%---------------------------------------------------------- + IfError = case IfCou of + {endif, Endif, IfLine} when Endif > 0 -> + [{FileName, IfLine, "unterminated `#if' conditional"}]; + _ -> + [] + end, + + Err2 = Err++IfError, + + case Err2 of + [] -> + {ok, lists:flatten(lists:reverse(Out)), lists:reverse(War)}; + _ -> + {error, lists:reverse(Err2)} + end. + +%%====================================================================================== +%% The entry for all included files +%% +%% +%% Output {Out, Defs, Err, War} +%%====================================================================================== +run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir) -> + + %%---------------------------------------------------------- + %% Run the first phase, i.e tokenise the file + %%---------------------------------------------------------- + [PrevFile | _T] = IncFile, + {File, FileInfoStart, FileInfoEnd} = + tokenise(FileList, FileName, IncLine, PrevFile), + + %%---------------------------------------------------------- + %% Run the second phase, i.e expand macros + %%---------------------------------------------------------- + + %% Try first pass without file info start/end + {OutT, ErrT, WarT, DefsT, IfCouT} = + expand(File, Defs, Err, War, [FileName|IncFile], IncDir), + + {Out2, Err2, War2, Defs2, IfCou2} = + case only_nls(OutT) of + true -> %% The file is defined before + {["\n"], ErrT, WarT, DefsT, IfCouT}; + false -> %% The file is not defined before, try second pass + expand([FileInfoStart|File]++FileInfoEnd, Defs, Err, War, [FileName|IncFile], IncDir) + end, + + %%---------------------------------------------------------- + %% Check if all #if #ifdef #ifndef have a matching #endif + %%---------------------------------------------------------- + IfError = case IfCou2 of + {endif, Endif, IfLine} when Endif > 0 -> + [{FileName, IfLine, "unterminated `#if' conditional"}]; + _ -> + [] + end, + + {Out2, Defs2, Err2++IfError, War2}. + + + +%% Return true if there is no data +%% other than new lines +only_nls([]) -> + true; +only_nls(["\n"|Rem]) -> + only_nls(Rem); +only_nls(["\r","\n"|Rem]) -> + only_nls(Rem); +only_nls([_|_Rem]) -> + false. + + + + + + + + + + +%%=================================================================================== +%%=================================================================================== +%%=================================================================================== +%% Tokenise the file +%% +%% +%% Output: File +%% +%% Description: +%% The input file is tokenised into a list where all comments are replaced +%% by a space and all "backslash-newline" sequences are removed. +%% +%% A file information is added at start and end of an included file to set the +%% current file name and line number. +%% +%% +%% A token consists of: +%% -------------------- +%% +%% {char, Char} special characters like ()[]{},!%& etc +%% {command,Command} a macro command +%% {expanded,Str} an expanded variable, used to prevent infinite loops +%% at self reference +%% {file_info,FI} start and end information of a file +%% FI is a string of the following format: +%% "# Line FileName Int" were Int is +%% 1 if start of an included file, +%% 2 when returning to "parent" file +%% {nl, L} newline +%% {number,Num) variable, a string starting with a number +%% {self_ref,Var} to allow reference to a variable again, used when expanding +%% self refering macros +%% space a space +%% space_exp a space, special notation to prevent not wanted concatination +%% {string, Str} a (tail of a) string constant +%% {string_part, Str} a head of a string constant defined on several consecutive lines +%% {sys_head, Str} (tail of) the file name of included system file +%% {sys_head_part , Str} the file name of included system file +%% {var,Var} variable, a string starting with minuscular or capital letter or +%% an underline +%% +%% Note, comments are not removed within a character or string constant +%% or inside an include-definition where the file name is delimited with < > +%%=================================================================================== +%%=================================================================================== +%%=================================================================================== + +tokenise(File, FileName) -> + {Result, _L} = token(File, 2, [], not_set, 0), + FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 ~p~n",[FileName]))), + FileInfoStart = {file_info, FI_start}, + [FileInfoStart | Result]. + +tokenise(File, FileName, IncLine, PrevFile) -> + {Result, _L} = token(File, 2, [], not_set, 0), + FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 ~p 1~n",[FileName]))), + FileInfoStart = {file_info, FI_start}, + FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p ~p 2~n~n",[IncLine-1,PrevFile]))), + FileInfoEnd = [{file_info, FI_end}], + {Result, FileInfoStart, FileInfoEnd}. +% [FileInfoStart | Result] ++ FileInfoEnd. + + +%%=================================================================================== +%% token(InputFile, L, Result, Gen) +%% Gen information of the first token on the line, default = not_set +%% +%% Output: File +%%=================================================================================== + +%%================================================================== +%% Normal line +%%================================================================== +%%--------------------------------------- +%% All file tokenised +%%--------------------------------------- +token([], L, [{nl,NL}|Result], _Gen, _BsNl) when L == NL+1-> + {lists:reverse([{nl,NL}|Result]), L}; +token([], L, Result, _Gen, _BsNl) -> + {lists:reverse([{nl,L-1}|Result]), L}; + +%%--------------------------------------- +%% String +%%--------------------------------------- +token(File, L, Result, string, BsNl) -> + case token_string(File, []) of + {Rem, Str, nl} -> + Result1 = [{nl, L}, {string,Str} | Result], + token(Rem, L+1, Result1, string, BsNl); + {Rem, Str} -> + token(Rem, L, [{string,Str}|Result], not_set, BsNl) + end; + +token([$"|File], L, Result, Gen, BsNl) -> + case token_string(File, []) of + {Rem, Str, nl} -> + Result1 = [{nl, L}, {string_part,Str} | Result], + token(Rem, L+1, Result1, string, BsNl); + {Rem, Str} -> + token(Rem, L, [{string,Str}|Result], Gen, BsNl) + end; + +%%--------------------------------------- +%% Include with < > +%%--------------------------------------- +token(File, L, Result, include, BsNl) -> + case token_include(File, []) of + {Rem, Str, nl} -> + Result1 = [{nl, L}, {sys_head,Str} | Result], + token(Rem, L+1, Result1, include, BsNl); + {Rem, Str} -> + token(Rem, L, [{sys_head,Str}|Result], not_set, BsNl) + end; + +token([$<|File], L, [space,{command,"include"}|Result], Gen, BsNl) -> + case token_include(File, []) of + {Rem, Str, nl} -> + Result1 = [{nl, L}, {sys_head_part,Str}, space, {command,"include"} |Result], + token(Rem, L+1,Result1, include, BsNl); + {Rem, Str} -> + Result1 = [{sys_head,Str}, space, {command,"include"} |Result], + token(Rem, L, Result1, Gen, BsNl) + end; +token([$<|File], L, [{command,"include"}|Result], Gen, BsNl) -> + case token_include(File, []) of + {Rem, Str, nl} -> + Result1 = [{nl, L}, {sys_head_part,Str}, space, {command,"include"} |Result], + token(Rem, L+1,Result1, include, BsNl); + {Rem, Str} -> + Result1 = [{sys_head,Str}, space, {command,"include"} |Result], + token(Rem, L, Result1, Gen, BsNl) + end; + + + + +%%--------------------------------------- +%% CR (just remove these) +%%--------------------------------------- +token([$\r|File], L, Result, Gen, BsNl) -> +% Bs = lists:duplicate(BsNl+1,{nl,L}), + token(File, L, Result, Gen, BsNl); %% Bs or BsNl? + +%%--------------------------------------- +%% Newline +%%--------------------------------------- +token([$\n|File], L, Result, _Gen, BsNl) -> + Bs = lists:duplicate(BsNl+1,{nl,L}), + token(File, L+1, Bs++Result, not_set, 0); + +token([$\\,$\n|File], L, Result, Gen, BsNl) -> + token(File, L, Result, Gen, BsNl+1); + +%%--------------------------------------- +%% Comments +%%--------------------------------------- +token([$/,$/|File], L, Result, not_set, BsNl) -> + Rem = skip_to_nl(File), + token(Rem, L+1,[{nl, L} | Result], not_set, BsNl); +token([$/,$/|File], L, Result, _Gen, BsNl) -> + Rem = skip_to_nl(File), + token(Rem, L+1,[{nl, L} | Result], not_set, BsNl); + +token([$/,$*|File], L, Result, not_set, BsNl) -> + case token_comment(File) of + {Rem, nl} -> + token(Rem, L+1, [{nl, L} | Result], not_set, BsNl); + Rem -> + token(Rem, L, Result, not_set, BsNl) + end; +token([$/,$*|File], L, Result, Gen, BsNl) -> + case token_comment(File) of + {Rem, nl} -> + token(Rem, L+1, [{nl, L}, space | Result], not_set, BsNl); + Rem -> + token(Rem, L, [space|Result], Gen, BsNl) + end; + +%%--------------------------------------- +%% Variable +%%--------------------------------------- +token([X|File], L, Result, Gen, BsNl) when ?is_upper(X) -> + GenNew = case Gen of not_set -> var; _ -> Gen end, + {Rem, Var} = tok_var(File, [X]), + token(Rem, L, [{var,Var}|Result], GenNew, BsNl); +token([X|File], L, Result, Gen, BsNl) when ?is_lower(X) -> + GenNew = case Gen of not_set -> var; _ -> Gen end, + {Rem, Var} = tok_var(File, [X]), + token(Rem, L, [{var,Var}|Result], GenNew, BsNl); +token([X|File], L, Result, Gen, BsNl) when ?is_underline(X) -> + GenNew = case Gen of not_set -> var; _ -> Gen end, + {Rem, Var} = tok_var(File, [X]), + token(Rem, L, [{var,Var}|Result], GenNew, BsNl); + +%%--------------------------------------- +%% Number +%%--------------------------------------- +token([X|File], L, Result, Gen, BsNl) when ?is_number(X) -> + GenNew = case Gen of not_set -> number; _ -> Gen end, + {Rem, Tokens} = tok_number(File, [X]), + token(Rem, L, [{number,Tokens}|Result], GenNew, BsNl); + +%%--------------------------------------- +%% Space +%%--------------------------------------- +token([X|File], L, [Y|Result], Gen, BsNl) when ?is_space(X) -> + case Y of + space -> + Rem = remove_leading_spaces(File), + token(Rem, L, [Y|Result], Gen, BsNl); + {nl,_,_} -> + Rem = remove_leading_spaces(File), + token(Rem, L, Result, Gen, BsNl); + _ -> + Rem = remove_leading_spaces(File), + token(Rem, L, [space, Y |Result], Gen, BsNl) + end; + +token([X|File], L, [Y|Result], Gen, BsNl) when ?is_tab(X) -> + case Y of + space -> + Rem = remove_leading_spaces(File), + token(Rem, L, [Y|Result], Gen, BsNl); + {nl,_,_} -> + Rem = remove_leading_spaces(File), + token(Rem, L, Result, Gen, BsNl); + _ -> + Rem = remove_leading_spaces(File), + token(Rem, L, [space, Y |Result], Gen, BsNl) + end; + +%%--------------------------------------- +%% Command +%%--------------------------------------- +token([$#|File], L, Result, not_set, BsNl) -> + {Rem, Command} = token_pp_com(File), + case catch list_to_integer(Command) of + {'EXIT', _} -> + token(Rem, L, [{command,Command}|Result], not_set, BsNl); + _Int -> + Result1 = [{number,Command}, {command,"line"}| Result], + token(Rem, L, Result1, not_set, BsNl) + end; + +%%--------------------------------------- +%% Char +%%--------------------------------------- +token([X|File], L, Result, Gen, BsNl) -> + GenNew = case Gen of not_set -> char; _ -> Gen end, + token(File, L, [{char,X}|Result], GenNew, BsNl). + + +%%================================================================== +%% Scan to the end of a token +%%================================================================== +%%--------------------------------------- +%% Number +%%--------------------------------------- +tok_number([], Str) -> + {[], lists:reverse(Str)}; +tok_number([X|File], Str) when ?is_upper(X) -> + tok_number(File, [X|Str]); +tok_number([X|File], Str) when ?is_lower(X) -> + tok_number(File, [X|Str]); +tok_number([X|File], Str) when ?is_underline(X) -> + tok_number(File, [X|Str]); +tok_number([X|File], Str) when ?is_number(X) -> + tok_number(File, [X|Str]); +tok_number(File, Str) -> + {File, lists:reverse(Str)}. + + +%%--------------------------------------- +%% Variable +%%--------------------------------------- +tok_var([], Str) -> + {[], lists:reverse(Str)}; +tok_var([X|File], Str) when ?is_upper(X) -> + tok_var(File, [X|Str]); +tok_var([X|File], Str) when ?is_lower(X) -> + tok_var(File, [X|Str]); +tok_var([X|File], Str) when ?is_underline(X) -> + tok_var(File, [X|Str]); +tok_var([X|File], Str) when ?is_number(X) -> + tok_var(File, [X|Str]); +tok_var(File, Str) -> + {File, lists:reverse(Str)}. + + +%%--------------------------------------- +%% Preprocessor command +%%--------------------------------------- +token_pp_com([X|File]) when ?is_upper(X) -> + tok_var(File, [X]); +token_pp_com([X|File]) when ?is_lower(X) -> + tok_var(File, [X]); +token_pp_com([X|File]) when ?is_underline(X) -> + tok_var(File, [X]); +token_pp_com([X|File]) when ?is_number(X) -> + tok_var(File, [X]); +token_pp_com(File) -> + Rem = remove_leading_spaces(File), + {Rem, "null"}. + + + +%%--------------------------------------- +%% Comment +%%--------------------------------------- +token_comment([]) -> + []; +token_comment([$*,$/|File]) -> + File; +token_comment([$\n|File]) -> + {[$/,$*|File], nl}; +token_comment([$\r,$\n|File]) -> + {[$/,$*|File], nl}; +token_comment([$\\,$\n|File]) -> + {[$/,$*|File], nl}; +%token_comment([$\\,$\n|File]) -> +% token_comment(File); +token_comment([_|File]) -> + token_comment(File). + + +%%--------------------------------------- +%% String +%%--------------------------------------- +token_string([], Str) -> + {[], lists:reverse(Str)}; +token_string([$"|File], Str) -> + {File, lists:reverse(Str)}; +token_string([$\n|File], Str) -> + {File, lists:reverse(Str), nl}; +token_string([$\r,$\n|File], Str) -> + {File, lists:reverse(Str), nl}; +token_string([$\\,$\n|File], Str) -> + token_string(File, Str); +token_string([X|File], Str) -> + token_string(File, [X|Str]). + + +%%--------------------------------------- +%% Include +%%--------------------------------------- +token_include([], Str) -> + {[], lists:reverse(Str)}; +token_include([$>|File], Str) -> + {File, lists:reverse(Str)}; +token_include([$\n|File], Str) -> + {File, lists:reverse(Str), nl}; +token_include([$\r,$\n|File], Str) -> + {File, lists:reverse(Str), nl}; +token_include([$\\,$\n|File], Str) -> + token_include(File, Str); +token_include([X|File], Str) -> + token_include(File, [X|Str]). + + + + +%%=================================================================================== +%% detokenise a list of tokens, until next newline +%% +%% Output: a string +%%=================================================================================== +detokenise(Tokens) -> + detokenise(Tokens, []). + +detokenise([], Result) -> + lists:flatten(Result); +detokenise([space], Result) -> + lists:flatten(Result); +detokenise([space_exp], Result) -> + lists:flatten(Result); +detokenise([space|Rem], Result) -> + detokenise(Rem, Result++[?space]); +detokenise([space_exp|Rem], Result) -> + detokenise(Rem, Result++[?space]); +detokenise([nl|Rem], Result) -> + detokenise(Rem, Result++[$\n]); +detokenise([{_, String}|Rem], Result) -> + detokenise(Rem, Result++[String]). + + +detokenise_pragma(Tokens) -> + detokenise_pragma(Tokens, []). + +detokenise_pragma([], Result) -> + lists:flatten(Result); +detokenise_pragma([space], Result) -> + lists:flatten(Result); +detokenise_pragma([space|Rem], Result) -> + detokenise_pragma(Rem, Result++[?space]); +detokenise_pragma([nl|Rem], Result) -> + detokenise_pragma(Rem, Result++[$\n]); +detokenise_pragma([{string, String}|Rem], Result) -> + detokenise_pragma(Rem, Result++[$"|String]++[$"]); +detokenise_pragma([{_, String}|Rem], Result) -> + detokenise_pragma(Rem, Result++[String]). + + + + + + + +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +%% Expand macros. +%% +%% +%% Output: A text file +%% +%% Description: Expands all macros. All macro definitions are logged in a list 'Defs' +%% and all found errors and warnings are logged in a list 'Err' and 'War', +%% respectively. +%% +%% When a macro name is found in a source line it is expanded according +%% to the current 'Defs'-list. The macro must agree both to the name +%% and number of parameters, otherwise an error is reported. +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== + + +expand(List, FileName, IncDir, Flags) -> + %% Get all definitions from preprocessor commnads + %% and merge them on top of the file collected. + CLDefs = get_cmd_line_defs(Flags), + expand(List, [], [], CLDefs, [FileName], IncDir, check_all, [], [], 1, FileName). + +expand(List, Defs, Err, War, [FileName|IncFile], IncDir) -> + expand(List, [], [], Defs, [FileName|IncFile], IncDir, check_all, Err, War, 1, FileName). + + +%%======================================================= +%% Main loop for the expansion +%%======================================================= +expand([], Out, _SelfRef, Defs, _IncFile, _IncDir, IfCou, Err, War, _L, _FN) -> +% io:format("~n ===============~n"), +% io:format(" definitions ~p~n",[lists:reverse(Defs)]), +% io:format(" found warnings ~p~n",[lists:reverse(War)]), +% io:format(" found errors ~p~n",[lists:reverse(Err)]), +% io:format(" ===============~n~n~n"), + {Out, Err, War, Defs, IfCou}; + +expand([{file_info, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, Str++Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +%%--------------------------------------- +%% Searching for endif, +%% i.e skip all source lines until matching +%% end if is encountered +%%--------------------------------------- +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) + when Command == "ifdef" -> + {_Removed, Rem2, _Nl} = read_to_nl(Rem), + IfCou2 = {endif, Endif+1, IfLine}, + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L, FN); + + +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) + when Command == "ifndef" -> + {_Removed, Rem2, _Nl} = read_to_nl(Rem), + IfCou2 = {endif, Endif+1, IfLine}, + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L, FN); + + +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) + when Command == "if" -> + case pp_command(Command, Rem, Defs, IncDir, Err, War, L, FN) of + {{'if', true}, Rem2, Err2, War2, Nl} -> + IfCou2 = {endif, Endif+1, IfLine}, + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); +%% {{'if', false}, Rem2, Err2, War2, Nl} -> Not implemented yet + {{'if', error}, Rem2, Err2, War2, Nl} -> + IfCou2 = {endif, Endif, IfLine}, + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN) + end; + +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) + when Command == "endif" -> + {_Removed, Rem2, Nl} = read_to_nl(Rem), + case Endif of + 1 -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + _ -> + IfCou2 = {endif, Endif-1, IfLine}, + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L+Nl, FN) + end; + + +expand([{command,_Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> + {_Removed, Rem2, _Nl} = read_to_nl(Rem), + IfCou2 = {endif, Endif, IfLine}, + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L, FN); + +%% Solves a bug when spaces in front of hashmark ! +expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> + expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN); + +expand([{nl,_Nl} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> + expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN); + + +expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> + {_Removed, Rem2, Nl} = read_to_nl(Rem), + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN); + + + + + +%%--------------------------------------- +%% Check all tokens +%%--------------------------------------- +expand([{nl, _N} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [$\n | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L+1, FN); + +expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([space_exp | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L, FN) -> + case pp_command(Command, Rem, Defs, IncDir, Err, War, L, FN) of + {define, Rem2, Defs2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {undef, Rem2, Defs2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {{include, ok}, FileName, FileCont, Rem2, Nl, Err2, War2} -> + {Out3, Defs3, Err3, War3} = + run_include(FileName, FileCont, Out, Defs, Err2, War2, L+Nl, IncFile, IncDir), + Nls = [], + Out4 = Out3++Nls++Out, + expand(Rem2, Out4, SelfRef, Defs3, IncFile, IncDir, check_all, Err3, War3, L+Nl, FN); + + {{include, error}, Rem2, Nl, Err2, War2} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {{ifdef, true}, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + IfCou2 = {endif, 1, L}, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); + {{ifdef, false}, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {{ifndef, true}, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + IfCou2 = {endif, 1, L}, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); + {{ifndef, false}, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {endif, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {{'if', true}, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + IfCou2 = {endif, 1, L}, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); +%% {{'if', false}, Removed, Rem2, Nl} -> Not implemented at present + {{'if', error}, Rem2, Err2, War2, Nl} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + + {'else', {_Removed, Rem2, Nl}} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + Err2 = {FN, L, "`else' command is not implemented at present"}, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + + {'elif', {_Removed, Rem2, Nl}} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + Err2 = {FN, L, "`elif' command is not implemented at present"}, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + + {warning, {WarningText, Rem2, Nl}} -> + [FileName|_More] = IncFile, + War2 = {FileName, L, "warning: #warning "++detokenise(WarningText)}, + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, [War2|War], L+Nl, FN); + + {error, {ErrorText, Rem2, Nl}} -> + [FileName|_More] = IncFile, + Err2 = {FileName, L, detokenise(ErrorText)}, + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + + {{line, ok}, {_Removed, Rem2, Nl}, L2, FN2, LineText} -> + Out2 = lists:duplicate(Nl,$\n)++LineText++Out, + [_X|IF] = IncFile, + IncFile2 = [FN2|IF], + expand(Rem2, Out2, SelfRef, Defs, IncFile2, IncDir, check_all, Err, War, L2, FN2); + {{line, error}, {_Removed, Rem2, Nl}, Err2} -> + Out2 = [lists:duplicate(Nl,$\n)|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + + hash_mark -> + expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L, FN); + + {pragma, Rem2, Nl, Text} -> + Out2 = lists:duplicate(Nl,$\n)++Text++Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + + {ident, Rem2, Nl, Text} -> + Out2 = lists:duplicate(Nl,$\n)++Text++Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + + {not_recognised, {Removed, Rem2, Nl}} -> + Text = lists:reverse([$#|Command]), + RemovedS = lists:reverse([?space|detokenise(Removed)]), + Out2 = [$\n|RemovedS]++Text++Out, + case Command of + [X|_T] when ?is_upper(X) -> + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + [X|_T] when ?is_lower(X) -> + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + [X|_T] when ?is_underline(X) -> + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + _ -> + Err2 = {FN, L, "invalid preprocessing directive name"}, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN) + end; + + Else -> +% io:format(" %%%%Else%%%%%% ~p~n",[Else]), + exit(Else) + end; + + +expand([{var, "__LINE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + LL = io_lib:format("~p",[L]), + expand(Rem, [LL | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{var, "__FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [$",FN,$" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{var, "__DATE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + {{Y,M,D},{_H,_Mi,_S}} = calendar:universal_time(), + Date = io_lib:format("\"~s ~p ~p\"",[month(M),D,Y]), + expand(Rem, [Date | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + {{_Y,_M,_D},{H,Mi,S}} = calendar:universal_time(), + HS = if H < 10 -> "0"++integer_to_list(H); + true -> integer_to_list(H) + end, + MiS = if Mi < 10 -> "0"++integer_to_list(Mi); + true -> integer_to_list(Mi) + end, + SS = if S < 10 -> "0"++integer_to_list(S); + true -> integer_to_list(S) + end, + Time = io_lib:format("\"~s:~s:~s\"",[HS,MiS,SS]), + expand(Rem, [Time | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{var, "__INCLUDE_LEVEL__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + IL = io_lib:format("~p",[length(IncFile)-1]), + expand(Rem, [IL | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{var, "__BASE_FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + [BF|_T] = lists:reverse(IncFile), + expand(Rem, [$",BF,$" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{var, Var} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + {Out2, Err2, War2, Rem2, SelfRef2} = + source_line(Var, Rem, SelfRef, Defs, Err, War, L, FN), + expand(Rem2, [Out2 | Out], SelfRef2, Defs, IncFile, IncDir, IfCou, Err2, War2, L, FN); + +expand([{char, Char} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [Char | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{number, Number} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [Number | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{expanded, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [Str | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{self_ref, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + SelfRef2 = lists:delete(Str,SelfRef), + expand(Rem, Out, SelfRef2, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{string, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [$", Str, $" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + +expand([{string_part, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> + {Str2, Rem2, Nl} = expand_string_part([$"|Str], Rem), + expand(Rem2, [Str2| Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L+Nl, FN). + + + + + + + + +%%======================================================================== +%% Expand a line starting as a partial string +%%======================================================================== +expand_string_part(Str, File) -> + expand_string_part(File, Str, 0). + +expand_string_part([{string, Str_part} | Rem], Str, Nl) -> + {Str++Str_part++[$"], Rem, Nl}; +expand_string_part([space | Rem], Str, Nl) -> + expand_string_part(Rem, Str, Nl); +expand_string_part([nl| Rem], Str, Nl) -> + expand_string_part(Rem, Str++[$\n], Nl); +expand_string_part([{string_part, Str_part} | Rem], Str, Nl) -> + expand_string_part(Rem, Str++Str_part, Nl). + + + + + +%%======================================================================== +%% Parse and integrate command line macro directives +%% At this momment, only -D and -U are supported (gcc like) +%%======================================================================== + + +%% Collect all command line macro definitions +get_cmd_line_defs(Flags) -> + Adjusted = parse_cmd_line(Flags,[]), + + {_Out, _Err, _War, Defs, _IfCou} = + expand(tokenise(Adjusted,""), + [], + [], + [], + [], + [], + check_all, + [], + [], + 1, + ""), + Defs. + +%% Parse command line macros +parse_cmd_line([],Found) -> + lists:flatten(lists:reverse(Found)); + +parse_cmd_line([45,68|Rest],Found) -> + {Collected,RestCmds} = collect_define(Rest,[]), + parse_cmd_line(RestCmds,[Collected|Found]); + +parse_cmd_line([45,85|Rest],Found) -> + {Collected,RestCmds} = collect_undefine(Rest,[]), + parse_cmd_line(RestCmds,[Collected|Found]); + +parse_cmd_line([_|Rest],Found) -> + parse_cmd_line(Rest,Found). + + +%% Collect defines and translate them +%% into a text format +collect_define([],Found) -> + { "#define "++lists:reverse(Found)++"\n", [] }; +collect_define([32|Rest],Found) -> + { "#define "++lists:reverse(Found)++"\n", Rest }; +collect_define([61|Rest],[]) -> + { "", Rest }; +collect_define([61|Rest],Found) -> + collect_define(Rest,[32|Found]); +collect_define([C|Rest],Found) -> + collect_define(Rest,[C|Found]). + + +%% Collect undefines and translate them +%% into a text format +collect_undefine([],Found) -> + { "#undef "++lists:reverse(Found)++"\n", [] }; +collect_undefine([32|Rest],Found) -> + { "#undef "++lists:reverse(Found)++"\n", Rest }; +collect_undefine([C|Rest],Found) -> + collect_undefine(Rest,[C|Found]). + + + + + + + + + + + + +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +%% Read a preprocessor command +%% +%% +%% Output: Depending of the command, typically = {Command, Rem, Err, War, Nl} +%% +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== + +pp_command(Command, [space|File], Defs, IncDir, Err, War, L, FN) -> + pp_command(Command, File, Defs, IncDir, Err, War, L, FN); + +pp_command(Command, File, Defs, IncDir, Err, War, L, FN) -> + + case Command of + %%---------------------------------------- + %% #define + %%---------------------------------------- + "define" -> + case define(File, Err, War, L, FN) of + {error, Rem, Err2, War2, Nl} -> + {define, Rem, Defs, Err2, War2, Nl}; + {warning, Rem, Name, No_of_para, Parameters, Macro, Err2, War2, Nl} -> + case is_define_ok(Name, No_of_para, Parameters, Macro, Defs) of + {yes, Defs2} -> + {define, Rem, Defs2, Err2, War2, Nl}; + {no, Defs2} -> + Text = lists:flatten(io_lib:format("`~s' redefined",[Name])), + {define, Rem, Defs2, Err2, [{FN, L, Text}|War2], Nl}; + {error, Text, Defs2} -> + {define, Rem, Defs2, [{FN, L, Text}|Err2], War2, Nl} + end; + {ok, Rem, Name, No_of_para, Parameters, Macro, Err2, War2, Nl} -> + case is_define_ok(Name, No_of_para, Parameters, Macro, Defs) of + {yes, Defs2} -> + {define, Rem, Defs2, Err2, War2, Nl}; + {no, Defs2} -> + Text = lists:flatten(io_lib:format("`~s' redefined",[Name])), + {define, Rem, Defs2, Err2, [{FN, L, Text}|War2], Nl}; + {error, Text, Defs2} -> + {define, Rem, Defs2, [{FN, L, Text}|Err2], War2, Nl} + end + end; + + %%---------------------------------------- + %% #undef + %%---------------------------------------- + "undef" -> + case undef(File, Err, War, L, FN) of + {error, Rem, Err2, War2, Nl} -> + {undef, Rem, Defs, Err2, War2, Nl}; + {ok, Rem, Name, Err2, War2, Nl} -> + Defs2 = lists:keydelete(Name, 1, Defs), + {undef, Rem, Defs2, Err2, War2, Nl} + end; + + %%---------------------------------------- + %% #include + %%---------------------------------------- + "include" -> + case include(File, IncDir) of + {error, Rem, Nl, Err2} -> + {{include, error}, Rem, Nl, [{FN, L, Err2}|Err], War}; + {error, Rem, Nl, Err2, NameNl} -> + {{include, error}, Rem, Nl, [{FN, L+ NameNl, Err2}|Err], War}; + {ok, FileName, FileCont, Rem, Nl} -> + {{include, ok}, FileName, FileCont, Rem, Nl, Err, War} + end; + + %%---------------------------------------- + %% #ifdef + %%---------------------------------------- + "ifdef" -> + case define(File, Err, War, L, FN) of + {error, Rem, Err2, War2, Nl} -> + {{ifdef, false}, Rem, Defs, Err2, War2, Nl}; + {warning, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> + case is_defined_before(Name, No_of_para, Defs) of + yes -> + {{ifdef, false}, Rem, Err2, War2, Nl}; + no -> + {{ifdef, true}, Rem, Err2, War2, Nl} + end; + {ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> + case is_defined_before(Name, No_of_para, Defs) of + yes -> + {{ifdef, false}, Rem, Err2, War2, Nl}; + no -> + {{ifdef, true}, Rem, Err2, War2, Nl} + end + end; + + + + %%---------------------------------------- + %% #ifndef + %%---------------------------------------- + "ifndef" -> + case define(File, Err, War, L, FN) of + {error, Rem, Err2, War2, Nl} -> + {{ifndef, false}, Rem, Defs, Err2, War2, Nl}; + {warning, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> + case is_defined_before(Name, No_of_para, Defs) of + yes -> + {{ifndef, true}, Rem, Err2, War2, Nl}; + no -> + {{ifndef, false}, Rem, Err2, War2, Nl} + end; + {ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> + case is_defined_before(Name, No_of_para, Defs) of + yes -> + {{ifndef, true}, Rem, Err2, War2, Nl}; + no -> + {{ifndef, false}, Rem, Err2, War2, Nl} + end + end; + + + %%---------------------------------------- + %% #endif + %%---------------------------------------- + "endif" -> + {Removed, Rem, Nl} = read_to_nl(File), + case Removed of + [] -> + {endif, Rem, Err, War, 1}; + _ -> + Text = "ignoring the tail of the line", + {ok, Rem, Err, [{FN, L, Text}|War], Nl} + end; + + + %%---------------------------------------- + %% #if + %%---------------------------------------- + "if" -> + case if_zero(File, Err, War, L, FN) of + {error, Rem2, _Removed, Nl} -> + Err2 = {FN, L, "only '#if 0' is implemented at present"}, + {{'if', error}, Rem2, [Err2 | Err], War, Nl}; + {ok, Rem2, 0, _Removed, Nl} -> + {{'if', true}, Rem2, Err, War, Nl}; + {ok, Rem2, _Num, _Removed, Nl} -> + Err2 = {FN, L, "only '#if 0' is implemented at present"}, + {{'if', error}, Rem2, [Err2 | Err], War, Nl} + end; + + %%---------------------------------------- + %% #else + %%---------------------------------------- + "else" -> + {'else', read_to_nl(File)}; + + %%---------------------------------------- + %% #elif + %%---------------------------------------- + "elif" -> + {'elif', read_to_nl(File)}; + + %%---------------------------------------- + %% #pragma + %%---------------------------------------- + "pragma" -> + {Removed, Rem, Nl} = read_to_nl(File), + {pragma, Rem, Nl, lists:reverse("#pragma " ++ detokenise_pragma(Removed))}; + + %%---------------------------------------- + %% #ident + %%---------------------------------------- + "ident" -> + {Removed, Rem, Nl} = read_to_nl(File), + {ident, Rem, Nl, lists:reverse("#ident " ++ detokenise_pragma(Removed))}; + + %%---------------------------------------- + %% #warning + %%---------------------------------------- + "warning" -> + {warning, read_to_nl(File)}; + + %%---------------------------------------- + %% #error + %%---------------------------------------- + "error" -> + {error, read_to_nl(File)}; + + %%---------------------------------------- + %% #line + %%---------------------------------------- + "line" -> + line(File, L, FN); + + %%---------------------------------------- + %% # + %%---------------------------------------- + "null" -> + hash_mark; + + %%---------------------------------------- + %% not recognised preprocessor commands + %%---------------------------------------- + _Else -> + {not_recognised, read_to_nl(File)} + end. + + + + +%%=============================================================== +%%=============================================================== +%%=============================================================== +%% if +%% +%% Only #if 0 is implemented at the time to be able to use if +%% to comment some code parts. +%%=============================================================== +%%=============================================================== +%%=============================================================== + +if_zero(File, _Err, _War, _L, _FN) -> + case if_zero(File) of + {ok, Remain, Num, Removed, Nl} -> + case catch list_to_integer(Num) of + {'EXIT', _} -> + {Removed2, Rem2, Nl2} = read_to_nl(File), + {error, Rem2, Removed2, Nl2}; + Int -> + {ok, Remain, Int, Removed, Nl} + end; + E -> + E + end. + +if_zero([{number,Num}]) -> + {ok, [], Num, [], 0}; +if_zero([{number,Num}, space]) -> + {ok, [], Num, [], 0}; +if_zero([{number,Num} | Rem]) -> + {Removed, Rem2, Nl} = read_to_nl(Rem), + {ok, Rem2, Num, Removed, Nl}; +%if_zero([{number,Num}, {nl,_X} | Rem]) -> +% {ok, Rem, Num, [], 1}; +if_zero(Rem) -> + {Removed, Rem2, Nl} = read_to_nl(Rem), + {error, Rem2, Removed, Nl}. + + + +%%=============================================================== +%%=============================================================== +%%=============================================================== +%% Define macro +%% +%% Check the syntax of the macro, extract the parameters if any. +%% If valid macro it is added to the Defs-list. +%% If a macro is redefined, a warning will be given, the latest +%% definition is always the valid one. +%%=============================================================== +%%=============================================================== +%%=============================================================== + +define(File, Err, War, L, FN) -> + case define_name(File) of + {ok, Rem, Name, No_of_para, Parameters, Macro, Nl} -> + {ok, Rem, Name, No_of_para, Parameters, Macro, Err, War, Nl}; + {{warning,no_space}, Rem, Name, No_of_para, Parameters, Macro, Nl} -> + Text = lists:flatten(io_lib:format("missing white space after `#define ~s'",[Name])), + {warning, Rem, Name, No_of_para, Parameters, Macro, Err, [{FN, L, Text}|War], Nl}; + {error, invalid_name, Nl} -> + Text = "invalid macro name", + {_Removed, Rem, Nl2} = read_to_nl(File), + {error, Rem, [{FN, L, Text}|Err], War, Nl+Nl2}; + {error, invalid_name, Name, Nl} -> + Text = lists:flatten(io_lib:format("invalid macro name `~s'",[Name])), + {_Removed, Rem, Nl2} = read_to_nl(File), + {error, Rem, [{FN, L, Text}|Err], War, Nl+Nl2}; + {error, illegal_arg} -> + {Removed, Rem, Nl} = read_to_nl(File), + RemovedS = detokenise(Removed), + Text = lists:flatten(io_lib:format("Invalid argument list ~s",[RemovedS])), + {error, Rem, [{FN, L, Text}|Err], War, Nl} + end. + + + +%%=========================================================== +%% Check if valid macro +%%=========================================================== +define_name([]) -> + {warning, no_macro}; +define_name([space]) -> + {warning, no_macro}; +%% Macro with parameters +define_name([{var,Name},{char,$(}|Rem]) -> + case read_para([{char,$(}|Rem]) of + {ok, Rem2, Para, NoOfPara} -> + {Removed, Rem3, _Nl} = read_to_nl(Rem2), + {ok, Rem3, Name, NoOfPara, Para, Removed, 1}; + Error -> + Error + end; +%% Macro without parameters +define_name([{var,Name}]) -> + {ok, [], Name, 0, [], [], 0}; +define_name([{var,Name}, space | Rem]) -> + {Removed, Rem2, Nl} = read_to_nl(Rem), + {ok, Rem2, Name, 0, [], Removed, Nl}; +define_name([{var,Name}, {nl,_X} | Rem]) -> + {ok, Rem, Name, 0, [], [], 1}; +define_name([{var,Name} | Rem]) -> + {Removed, Rem2, Nl} = read_to_nl(Rem), + {{warning,no_space}, Rem2, Name, 0, [], Removed, Nl}; +%% Invalid macro name +define_name([{number, Name} | _Rem]) -> + {error, invalid_name, Name, 0}; +define_name(_Rem) -> + {error, invalid_name, 0}. + + + + + + + +%%=============================================================== +%%=============================================================== +%%=============================================================== +%% Undefine macro +%% +%% If it is a valid undef command the macro name will be deleted +%% from the Defs-list +%%=============================================================== +%%=============================================================== +%%=============================================================== + +undef(File, Err, War, L, FN) -> + case undef(File) of + {ok, Rem, Name, Nl} -> + {ok, Rem, Name, Err, War, Nl}; + {warning, Rem, Name, Nl} -> + Text = "ignoring the tail of the line", + {ok, Rem, Name, Err, [{FN, L, Text}|War], Nl}; + {error, invalid_name} -> + Text = "invalid macro name", + {_Removed, Rem, Nl} = read_to_nl(File), + {error, Rem, [{FN, L, Text}|Err], War, Nl}; + {error, invalid_name, Name} -> + Text = lists:flatten(io_lib:format("invalid macro name `~s'",[Name])), + {_Removed, Rem, Nl} = read_to_nl(File), + {error, Rem, [{FN, L, Text}|Err], War, Nl} + end. + +%%------------------------------------------------- +%% Check if valid macro name +%%------------------------------------------------- +undef([]) -> + {error, invalid_name, []}; +%% Valid name +undef([{var,Name}]) -> + {ok, [], Name, 0}; +undef([{var,Name}, {nl,_X} | Rem]) -> + {ok, Rem, Name, 1}; +undef([{var,Name}, space, {nl,_X} | Rem]) -> + {ok, Rem, Name, 1}; +undef([{var,Name} | Rem]) -> + {_Removed, Rem2, Nl} = read_to_nl(Rem), + {warning, Rem2, Name, Nl}; +%% Invalid macro name +undef([{number, Name} | _Rem]) -> + {error, invalid_name, Name}; +undef(_Rem) -> + {error, invalid_name}. + + + + + + +%%=============================================================== +%%=============================================================== +%%=============================================================== +%% Include macro +%% +%% Read the included file +%%=============================================================== +%%=============================================================== +%%=============================================================== + +include(File, IncDir) -> + case include2(File) of + {ok, FileName, Rem, Nl, FileType} -> + %% The error handling is lite strange just to make it compatible to gcc + case {read_inc_file(FileName, IncDir), Nl, FileType} of + {{ok, FileList, FileNamePath}, _, _} -> + {ok, FileNamePath, FileList, Rem, Nl}; + {{error, Text}, _, own_file} -> + NameNl = count_nl(FileName,0), + Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), + {error, Rem, Nl, Error, NameNl}; + {{error, Text}, 1, sys_file} -> + NameNl = count_nl(FileName,0), + Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), + {error, Rem, Nl, Error, NameNl}; + {{error, _Text}, _, sys_file} -> + {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"} + end; + + {error, {_Removed, Rem, Nl}} -> + {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"} + end. + +count_nl([],Nl) -> + Nl; +count_nl([$\n|T],Nl) -> + count_nl(T,Nl+1); +count_nl([_H|T],Nl) -> + count_nl(T,Nl). + +%%================================================= +%% Extract the file name from the token list +%%================================================= +include2([space|Rem]) -> + include2(Rem); + +include2([{string, FileName}]) -> + {ok, FileName, [], 1, own_file}; +include2([{string, FileName}, space]) -> + {ok, FileName, [], 1, own_file}; +include2([{string, FileName}, {nl, _X} | Rem]) -> + {ok, FileName, Rem, 1, own_file}; +include2([{string, FileName}, space, {nl, _X} | Rem]) -> + {ok, FileName, Rem, 1, own_file}; +include2([{string, _FileName}, _No_nl | Rem]) -> + {error, read_to_nl(Rem)}; +include2([{string_part, File_part}, {nl, _X} | Rem]) -> + case include_read_string_file_name(File_part++[$\n], Rem, 1) of + {ok, FileName, Rem2, Nl} -> + {ok, FileName, Rem2, Nl, own_file}; + error -> + {error, read_to_nl([{string_part,File_part} | Rem])} + end; +include2([{sys_head, FileName}]) -> + {ok, FileName, [], 1, sys_file}; +include2([{sys_head, FileName}, space]) -> + {ok, FileName, [], 1, sys_file}; +include2([{sys_head, FileName}, {nl, _X} | Rem]) -> + {ok, FileName, Rem, 1, sys_file}; +include2([{sys_head, FileName}, space, {nl, _X} | Rem]) -> + {ok, FileName, Rem, 1, sys_file}; +include2([{sys_head, _FileName}, _No_nl | Rem]) -> + {error, read_to_nl(Rem)}; +include2([{sys_head_part ,File_part}, {nl, _X} | Rem]) -> + case include_read_sys_file_name(File_part++[$\n], Rem, 1) of + {ok, FileName, Rem2, Nl} -> + {ok, FileName, Rem2, Nl, sys_file}; + error -> + {error, read_to_nl([{sys_head_part, File_part} | Rem])} + end; +include2(Rem) -> + {error, read_to_nl(Rem)}. + + + +%%------------------------------------------------- +%% File name framed by " " +%%------------------------------------------------- +include_read_string_file_name(File, [{string, File_part}, {nl,_X} | Rem], Nl) -> + {ok, File++File_part, Rem, Nl+1}; +include_read_string_file_name(File, [{string_part, File_part}, {nl,_X} | Rem], Nl) -> + include_read_string_file_name(File++File_part++[$\n], Rem, Nl+1); +include_read_string_file_name(_File, _X, _Nl) -> + error. + +%%------------------------------------------------- +%% File name framed by < > +%%------------------------------------------------- +include_read_sys_file_name(File, [{sys_head, File_part}, {nl,_X} | Rem], Nl) -> + {ok, File++File_part, Rem, Nl+1}; +include_read_sys_file_name(File, [{sys_head_part, File_part}, {nl,_X} | Rem], Nl) -> + include_read_sys_file_name(File++File_part++[$\n], Rem, Nl+1); +include_read_sys_file_name(_File, _X, _Nl) -> + error. + + + + + + + +%%=============================================================== +%%=============================================================== +%%=============================================================== +%% Line macro +%% +%% The line macro may redefine both the current line number and +%% the current file name: #line ' new_line_nr' 'new_file_name' +%%=============================================================== +%%=============================================================== +%%=============================================================== + +line(File, L, FN) -> + line(File, L, FN, not_defined, not_defined). + + + +line([], L, FN, _Line, _File) -> + {{line, error}, {[],[],0}, {FN,L,"invalid format `#line' directive"}}; + +line([space|Rem], L, FN, Line, File) -> + line(Rem, L, FN, Line, File); + +%%------------------------------ +%% Line number expected +%%------------------------------ +line([{number,Number}|Rem], L, FN, not_defined, File) -> + case catch list_to_integer(Number) of + {'EXIT', _} -> + {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}; + Int -> + line(Rem, L, FN, Int, File) + end; +line(Rem, L, FN, not_defined, _File) -> + {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}; + +%%------------------------------ +%% File name or newline expected +%%------------------------------ +line([{nl, _NL}|Rem], _L, FN, Line, not_defined) -> + {{line, ok}, {[],Rem,1}, Line, FN, io_lib:format("~n~p ~p #",[FN, Line-1])}; +line([{string,NewFN}|Rem], _L, _FN, Line, not_defined) -> + {{line, ok}, read_to_nl(Rem), Line, NewFN, io_lib:format("~n~p ~p #",[NewFN, Line-1])}; +line(Rem, L, FN, _Line, _File) -> + {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}. + + + + +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +%% Source line +%% +%% +%% Output: {Str, Err, War, Rem, SelfRef} +%% +%% Description: The input source line is searched for macros. If a macro is found it +%% is expanded. The result of an expansion is rescanned for more macros. +%% To prevent infinite loops if the macro is self referring +%% an extra token is put into the Rem list. The variable SelfRef +%% contains all the macros which are inhibited to be expanded. +%% A special specae token is also inserted to prevent not wanted +%% concatinations if one of the variables to be concatinated is expanded. +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== + +source_line(Str, Rem, SelfRef, Defs, Err, War, L, FN) -> + {Rem2, Para, No_of_para} = case read_para(Rem) of + {ok, RemT, ParaT, No_of_paraT} -> + {RemT, ParaT, No_of_paraT}; + {error, illegal_arg} -> + {[], [], 0} + end, + + + %%------------------------------------------------- + %% Check if a valid macro + %%------------------------------------------------- + case lists:keysearch(Str, 1, Defs) of + %% a macro without parameters + {value, {Str, 0, _MacroPara, Macro}} -> + case lists:member(Str, SelfRef) of + true -> + {[Str], Err, War, Rem, SelfRef}; + false -> + ExpandedRes2 = sl_mark_expanded(Macro, Str), + {[], Err, War, ExpandedRes2 ++ [{self_ref,Str}|Rem], [Str|SelfRef]} + end; + + %% a macro with parameters + {value, {Str, N, _MacroPara, Macro}} when N == No_of_para -> + case lists:member(Str, SelfRef) of + true -> + {[Str], Err, War, Rem, SelfRef}; + false -> + ExpandedRes = sl_macro_expand(Macro, Para, Defs), + ExpandedRes2 = sl_mark_expanded(ExpandedRes, Str), + {[], Err, War, ExpandedRes2 ++ [{self_ref,Str}|Rem2], [Str|SelfRef]} + end; + + %% a variable, because it doesn't have any parameters + {value, {Str, _N, _MacroPara, _Macro}} when No_of_para == 0 -> + {Str, Err, War, Rem, SelfRef}; + + %% illegal no of parameters + {value, {Str, N, _MacroPara, _Macro}} when No_of_para < N -> + Text = io_lib:format(" macro `~s' used with just ~p arg",[Str,No_of_para]), + Err2 = {FN, L, lists:flatten(Text)}, + {Str, [Err2|Err], War, Rem, SelfRef}; + {value, {Str, _N, _MacroPara, _Macro}} -> + Text = io_lib:format(" macro `~s' used with too many (~p) args",[Str,No_of_para]), + Err2 = {FN, L, lists:flatten(Text)}, + {Str, [Err2|Err], War, Rem, SelfRef}; + + %% no macro + false -> + {Str, Err, War, Rem, SelfRef} + end. + + + + + +%%================================================= +%% Expand a macro +%%================================================= +sl_macro_expand(Macro, Para, Defs) -> + sl_macro_expand(Macro, Para, Defs, []). + + +%%................... +%% End +%%................... +sl_macro_expand([], _Para, _Defs, Res) -> + lists:reverse(Res); + +%%................... +%% Concatination +%%................... +%% para ## para +sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> + Exp = sl_para_para({para, N},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% para## para +sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> + Exp = sl_para_para({para, N},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% para ##para +sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> + Exp = sl_para_para({para, N},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% para##para +sl_macro_expand([{para, N}, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> + Exp = sl_para_para({para, N},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); + +%% para ## var +sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, {var, Var}|T], Para, Defs, Res) -> + Exp = sl_para_var({para, N}, {var, Var}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% para## var +sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, {var, Var} | T], Para, Defs, Res) -> + [{var, VarN}] = lists:nth(N,Para), + sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]); +%% para ##var +sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, {var, Var} | T], Para, Defs, Res) -> + [{var, VarN}] = lists:nth(N,Para), + sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]); +%% para##var +sl_macro_expand([{para, N}, {char,$#}, {char,$#}, {var, Var} | T], Para, Defs, Res) -> + [{var, VarN}] = lists:nth(N,Para), + sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]); + +%% var ## para +sl_macro_expand([{var, Var}, space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> + Exp = sl_var_para({var, Var},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% var## para +sl_macro_expand([{var, Var}, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> + Exp = sl_var_para({var, Var},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% var ##para +sl_macro_expand([{var, Var}, space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> + Exp = sl_var_para({var, Var},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); +%% var##para +sl_macro_expand([{var, Var}, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> + Exp = sl_var_para({var, Var},{para, M}, Para), + sl_macro_expand(Exp++T, Para, Defs, [space |Res]); + +%% expanded ## para +sl_macro_expand([space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> + [{var, VarM}] = lists:nth(M,Para), + sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); +%% expanded## para +sl_macro_expand([{char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> + [{var, VarM}] = lists:nth(M,Para), + sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); +%% expanded ##para +sl_macro_expand([space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> + [{var, VarM}] = lists:nth(M,Para), + sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); +%% expanded##para +sl_macro_expand([{char,$#}, {char,$#}, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> + [{var, VarM}] = lists:nth(M,Para), + sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); + +%% para ## ? +sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, X | T], Para, Defs, Res) -> + Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), + sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); +%% para## ? +sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, X | T], Para, Defs, Res) -> + Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), + sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); +%% para ##? +sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, X | T], Para, Defs, Res) -> + Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), + sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); +%% para##? +sl_macro_expand([{para, N}, {char,$#}, {char,$#}, X | T], Para, Defs, Res) -> + Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), + sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); + +sl_macro_expand([{char,$#}, {char,$#}, space |T], Para, Defs, [space|Res]) -> + sl_macro_expand(T, Para, Defs, Res); +sl_macro_expand([{char,$#}, {char,$#} |T], Para, Defs, [space|Res]) -> + sl_macro_expand(T, Para, Defs, Res); +sl_macro_expand([{char,$#}, {char,$#}, space |T], Para, Defs, Res) -> + sl_macro_expand(T, Para, Defs, Res); +sl_macro_expand([{char,$#}, {char,$#} |T], Para, Defs, Res) -> + sl_macro_expand(T, Para, Defs, Res); + +%%................... +%% Stringification +%%................... +sl_macro_expand([{char,$#}, {para, N}|T], Para, Defs, Res) -> + Nth = lists:nth(N,Para), + Tokens = detokenise(Nth), + sl_macro_expand(T, Para, Defs, [{string,Tokens}|Res]); +sl_macro_expand([{char,$#}, space, {para, N}|T], Para, Defs, Res) -> + Nth = lists:nth(N,Para), + Tokens = detokenise(Nth), + sl_macro_expand(T, Para, Defs, [{string,Tokens}|Res]); + +%%................... +%% A parameter +%%................... +sl_macro_expand([{para, N}|T], Para, Defs, Res) -> + Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), + sl_macro_expand(T, Para, Defs, lists:flatten([Reexp|Res])); + +%%................... +%% No parameter +%%................... +sl_macro_expand([H|T], Para, Defs, Res) -> + sl_macro_expand(T, Para, Defs, [H|Res]). + + + +%%------------------------------------------------- +%% Expand parameters +%%------------------------------------------------- +sl_para_para({para, N}, {para, M}, Para) -> + case sl_para_1st(lists:nth(N,Para)) of + {ok, Para1st} -> + Para1st ++ sl_para_2nd(lists:nth(M,Para)); + {exp, Para1st} -> + Para1st ++ sl_para_2nd(lists:nth(M,Para)) ++ [space_exp]; + {space, Para1st} -> + Para1st ++ [space_exp | sl_para_2nd(lists:nth(M,Para))] + end. + + +sl_var_para(Var, {para, M}, Para) -> + [Var|sl_para_2nd(lists:nth(M,Para))]. + + +sl_para_var({para, N}, Var, Para) -> + case sl_para_1st(lists:nth(N,Para)) of + {ok, Para1st} -> + Para1st ++ [Var]; + {exp, Para1st} -> + Para1st ++ [Var | space_exp]; + {space, Para1st} -> + Para1st ++ [space_exp | Var] + end. + + +sl_para_1st([{var, Var}]) -> + {ok,[{expanded,Var}]}; +sl_para_1st([{var, Var}, space]) -> + {ok,[{expanded,Var}]}; +sl_para_1st([{var, Var}, space_exp]) -> + {exp, [{expanded,Var}]}; +sl_para_1st(L) -> + {space, L}. + +sl_para_2nd([{var, Var}]) -> + [{expanded,Var}]; +sl_para_2nd([{var, Var}, space_exp]) -> + [{expanded,Var}]; +sl_para_2nd([space, {var, Var}]) -> + [{expanded,Var}]; +sl_para_2nd([space_exp, {var, Var}]) -> + [{expanded,Var}]; +sl_para_2nd(L) -> + L++[space]. + + + +%%------------------------------------------------- +%% Check if the expansion is a valid macro, +%% do not reexpand if concatination +%%------------------------------------------------- +sl_macro_reexpand([], _Defs, Result) -> + Result; +sl_macro_reexpand([{var,Var}|Rem], Defs, Result) -> + case lists:keysearch(Var, 1, Defs) of + {value, {Var, 0, _MacroPara, Macro}} -> + Rem2 = case Rem of + [space | RemT] -> + [space_exp | RemT]; + _ -> + [space_exp | Rem] + end, + sl_macro_reexpand(Macro++Rem2, Defs, Result); + _ -> + sl_macro_reexpand(Rem, Defs, [{var,Var}|Result]) + end; +sl_macro_reexpand([H|Rem], Defs, Result) -> + sl_macro_reexpand(Rem, Defs, [H|Result]). + + + +%%------------------------------------------------- +%% Self referring macros are marked not be reexpanded +%%------------------------------------------------- +sl_mark_expanded(QQ, Str) -> + sl_mark_expanded(QQ, Str, []). + +sl_mark_expanded([], _Str, Res) -> + lists:reverse(Res); +sl_mark_expanded([H|T], Str, Res) -> + case H of + {_,Str} -> + sl_mark_expanded(T, Str, [{expanded, Str}|Res]); + _ -> + sl_mark_expanded(T, Str, [H|Res]) + end. + + + + + + + + + +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== +%% Misceleaneous functions +%%====================================================================================== +%%====================================================================================== +%%====================================================================================== + + +%%=============================================================== +%% Check the Flags for include directories +%%=============================================================== +include_dir(Flags) when is_list(Flags)-> + include_dir(Flags,[]); +include_dir(_Flags) -> + []. + +include_dir(Flags,IncDir) -> + case string:str(Flags,"-I") of + 0 -> + lists:reverse(IncDir); + X -> + Rem2 = string:sub_string(Flags, X+2), + Rem = string:strip(Rem2, left), + Y = string:str(Rem," "), + case string:str(Rem," ") of + 0 -> + lists:reverse([string:sub_string(Rem, Y+1)|IncDir]); + Y -> + include_dir(string:sub_string(Rem, Y+1), + [string:sub_string(Rem,1,Y-1)|IncDir]) + end + end. + + + +%%=============================================================== +%% Read a included file. Try current dir first then the IncDir list +%%=============================================================== + +read_inc_file(FileName, IncDir) -> + case catch file:read_file(FileName) of + {ok, Bin} -> + FileList = binary_to_list(Bin), + {ok, FileList, FileName}; + {error, _} -> + read_inc_file2(FileName, IncDir) + end. + +read_inc_file2(_FileName, []) -> + {error, "No such file or directory"}; +read_inc_file2(FileName, [D|Rem]) -> + Dir = case lists:last(D) of + $/ -> + D; + _ -> + D++"/" + end, + + case catch file:read_file(Dir++FileName) of + {ok, Bin} -> + FileList = binary_to_list(Bin), + {ok, FileList, Dir++FileName}; + {error, _} -> + read_inc_file2(FileName, Rem) + end. + + + + +%%=============================================================== +%% Read parameters of a macro or a variable in a source line +%%=============================================================== +read_para([{char,$(} | Rem]) -> + read_para(Rem, 1, [], [], 1); +read_para([space,{char,$(} | Rem]) -> + read_para(Rem, 1, [], [], 1); +read_para(_Rem) -> + {ok, [], [], 0}. + + +%% Abrupt end of the list +read_para([], _NoOfParen, _Current, _Para, _NoOfPara) -> + {error, illegal_arg}; +%% All parameters checked +read_para([{char,$)}|Rem], 1, [], Para, NoOfPara) -> + {ok, Rem, lists:reverse(Para), NoOfPara}; +read_para([{char,$)}|Rem], 1, Current, Para, NoOfPara) -> + {ok, Rem, lists:reverse([Current|Para]), NoOfPara}; + +%% Continue reading +read_para([{char,$)}|Rem], NoOfParen, Current, Para, NoOfPara) -> + read_para(Rem, NoOfParen-1, Current++[{char,$)}], Para, NoOfPara); +read_para([{char,$(}|Rem], NoOfParen, Current, Para, NoOfPara) -> + read_para(Rem, NoOfParen+1, Current++[{char,$(}], Para, NoOfPara); +read_para([{char,$,}|Rem], NoOfParen, Current, Para, NoOfPara) when NoOfParen == 1 -> + read_para(Rem, NoOfParen, [], [Current|Para], NoOfPara+1); +read_para([space|Rem], NoOfParen, [], Para, NoOfPara) -> + read_para(Rem, NoOfParen, [], Para, NoOfPara); +read_para([X|Rem], NoOfParen, Current, Para, NoOfPara) -> + read_para(Rem, NoOfParen, Current++[X], Para, NoOfPara). + + + + + + +%%=================================================================================== +%% check if a macro is already defined +%%=================================================================================== +is_define_ok(Name, No_of_para, Parameters, Macro, Defs) -> + + case lists:keysearch(Name, 1, Defs) of + {value, {Name, No_of_para, _MacroPara, Macro}} -> + {yes, Defs}; + {value, _} -> + Defs2 = lists:keydelete(Name, 1, Defs), + NewMacro = is_define_ok_check_para(Parameters, Macro, []), + case is_stringify_ok(NewMacro) of + yes -> + {no, [{Name, No_of_para, Parameters, NewMacro}|Defs2]}; + no -> + ErrorText = "`#' operator is not followed by a macro argument name", + {error, ErrorText, [{Name, No_of_para, Parameters, NewMacro}|Defs2]} + end; + false -> + NewMacro = is_define_ok_check_para(Parameters, Macro, []), + case is_stringify_ok(NewMacro) of + yes -> + {yes, [{Name, No_of_para, Parameters, NewMacro}|Defs]}; + no -> + ErrorText = "`#' operator is not followed by a macro argument name", + {error, ErrorText, [{Name, No_of_para, Parameters, NewMacro}|Defs]} + end + end. + +is_define_ok_check_para(_Para, [], Result) -> + lists:reverse(Result); + +is_define_ok_check_para(Para, [H|T], Result) -> + case define_arg_para_number(1, Para, H) of + no_para -> + is_define_ok_check_para(Para, T, [H|Result]); + N -> + is_define_ok_check_para(Para, T, [{para,N}|Result]) + end. + +define_arg_para_number(_N, [], _Current) -> + no_para; +define_arg_para_number(N, [H|_Para], Current) when H == [Current] -> + N; +define_arg_para_number(N, [_H|Para], Current) -> + define_arg_para_number(N+1, Para, Current). + + +is_stringify_ok([]) -> + yes; +is_stringify_ok([{char,$#},{char,$#}|T]) -> + is_stringify_ok(T); +is_stringify_ok([{char,$#},space,{para,_X}|T]) -> + is_stringify_ok(T); +is_stringify_ok([{char,$#},{para,_X}|T]) -> + is_stringify_ok(T); +is_stringify_ok([{char,$#},space,{var,_X}|T]) -> + is_stringify_ok(T); +is_stringify_ok([{char,$#},{var,_X}|T]) -> + is_stringify_ok(T); +is_stringify_ok([{char,$#},space,{nl,_X}|_T]) -> + no; +is_stringify_ok([{char,$#},{nl,_X}|_T]) -> + no; +is_stringify_ok([{char,$#}|_T]) -> + no; +is_stringify_ok([_H|T]) -> + is_stringify_ok(T). + +%%=================================================================================== +%% check if a macro is already defined +%%=================================================================================== +is_defined_before(Name, No_of_para, Defs) -> + case lists:keysearch(Name, 1, Defs) of + {value, {Name, No_of_para, _MacroPara, _Macro}} -> + yes; + {value, _} -> + no; + false -> + no + end. + + + + +%%=================================================================================== +%% read_to_nl(File) +%%=================================================================================== +read_to_nl([space|Rem]) -> + read_to_nl(Rem, [], 1); +read_to_nl(Rem) -> + read_to_nl(Rem, [], 1). + +read_to_nl([], Result, Nl) -> + {lists:reverse(Result), [], Nl}; +read_to_nl([{nl, _N}|Rem], [{string_part,String} | Result], Nl) -> + read_to_nl(Rem, [nl, {string_part,String}|Result], Nl+1); +read_to_nl([{nl, _N}|Rem], [{sys_head_part,String} | Result], Nl) -> + read_to_nl(Rem, [nl, {sys_head_part,String}|Result], Nl+1); +read_to_nl([{nl, _N}|Rem], Result, Nl) -> + {lists:reverse(Result), Rem, Nl}; +read_to_nl([space|Rem], Result, Nl) -> + read_to_nl(Rem, [space|Result], Nl); +read_to_nl([{X,String}|Rem], Result, Nl) -> + read_to_nl(Rem, [{X,String}|Result], Nl). + + + + +%%=========================================================== +%% Read characters until next newline +%%=========================================================== +%read_to_nl2(Str) -> read_to_nl2([],Str). + +%read_to_nl2(Line, []) -> {Line,[]}; +%read_to_nl2(Line, [$\n|Str]) -> {Line, Str}; +%read_to_nl2(Line, [X|Str]) -> read_to_nl2([X|Line], Str). + + + + +%%=========================================================== +%% Remove leading spaces from a list +%%=========================================================== +remove_leading_spaces([?space|List]) -> + remove_leading_spaces(List); +remove_leading_spaces([?tab|List]) -> + remove_leading_spaces(List); +remove_leading_spaces(List) -> + List. + + + + +%%=========================================================== +%% Skip characters until next newline +%%=========================================================== +skip_to_nl([]) -> []; +skip_to_nl([$\n | Str]) -> Str; +skip_to_nl([$\\,$\n | Str]) -> [$/,$/|Str]; +skip_to_nl([_|Str]) -> skip_to_nl(Str). + + + + +month(1) -> "Jan"; +month(2) -> "Feb"; +month(3) -> "Mar"; +month(4) -> "Apr"; +month(5) -> "May"; +month(6) -> "Jun"; +month(7) -> "Jul"; +month(8) -> "Aug"; +month(9) -> "Sep"; +month(10) -> "Oct"; +month(11) -> "Nov"; +month(12) -> "Dec". + + + + diff --git a/lib/ic/src/ic_pragma.erl b/lib/ic/src/ic_pragma.erl new file mode 100644 index 0000000000..9165e3b03b --- /dev/null +++ b/lib/ic/src/ic_pragma.erl @@ -0,0 +1,1957 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ic_pragma). + + +-export([pragma_reg/2,pragma_cover/3]). +-export([pragma_prefix/3,pragma_version/3,pragma_id/3]). +-export([mk_alias/3,get_alias/2,scope2id/2,id2scope/2,mk_scope/1]). +-export([mk_ref/3,get_incl_refs/1,get_local_refs/1]). +-export([get_dependencies/1, add_inh_data/3, preproc/3]). +-export([getBrokerData/3,defaultBrokerData/1,list_to_term/1]). +-export([get_local_c_headers/2,get_included_c_headers/1,is_inherited_by/3]). +-export([no_doubles/1,fetchRandomLocalType/1,fetchLocalOperationNames/2]). +-export([is_local/2]). + +%% Debug +-export([print_tab/1,slashify/1,is_short/1]). + +-import(lists,[suffix/2,delete/2,reverse/1,keysearch/3,member/2,last/1,flatten/1]). +-import(string,[tokens/2]). +-import(ets,[insert/2,lookup/2]). + +-import(ic_forms, [get_id2/1, get_body/1, get_line/1]). +-import(ic_util, [to_atom/1]). +-import(ic_genobj, [idlfile/1]). +-import(ic_options, [get_opt/2]). + +-include("icforms.hrl"). +-include("ic.hrl"). + + + + +%% Initialization of the pragma table and +%% start of pragma registration. +%% NOTE : this pragma registration is build +%% as a separate stage under compilation. +%% If it is to be optimised, it should be +%% embodied in one of other compiling stages. +pragma_reg(G,X) -> + S = ic_genobj:pragmatab(G), + init_idlfile(G,S), + init_pragma_status(S), + registerOptions(G,S), + pragma_reg_all(G, S, [], X), + denote_specific_code_opts(G), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case get_pragma_compilation_status(S) of + true -> + %% Remove ugly pragmas from form + PragmaCleanForm = cleanup(X), + {ok,PragmaCleanForm}; + false -> + ErrorNr = get_pragma_error_nr(S), + %% Just print the number of errors found + case ErrorNr > 1 of + true -> + io:format("There were ~p errors found on file ~p~n", + [ErrorNr,get_idlfile(S)]), + error; + false -> + io:format("There were ~p error found on file ~p~n", + [ErrorNr,get_idlfile(S)]), + error + end + end. + + + +registerOptions(G,S) -> + OptList = ets:tab2list(ic_genobj:optiontab(G)), + registerOptions(G,S,OptList). + + +registerOptions(_G,_S,[]) -> + true; +registerOptions(G,S,[{{option,{broker,Scope}},{Mod,Type}}|Rest]) -> + insert(S, + {codeopt, + reverse(tokens(Scope,":")), + {broker,{Mod,Type}}, + -1, + nil, + nil}), + registerOptions(G,S,Rest); +registerOptions(G,S,[_|Rest]) -> + registerOptions(G,S,Rest). + + +%% Decide if to apply pragmas +%% by checking backend switch +applyPragmasInBe(G) -> + case get_opt(G, be) of + erl_plain -> + false; + _ -> + true + end. + + +%% Decide if the code option directive +%% is allowed to change backend +applyCodeOpt(G) -> + case get_opt(G, be) of + erl_corba -> %% Does not support codeopt + false; + erl_plain -> %% Does not support codeopt + false; + c_native -> %% Does not support codeopt + false; + _ -> + true + end. + + + +%% This removes all pragma records from the form. +%% When debugged, it can be enbodied in pragma_reg_all. +cleanup([],C) -> C; +cleanup([X|Xs],CSF) -> + cleanup(Xs, CSF++cleanup(X)). + +cleanup(X) when is_list(X) -> cleanup(X,[]); +cleanup(X) when is_record(X, preproc) -> [X]; +cleanup(X) when is_record(X, pragma) -> []; +cleanup(X) when is_record(X, op) -> % Clean inside operation parameters + [ X#op{params = cleanup(X#op.params,[])}]; + +cleanup(X) when is_record(X, module) -> % Clean inside module body + [ X#module{body = cleanup(X#module.body,[])}]; + +cleanup(X) when is_record(X, interface) -> % Clean inside interface body + [ X#interface{body = cleanup(X#interface.body,[])}]; + +cleanup(X) when is_record(X, except) -> % Clean inside exception body + [ X#except{body = cleanup(X#except.body,[])}]; + +cleanup(X) when is_record(X, struct) -> % Clean inside struct body + [ X#struct{body = cleanup(X#struct.body,[])}]; + +cleanup(X) when is_record(X, case_dcl) -> % Clean inside union body + [ X#case_dcl{label = cleanup(X#case_dcl.label,[])}]; + +cleanup(X) when is_record(X, union) -> % Clean inside union body + [ X#union{body = cleanup(X#union.body,[])}]; + +cleanup(X) when is_record(X, enum) -> % Clean inside enum body + [ X#enum{body = cleanup(X#enum.body,[])}]; + +cleanup(X) -> [X]. + + + + +%% pragma_reg_all is top level registration for pragmas +pragma_reg_all(_G, _S, _N, []) -> ok; +pragma_reg_all(G, S, N, [X|Xs]) -> + pragma_reg(G, S, N, X), + pragma_reg_all(G, S, N, Xs). + + +%% pragma_reg is top level registration for pragmas +pragma_reg(G, S, N, X) when is_list(X) -> pragma_reg_list(G, S, N, X); +pragma_reg(_G, S, _N, X) when element(1, X) == preproc -> + case X#preproc.aux of + [{_, _, "1"}] -> + IncludeEntryLNr = get_line(X#preproc.id), + IncludeFileName = element(3,element(3,X)), + insert(S,{includes,get_idlfile(S),IncludeFileName,IncludeEntryLNr}); + _Other -> + ok + end, + set_idlfile(S,element(3,element(3,X))); +pragma_reg(G, S, N, X) when element(1, X) == pragma -> + case applyPragmasInBe(G) of + + %% Pragmas are allowed to be + %% applied in this this backend. + true -> + + File = get_idlfile(S), % The current file or an included one. + Type = case idlfile(G) of % Local/Included flag + File -> + local; + _ -> + included + end, + + %% Register pragmas into pragmatab. + case X of + {pragma,{_,LineNr,"prefix"}, _To, _Apply} -> + insert(S,{prefix,X,LineNr,N,File,Type}); + + {pragma,{_,_,"ID"},_,_} -> + pragma_reg_ID(G, S, N, X); + + {pragma,{_,_,"version"},_,_} -> + pragma_reg_version(G, S, N, X ); + + {pragma,{_,_,"CODEOPT"},_,_} -> + pragma_reg_codeOpt(G,S,N,X); + + {pragma,{_,LineNr,BadPragma}, _To, _Apply} -> + io:format("Warning : on file ~p :~n",[get_idlfile(S)]), + io:format(" Unknown pragma directive ~p on line ~p, ignored.~n", + [BadPragma,LineNr]) + end; + + %% Pragmas are not to be applied in + %% this backend, ignore all pragmas. + false -> + true + end, + ok; + +pragma_reg(G, S, N, X) when is_record(X, module) -> + mk_ref(G,[get_id2(X) | N],mod_ref), + mk_file_data(G,X,N,module), + pragma_reg_all(G, S, [get_id2(X) | N], get_body(X)); + +pragma_reg(G, S, N, X) when is_record(X, interface) -> + mk_ref(G,[get_id2(X) | N],ifc_ref), + mk_file_data(G,X,N,interface), + pragma_reg_all(G, S, [get_id2(X) | N], get_body(X)); + +pragma_reg(G, S, N, X) when is_record(X, op) -> + %% Add operation in table + insert(S,{op, + get_id2(X), + N, + get_idlfile(S), + get_filepath(S)}), + mk_file_data(G,X,N,op), + pragma_reg_all(G, S, N, X#op.params); + +pragma_reg(G, S, N, X) when is_record(X, except) -> + mk_ref(G,[get_id2(X) | N],except_ref), + mk_file_data(G,X,N,except), + pragma_reg_all(G, S, N, X#except.body); + +pragma_reg(G, _S, N, X) when is_record(X, const) -> + mk_ref(G,[get_id2(X) | N],const_ref), + mk_file_data(G,X,N,const); + +pragma_reg(G, _S, N, X) when is_record(X, typedef) -> + XX = #id_of{type=X}, + lists:foreach(fun(Id) -> + mk_ref(G,[get_id2(Id) | N],typedef_ref), + mk_file_data(G,XX#id_of{id=Id},N,typedef) + end, + ic_forms:get_idlist(X)); + +pragma_reg(G, S, N, X) when is_record(X, enum) -> + mk_ref(G,[get_id2(X) | N],enum_ref), + mk_file_data(G,X,N,enum), + pragma_reg_all(G, S, N, X#enum.body); + +pragma_reg(G, S, N, X) when is_record(X, union) -> + mk_ref(G,[get_id2(X) | N],union_ref), + mk_file_data(G,X,N,union), + pragma_reg_all(G, S, N, X#union.body); + +pragma_reg(G, S, N, X) when is_record(X, struct) -> + mk_ref(G,[get_id2(X) | N],struct_ref), + mk_file_data(G,X,N,struct), + pragma_reg_all(G, S, N, X#struct.body); + +pragma_reg(G, _S, N, X) when is_record(X, attr) -> + XX = #id_of{type=X}, + lists:foreach(fun(Id) -> + mk_ref(G,[get_id2(Id) | N],attr_ref), + mk_file_data(G,XX#id_of{id=Id},N,attr) + end, + ic_forms:get_idlist(X)); + +pragma_reg(_G, _S, _N, _X) -> ok. + + + + +pragma_reg_list(_G, _S, _N, []) -> ok; +pragma_reg_list(G, S, N, List ) -> + CurrentFileName = get_idlfile(S), + pragma_reg_list(G, S, N, CurrentFileName, List). + +pragma_reg_list(_G, _S, _N, _CFN, []) -> ok; +pragma_reg_list(G, S, N, CFN, [X | Xs]) -> + case X of + {preproc,_,{_,_,FileName},_} -> + set_idlfile(S,FileName), + pragma_reg(G, S, N, X), + pragma_reg_list(G, S, N, FileName, Xs); + _ -> + pragma_reg(G, S, N, X), + pragma_reg_list(G, S, N, CFN, Xs) + end. + + + + + +pragma_reg_ID(G, S, N, X) -> + {pragma,{_,LineNr,"ID"}, _To, Apply} = X, + + + File = get_idlfile(S), % The current file or an included one. + Type = case idlfile(G) of % Local/Included flag + File -> + local; + _ -> + included + end, + + %% Check if ID is one of the allowed types : + %% * OMG IDL + %% * DCE UUID + %% * LOCAL + case tokens(element(3,Apply),":") of + ["IDL",_,_] -> + insert(S,{id,X,LineNr,N,File,Type}); + ["DCE",_,VSN] -> + case is_short(VSN) of + true -> + insert(S,{id,X,LineNr,N,File,Type}); + false -> + set_compilation_failure(S), + io:format("Error on file ~p :~n",[get_idlfile(S)]), + io:format(" Bad pragma ID ~p on line ~p,~n", + [element(3,Apply),LineNr]), + io:format(" the version part of ID is not a short integer.~n") + end; + ["LOCAL"|_] -> + insert(S,{id,X,LineNr,N,File,Type}); + _ -> + set_compilation_failure(S), + io:format("Error on file ~p :~n",[get_idlfile(S)]), + io:format(" Bad pragma ID ~p on line ~p.~n", + [element(3,Apply),LineNr]) + end. + + + +pragma_reg_version(G, S, N, X) -> + {pragma,{_,LineNr,"version"}, _To, Apply} = X, + + File = get_idlfile(S), % The current file or an included one. + Type = case idlfile(G) of % Local/Included flag + File -> + local; + _ -> + included + end, + + case tokens(Apply,".") of + [Major,Minor] -> + case is_short(Major) and is_short(Minor) of + true -> + insert(S,{version,X,LineNr,N,File,Type}); + false -> + set_compilation_failure(S), + io:format("Error on file ~p :~n",[get_idlfile(S)]), + io:format(" Bad pragma version ~p on line ~p,~n", + [Apply,LineNr]), + io:format(" the version is not valid.~n") + end; + _ -> + set_compilation_failure(S), + io:format("Error on file ~p :~n",[get_idlfile(S)]), + io:format(" Bad pragma version ~p on line ~p,~n", + [Apply,LineNr]), + io:format(" the version is not valid.~n") + end. + + +pragma_reg_codeOpt(G, S, _N, {pragma,{_,LineNr,"CODEOPT"},_,Apply} )-> + case applyCodeOpt(G) of + true -> + {_,_,OptionList_str} = Apply, + case list_to_term(OptionList_str) of + error -> + ic_error:error(G,{pragma_code_opt_bad_option_list,LineNr}); + OptionList -> + case lists:keysearch(be,1,OptionList) of + false -> + %% Add the terms of the option list + %% to the compiler option list + applyCodeOpts(G,S,LineNr,OptionList); + {value, {be,Type}} -> + %% If backend is set from user, + %% let the same backend be otherwize + %% set backend by codeOpt directive + case get_opt(G, be) of + false -> + %% Add the terms of the option list + %% to the compiler option list + applyCodeOpts(G,S,LineNr,OptionList); + _ -> + %% Add all the terms of the option list + %% to the compiler option list but the + %% backend option + applyCodeOpts(G, + S, + LineNr, + lists:delete({be,Type},OptionList)) + end + end + end; + false -> + true + end. + + + +applyCodeOpts(_,_,_,[]) -> + true; +applyCodeOpts(G,S,LNr,[{{broker,Scope},{M,T}}|Xs]) -> + ScopedId = reverse(tokens(Scope,":")), + case ets:match(S, + {codeopt,ScopedId, + '$1','$2','_','_'}) of + [] -> + %% Add pragma in table + insert(S, + {codeopt, + ScopedId, + {broker,{M,T}}, + LNr, + get_idlfile(S), + get_filepath(S)}), + %% Continue + applyCodeOpts(G,S,LNr,Xs); + _ -> + %% Use the code option + %% from user and continue + applyCodeOpts(G,S,LNr,Xs) + end; +applyCodeOpts(G,S,LNr,[X|Xs]) -> + case is_allowed_opt(X) of + true -> + %% Add that term of the option list + %% to the compiler option list + ic_options:add_opt(G, [X], true), + %% Continue + applyCodeOpts(G,S,LNr,Xs); + false -> + %% Print warning and continue + io:format("Warning on file ~p :~n",[get_idlfile(S)]), + io:format(" Bad option in pragma : ~p, ignored !~n",[X]), + applyCodeOpts(G,S,LNr,Xs) + end. + + +is_allowed_opt({X,Y}) -> + ic_options:allowed_opt(X,Y); +is_allowed_opt(_X) -> + false. + + + +%% Returns a tuple { PFX, VSN, ID }, that is the +%% pragma prefix, version and id coverages of +%% the scope SCOPE. This is done by use of the +%% function pragma_cover/4. +pragma_cover(G,Scope,Object) -> + pragma_cover(ic_genobj:pragmatab(G),get_id2(Object),Scope,get_line(Object)). + +%% Returns a tuple { PFX, VSN, ID }, that is the +%% pragma prefix, version and id coverages of +%% the scope SCOPE +pragma_cover(PragmaTab,Name,Scope,LineNr) -> + PFX = pragma_prefix_cover(PragmaTab,Name,Scope,LineNr), + VSN = pragma_version_cover(PragmaTab,Name,Scope,LineNr), + ID = pragma_id_cover(PragmaTab,Name,Scope,LineNr), + { PFX, VSN, ID }. + + + +%% Finds out which pragma PREFIX that affects +%% the scope Scope +pragma_prefix(G,Scope,Object) -> + pragma_prefix_cover(ic_genobj:pragmatab(G),get_id2(Object),Scope,get_line(Object)). + + +%% Finds out which pragma PREFIX that affects +%% the scope Scope +pragma_prefix_cover(PragmaTab,Name,Scope,LineNr) -> + case lookup(PragmaTab,prefix) of + [] -> + none; + PragmaPrefixList -> + FilteredPragmaPrefixList = + filter_pragma_prefix_list(PragmaTab,Name,Scope,PragmaPrefixList), + case most_local(FilteredPragmaPrefixList,Scope) of + [] -> + none; + MostLocalList -> + case dominant_prefix(MostLocalList,LineNr) of + none -> + none; + + %% Just filter empty pragma prefix + {prefix,{pragma,{_,_,_},_,{'<string_literal>',_,[]}},_,_,_,_} -> + none; + + DP -> + %% Return the scoped id (reversed list of + %% path elements, but remember to remove + %% '[]' that represents the top level + slashify(lists:sublist(Scope, 1, + length(Scope) - length(element(4,DP))) ++ + [ element(3,element(4,element(2,DP)))]) + end + end + end. + + +%% Returns a slashified name, [I1, M1] becomes "M1/I1" +slashify(List) -> lists:foldl(fun(X, Acc) -> X++"/"++Acc end, + hd(List), tl(List)). + + +%% Finds out which pragma VERSION that affects +%% the scope Scope +pragma_version(G,Scope,Object) -> + pragma_version_cover(ic_genobj:pragmatab(G),get_id2(Object),Scope,get_line(Object)). + +%% Finds out which pragma VERSION that affects +%% the scope Scope +pragma_version_cover(PragmaTab,Name,Scope,LineNr) -> + case lookup(PragmaTab,version) of + [] -> + default_version(); + PragmaVersionList -> + case all_actual_for_version_or_id( PragmaVersionList, Name ) of + [] -> + default_version(); + ActualVersionList -> + case most_local(ActualVersionList,Scope) of + [] -> + default_version(); + MostLocalList -> + case dominant_version(MostLocalList,LineNr) of + DV -> + element(4,element(2,DV)) + end + end + end + end. + + +default_version() -> "1.0". + + + +%% Finds out which pragma ID that affects +%% the scope Scope +pragma_id(G,Scope,Object) -> + pragma_id_cover(ic_genobj:pragmatab(G),get_id2(Object),Scope,get_line(Object)). + +%% Finds out which pragma ID that affects +%% the scope Scope +pragma_id_cover(PragmaTab,Name,Scope,LineNr) -> + case lookup(PragmaTab,id) of + [] -> + none; + PragmaIdList -> + case all_actual_for_version_or_id( PragmaIdList, Name ) of + [] -> + none; + ActualIdList -> + case most_local(ActualIdList,Scope) of + [] -> + none; + MostLocalList -> + case dominant_id(MostLocalList,LineNr) of + PI -> + element(3,element(4,element(2,PI))) + end + end + end + end. + + + + +%% Finds out which pragma VERSION ( or ID ) that +%% that affects the scope object with name NAME +all_actual_for_version_or_id(NList, Name) -> + all_actual_for_version_or_id( NList, [], Name ). + +all_actual_for_version_or_id([], Actual, _) -> + Actual; +all_actual_for_version_or_id([First|Rest], Found, Name) -> + case is_actual_for_version_or_id(First,Name) of + true -> + all_actual_for_version_or_id(Rest, [First|Found], Name); + false -> + all_actual_for_version_or_id(Rest, Found, Name) + end. + +is_actual_for_version_or_id( Current, Name ) -> + case element(3,element(3,element(2,Current))) of + Name -> + true; + OtherName -> + suffix([Name],tokens(OtherName,"::")) + end. + + + + +%% Find the most locally defind pragmas +%% to the scope SCOPE +most_local( SList, Scope ) -> + case SList of + [] -> + []; + [First|Rest] -> + case suffix( element(4,First), Scope ) of + true -> + most_local( Rest, First, Scope, [First] ); + false -> + most_local( Rest, Scope ) + end + end. + +%% Returns a list of all pragmas found in the +%% same scope. Should choose the right one by looking +%% att the position of the pragma in relation to +%% the current object..... ( For hairy cases ). +most_local( SList, Current, Scope, AllFound ) -> + case SList of + [] -> + AllFound; + [First|Rest] -> + FirstScope = element(4,First), + case suffix( FirstScope, Scope ) of + true -> + CurrentScope = element(4,Current), + case suffix( CurrentScope, FirstScope ) of + true -> + case length( CurrentScope ) == length( FirstScope ) of + true -> %% SAME SCOPE ! KEEP BOTH + most_local( Rest, Current, Scope, [First|AllFound] ); + false -> + most_local( Rest, First, Scope, [First] ) + end; + false -> + most_local( Rest, Current, Scope, AllFound ) + end; + false -> + most_local( Rest, Current, Scope, AllFound ) + end + end. + + + + +%% Find the most dominant prefix pragmas +%% located onto the SAME scope. Now +%% we look att the line number, the position +%% on the file. +dominant_prefix(SList,LineNr) -> + case SList of + [First|Rest] -> + dominant_prefix(Rest,First,LineNr) + end. + + +dominant_prefix([],{prefix,X,PLNr,N,F,T},LineNr) -> + case LineNr > PLNr of + true -> + {prefix,X,PLNr,N,F,T}; + false -> + none + end; +dominant_prefix([{prefix,FX,FPLNr,FN,F1,T1}|Rest],{prefix,CX,CPLNr,CN,F2,T2},LineNr) -> + case LineNr > FPLNr of % Check if FIRST before the object + true -> + case FPLNr > CPLNr of % Check if FIRST after CURRENT + true -> + dominant_prefix(Rest,{prefix,FX,FPLNr,FN,F1,T1},LineNr); + false -> + dominant_prefix(Rest,{prefix,CX,CPLNr,CN,F2,T2},LineNr) + end; + false -> % FIRST does not affect the object + dominant_prefix(Rest,{prefix,CX,CPLNr,CN,F2,T2},LineNr) + end. + + + + +%% Find the most dominant version pragmas +%% located onto the SAME scope. Now +%% we look att the line number, the position +%% on the file. +dominant_version(SList,LineNr) -> + case SList of + [First|Rest] -> + dominant_version(Rest,First,LineNr) + end. + + +dominant_version([],Current,_) -> Current; +dominant_version([{version,FX,FPLNr,FN,F1,T1}|Rest],{version,CX,CPLNr,CN,F2,T2},LineNr) -> + case FPLNr > CPLNr of % Check if FIRST after CURRENT + true -> + dominant_version(Rest,{prefix,FX,FPLNr,FN,F1,T1},LineNr); + false -> + dominant_version(Rest,{prefix,CX,CPLNr,CN,F2,T2},LineNr) + end. + + + + +%% Find the most dominant id pragmas +%% located onto the SAME scope. Now +%% we look att the line number, the position +%% on the file. +dominant_id(SList,LineNr) -> + case SList of + [First|Rest] -> + dominant_id(Rest,First,LineNr) + end. + + +dominant_id([],Current,_) -> Current; +dominant_id([{id,FX,FPLNr,FN,F1,T1}|Rest],{id,CX,CPLNr,CN,F2,T2},LineNr) -> + case FPLNr > CPLNr of % Check if FIRST after CURRENT + true -> + dominant_id(Rest,{id,FX,FPLNr,FN,F1,T1},LineNr); + false -> + dominant_id(Rest,{id,CX,CPLNr,CN,F2,T2},LineNr) + end. + + + + + +%% This registers a module defined inside the file or +%% an included file. A tuple that describes the module +%% is added to the table. +%% Observe that the modules registered are ONLY those +%% who are in the top level, not definedd inside others ! +mk_ref(G,Name,Type) -> + case length(Name) > 1 of + true -> %% The interface is NOT defined att top level + true; + false -> + S = ic_genobj:pragmatab(G), + File = get_idlfile(S), % The current file or an included one. + case idlfile(G) of % The current file to be compiled. + File -> + insert(S,{Type,Name,File,local}); + _ -> + insert(S,{Type,Name,File,included}) + end + end. + + +%% The same as mk_ref/3 but this registers everything with +%% all vital information available inside files. +%% Registers ESSENTIAL data for included files +mk_file_data(G,X,Scope,Type) -> + S = ic_genobj:pragmatab(G), + Name = get_id2(X), + PreprocFile = get_idlfile(S), % The current file or an included one. + CompFile = idlfile(G), % The current file compiled + Depth = length(Scope), % The depth of the scope + ScopedName = ic_util:to_undersc([Name|Scope]), + Line = ic_forms:get_line(X), + case PreprocFile of + CompFile -> + insert(S,{file_data_local,CompFile,CompFile,Type,Scope,Name,ScopedName,Depth,Line}); + PreprocFile -> + insert(S,{file_data_included,PreprocFile,CompFile,Type,Scope,Name,ScopedName,Depth,Line}) + end. + + + +%% Return a list with all the headers from +%% the local file that represent the module +%% or interface that is preciding the current +get_local_c_headers(G,X) -> + S = ic_genobj:pragmatab(G), + Local = lookup(S,file_data_local), + FoundLocal = get_local_c_headers(X,Local,Local), + no_doubles(FoundLocal). + +get_local_c_headers(X,Local,Local) -> + get_local_c_headers(X,Local,Local,[]). + +get_local_c_headers(_X,[],_All,Found) -> + Found; +get_local_c_headers(X,[{file_data_local,_PF_idl,_,module,_,_,SN,_,Line}|Hs],All,Found)-> + case ic_forms:get_line(X) > Line of + true -> + get_local_c_headers(X,Hs,All,[SN|Found]); + false -> + get_local_c_headers(X,Hs,All,Found) + end; +get_local_c_headers(X,[{file_data_local,_PF_idl,_,interface,_,_,SN,_,Line}|Hs],All,Found)-> + case ic_forms:get_line(X) > Line of + true -> + get_local_c_headers(X,Hs,All,[SN|Found]); + false -> + get_local_c_headers(X,Hs,All,Found) + end; +get_local_c_headers(X,[_|Hs],All,Found) -> + get_local_c_headers(X,Hs,All,Found). + + + +%% Return a list with all the headers from +%% the included file that represent the module +%% or interface that have to be included +get_included_c_headers(G) -> + S = ic_genobj:pragmatab(G), + Included = lookup(S,file_data_included), + FoundIncluded = get_included_c_headers(Included,Included), + no_doubles(FoundIncluded). + +get_included_c_headers(Included,Included) -> + get_included_c_headers(Included,Included,[]). + +get_included_c_headers([],_All,Found) -> + Found; +get_included_c_headers([{file_data_included,PF_idl,_CF_idl,T,_S,_N,SN,0,_}|Hs],All,Found) -> + Len = length(PF_idl), + FN = string:sub_string(PF_idl,1,Len-4), + case only_top_level(PF_idl,All) of + true -> + %% + L = string:tokens(FN,"/"), + FN2 = lists:last(L), + %% + get_included_c_headers(Hs,All,["oe_"++FN2|Found]); + false -> + case T of + module -> + case contains_interface(PF_idl,All) of + true -> + %% + L = string:tokens(FN,"/"), + FN2 = lists:last(L), + %% + get_included_c_headers(Hs,All,["oe_"++FN2|Found]); + false -> + get_included_c_headers(Hs,All,[SN|Found]) + end; + interface -> + case contains_interface(PF_idl,All) of + true -> + %% + L = string:tokens(FN,"/"), + FN2 = lists:last(L), + %% + get_included_c_headers(Hs,All,["oe_"++FN2|Found]); + false -> + get_included_c_headers(Hs,All,[SN|Found]) + end; + _ -> + get_included_c_headers(Hs,All,["oe_"++FN|Found]) + end + end; +get_included_c_headers([{file_data_included,_PF_idl,_,module,_,_,SN,_,_}|Hs],All,Found)-> + get_included_c_headers(Hs,All,[SN|Found]); +get_included_c_headers([{file_data_included,_PF_idl,_,interface,_,_,SN,_,_}|Hs],All,Found)-> + get_included_c_headers(Hs,All,[SN|Found]); +get_included_c_headers([_|Hs],All,Found) -> + get_included_c_headers(Hs,All,Found). + +%% Help functions for the above + +only_top_level(_PF_idl,[]) -> + true; +only_top_level(PF_idl,[H|Hs]) -> + case element(2,H) of + PF_idl -> + case element(8,H) > 0 of + true -> + false; + false -> + only_top_level(PF_idl,Hs) + end; + _ -> + only_top_level(PF_idl,Hs) + end. + +contains_interface(_PF_idl,[]) -> + false; +contains_interface(PF_idl,[H|Hs]) -> + case element(2,H) of + PF_idl -> + case element(4,H) of + interface -> + case element(8,H) > 0 of + true -> + true; + false -> + contains_interface(PF_idl,Hs) + end; + _ -> + contains_interface(PF_idl,Hs) + end; + _ -> + contains_interface(PF_idl,Hs) + end. + + + +%% This returns a list of everything defined in an included file. +get_incl_refs(G) -> + S = ic_genobj:pragmatab(G), + + RefList = + ets:match(S,{mod_ref,'$0','_',included}) ++ + ets:match(S,{ifc_ref,'$0','_',included}) ++ + ets:match(S,{const_ref,'$0','_',included}) ++ + ets:match(S,{typedef_ref,'$0','_',included}) ++ + ets:match(S,{except_ref,'$0','_',included}) ++ + ets:match(S,{struct_ref,'$0','_',included}) ++ + ets:match(S,{union_ref,'$0','_',included}) ++ + ets:match(S,{enum_ref,'$0','_',included}) ++ + ets:match(S,{attr_ref,'$0','_',included}), + + case RefList of + [] -> + none; + _ -> + RefList + end. + + + +%% This returns a list of everything locally defined. +get_local_refs(G) -> + S = ic_genobj:pragmatab(G), + + RefList = + ets:match(S,{mod_ref,'$0','_',local}) ++ + ets:match(S,{ifc_ref,'$0','_',local}) ++ + ets:match(S,{const_ref,'$0','_',local}) ++ + ets:match(S,{typedef_ref,'$0','_',local}) ++ + ets:match(S,{except_ref,'$0','_',local}) ++ + ets:match(S,{struct_ref,'$0','_',local}) ++ + ets:match(S,{union_ref,'$0','_',local}) ++ + ets:match(S,{enum_ref,'$0','_',local}) ++ + ets:match(S,{attr_ref,'$0','_',local}), + + case RefList of + [] -> + none; + _ -> + RefList + end. + + + + + +%% This is intented to be used for solving the identification +%% problem introduced by pragmas. It creates aliases between +%% scoped and "final" identities. +mk_alias(G,PragmaId,ScopedId) -> + %io:format("~nMaking alias -> ~p~n",[PragmaId]), + S = ic_genobj:pragmatab(G), + insert(S,{alias,ScopedId,PragmaId}). + + +%% This is used to find out if the object described with +%% the scoped id is created. If this is the case, it should +%% be registered as an alias and the identity of the object +%% is returned. Otherwize "none" is returned. +get_alias(G,ScopedId) -> + S = ic_genobj:pragmatab(G), + case ets:match(S,{alias,ScopedId,'$1'}) of + [] -> + none; + [[IfrId]] -> + %io:format("~nFound alias -> ~p~n",[IfrId]), + IfrId + end. + + + +%% Returns the alias id or constructs an id +scope2id(G,ScopedId) -> + case get_alias(G,ScopedId) of + none -> + case is_included(G,ScopedId) of + true -> %% File included + get_included_IR_ID(G,ScopedId); + false -> %% File local + NewIfrId = mk_id(ScopedId), % Create a "standard" id + mk_alias(G,NewIfrId,ScopedId), % Create an alias + NewIfrId + end; + IfrId -> + IfrId + end. + + + + +is_included(G,ScopedId) -> + S = ic_genobj:pragmatab(G), + Name = ic_util:to_undersc(ScopedId), + case ets:match(S,{file_data_included,'_','_','_','_','_',Name,'_','_'}) of + [[]] -> + true; + _ -> + false + end. + + + +get_included_IR_ID(G,ScopedId) -> + S = ic_genobj:pragmatab(G), + ScopedName = ic_util:to_undersc(ScopedId), + [[Scope,Name,LNr]] = ets:match(S,{file_data_included,'_','_','_','$3','$4',ScopedName,'_','$7'}), + {Prefix,Vsn,Id} = pragma_cover(S,Name,Scope,LNr), + case Id of + none -> + case Prefix of + none -> + IR_ID = + lists:flatten(io_lib:format("IDL:~s:~s",[ScopedName, Vsn])), + ic_pragma:mk_alias(G,IR_ID,ScopedId), + IR_ID; + _ -> + IR_ID = + lists:flatten(io_lib:format("IDL:~s:~s",[Prefix ++ "/" ++ ScopedName, Vsn])), + ic_pragma:mk_alias(G,IR_ID,ScopedId), + IR_ID + end; + _ -> + ic_pragma:mk_alias(G,Id,ScopedId), + Id + end. + + + + + +%% Returns the scope for object +id2scope(G,IfrId) -> + S = ic_genobj:pragmatab(G), + case lookup(S,alias) of + [] -> + mk_scope(IfrId); + AliasList -> + case keysearch(IfrId,3,AliasList) of + false -> + mk_scope(IfrId); + {value,{alias,ScopedId,_}} -> + ScopedId + end + end. + +%% Returns a "standard" IDL ID by getting the scope list +mk_id(ScopedId) -> + "IDL:" ++ ic_pragma:slashify(ScopedId) ++ ":" ++ default_version(). + +%% Returns the scope of an object when getting a "standard" IDL ID +mk_scope(IfrId) -> + [_,Body,_] = tokens(IfrId,":"), + reverse(tokens(Body,"/")). + + + +%% This is used to note the exact compiled file +%% under pragma creation. There are two options, the +%% main file or files included by the main file. This +%% just denotes the CURRENT file, the main file or +%% the included ones. A very usual field is the file +%% path that shows the include path of the file + +init_idlfile(G,S) -> + IdlFile = idlfile(G), + insert(S,{file,IdlFile,[]}). + +set_idlfile(S,FileName) -> + FilePath = get_filepath(S), + case FilePath of + [] -> + ets:delete(S,file), + insert(S,{file,FileName,[FileName|FilePath]}); + _ -> + case hd(FilePath) of + [] -> + ets:delete(S,file), + insert(S,{file,FileName,[FileName|FilePath]}); + _ -> + case tl(FilePath) of + [] -> + ets:delete(S,file), + insert(S,{file,FileName,[FileName|FilePath]}); + _ -> + case hd(tl(FilePath)) of + [] -> + ets:delete(S,file), + insert(S,{file,FileName,[FileName|FilePath]}); + FileName -> + ets:delete(S,file), + insert(S,{dependency,FilePath}), % Add dependency branch + insert(S,{file,FileName,tl(FilePath)}); + _ -> + ets:delete(S,file), + insert(S,{file,FileName,[FileName|FilePath]}) + end + end + end + end. + +get_idlfile(S) -> + [FT] = lookup(S,file), + element(2,FT). + +get_filepath(S) -> + [FT] = lookup(S,file), + element(3,FT). + + +%% This returns a list of file names +%% that direct or indirect the current +%% compiled file is depended on. +get_dependencies(G) -> + S = ic_genobj:pragmatab(G), + case lookup(S,dependency) of + [] -> + []; + Dependencies -> + {get_idlfile(S),get_dependencies(Dependencies,[])} + end. + +get_dependencies([],Dependencies) -> + no_doubles(Dependencies); +get_dependencies([{dependency,Path}|Tail],Current) -> + get_dependencies(Tail,[hd(Path)|Current]). + + +no_doubles(List) -> + no_doubles(List,[]). + +no_doubles([],NoDoubles) -> + NoDoubles; +no_doubles([X|Xs],Current) -> + case member(X,Xs) of + true -> + no_doubles(Xs,Current); + false -> + no_doubles(Xs,[X|Current]) + end. + + + + +%% Pragma compilation status initialization +init_pragma_status(S) -> + insert(S,{status,true,0}). + +%% Pragma compilation status set to failure +%% and count up the number of errors +set_compilation_failure(S) -> + [{status,_,ErrorNr}] = lookup(S,status), + ets:delete(S,status), + insert(S,{status,false,ErrorNr+1}). + +%% Pragma compilation status set to lookup +get_pragma_compilation_status(S) -> + [Status] = lookup(S,status), + element(2,Status). + +%% Pragma error number +get_pragma_error_nr(S) -> + [Status] = lookup(S,status), + element(3,Status). + + +%% Short check +is_short(N_str) when is_list(N_str) -> + case is_short_decimal_str(N_str) of + true -> + true; + false -> + false + end; +is_short(N) when is_integer(N)-> + (N < 65535) and (N > -65536); +is_short(_) -> false. + + +%% Check if the string is a +%% list of characters representing +%% a short. Avoid crash !. +is_short_decimal_str(N_str) -> + case is_decimal_str(N_str) of + true -> + N = list_to_integer(N_str), + (N < 65535) and (N > -65536); + false -> + false + end. + +%% Check if the string is a +%% list of characters representing +%% decimals. +is_decimal_str([]) -> + true; +is_decimal_str([First|Rest]) -> + case is_decimal_char(First) of + true -> + is_decimal_str(Rest); + false -> + false + end. + +%% True if D is a character +%% representing a decimal (0-9). +is_decimal_char(D) -> + case (48=<D) and (D=<57) of + true -> + true; + false -> + false + end. + + +%% Prints out all the table +print_tab(G) -> + io:format("~nPragmaTab = ~p~n",[ets:tab2list(ic_genobj:pragmatab(G))]). + + +list_to_term(List) -> + case catch erl_scan:string(List) of + {ok, Tokens, _} -> + case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of + {ok,Term} -> + Term; + _ -> + error + end; + _ -> + error + end. + + + +%% Cleanup all other code options for a specified scope +%% in the same file, but the most dominant. +cleanup_codeOptions(G,S,ScopedId) -> + case ets:match(S,{codeopt,ScopedId,'$1','$2',idlfile(G),'$4'}) of + [] -> + %% No codeOpt directive is placed inside the + %% currently compiled file. Try to find other + %% directives located in included files. + true; + List -> + %% A codeOpt directive is placed inside the + %% currently compiled file. This dominates + %% all other directives. + CodeOption = best_positioned_codeOpt(List), + %% Remove code options that do not affect + %% the code production (redundant) + remove_redundant_codeOpt(S,[ScopedId|CodeOption]) + end. + + +%% Best positioned is the codeopt located +%% "highest" on the SAME file, the one with +%% lowest line number. +best_positioned_codeOpt([X|Xs]) -> + best_positioned_codeOpt(Xs,X). + +best_positioned_codeOpt([],Found) -> + Found; +best_positioned_codeOpt([X|Xs],Current) -> + case hd(tl(X)) > hd(tl(Current)) of + true -> + best_positioned_codeOpt(Xs,Current); + false -> + best_positioned_codeOpt(Xs,X) + end. + + +remove_redundant_codeOpt(S,[ScopedId,CodeOption,LNr,FilePath]) -> + ets:match_delete(S,{codeopt,ScopedId,'$1','$2','$3','$4'}), + ets:insert(S,{codeopt,ScopedId,CodeOption,LNr,last(FilePath),FilePath}). + + + + +add_inh_data(G,InclScope,X) -> + S = ic_genobj:pragmatab(G), + case X#interface.inherit of + [] -> + true; + [InhBody] -> + Scope = [get_id2(X)|InclScope], + insert(S,{inherits,Scope,InhBody}); + InhList -> + add_inh_data(G, S, InclScope, X, InhList) + end. + +add_inh_data(_,_,_,_,[]) -> + true; +add_inh_data(G, S, InclScope, X, [InhBody|InhBodies]) -> + Scope = [get_id2(X)|InclScope], + insert(S, {inherits,Scope,InhBody}), + add_inh_data(G, S, InclScope, X, InhBodies). + + +%% Returns a default broker data +defaultBrokerData(G) -> + {to_atom(ic_genobj:impl(G)),transparent}. + + +%% Loops through the form and sdds inheritence data +preproc(G, N, [X|Xs]) when is_record(X, interface) -> + %% Add inheritence data to pragmatab + ic_pragma:add_inh_data(G,N,X), + N2 = [get_id2(X) | N], + preproc(G, N2, get_body(X)), + lists:foreach(fun({_Name, Body}) -> preproc(G, N2, Body) end, + X#interface.inherit_body), + preproc(G, N, Xs); + +preproc(G,N,[X|Xs]) when is_record(X, module) -> + N2 = [get_id2(X) | N], + preproc(G, N2, get_body(X)), + preproc(G,N,Xs); + +preproc(G,N,[_X|Xs]) -> + preproc(G,N,Xs); + +preproc(_G, _N, []) -> + ok. + + +%% Returns a tuple / list of tuples { Mod, Type } +%% Does not check overridence because it is the +%% top scope for the module to be produced and +%% cannot be overriden. +getBrokerData(G,X,Scope) -> + S = ic_genobj:pragmatab(G), + cleanup_codeOptions(G,S,Scope), + + %% Check if it is an operation denoted + case isOperation(S,Scope) of + %% Yes, check options + true -> + %% Look if there is a specific code option on top file + case hasSpecificCodeoptionOnTopFile(S,ic_genobj:idlfile(G),Scope) of + true -> + %% Yes, let it work + getBrokerData(G,S,X,Scope,[Scope],[]); + false -> + %% No, try to see if there is codeoption on top file + case hasNonSpecificCodeoptionOnTopFile(S,ic_genobj:idlfile(G)) of + true -> + %% Yes, override every other specific code option + [_H|T] = Scope, + getBrokerData(G,S,X,Scope,[T],[]); + false -> + %% No, let inherited specific code options work + getBrokerData(G,S,X,Scope,[Scope],[]) + end + end; + %% No, continue + false -> + getBrokerData(G,S,X,Scope,[Scope],[]) + end. + +%% Returns a tuple / list of tuples { Mod, Type } +%% Inside loop, uses overridence. +getBrokerData(G,X,RS,Scope,CSF) -> + S = ic_genobj:pragmatab(G), + cleanup_codeOptions(G,S,Scope), + OvScope = overridedFrom(S,RS,Scope), + getBrokerData(G,S,X,RS,[OvScope],[OvScope|CSF]). + + + +getBrokerData(G,S,X,RS,[[[First]|Rest]],CSF) when is_integer(First) -> + Scope = [[First]|Rest], + case ets:match(S,{codeopt,Scope,'$1','_','_','_'}) of + [] -> + case ets:match(S,{inherits,Scope,'$1'}) of + [] -> %% No inheritence, no pragma codeopt + defaultBrokerData(G); %% Default + [InhScope] -> + getBrokerData(G,S,X,RS,InhScope,CSF); + InhList -> + getBrokerDataInh(G,S,X,RS,Scope,CSF,InhList) + end; + [[{broker,{Module,Type}}]] -> %% A branch only, with pragma codeopt + {Module,Type}; + List -> %% Multiple branches with pragma codeopt + flatten(List) + end; + +getBrokerData(G,S,X,RS,[[[First]|Rest]],CSF) -> + getBrokerDataLoop(G,S,X,RS,[[First]|Rest],CSF); + +getBrokerData(G,S,X,RS,[Scope],CSF) -> + %io:format(" 1"), + case ets:match(S,{codeopt,Scope,'$1','_','_','_'}) of + [] -> + %io:format(" 2"), + case ets:match(S,{inherits,Scope,'$1'}) of + [] -> %% No inheritence, no pragma codeopt + %io:format(" 5"), + defaultBrokerData(G); %% Default + [InhScope] -> + %io:format(" 6"), + getBrokerData(G,S,X,RS,InhScope,CSF); + InhList -> + %io:format(" 7"), + getBrokerDataInh(G,S,X,RS,Scope,CSF,InhList) + end; + [[{broker,{Module,Type}}]] -> %% A branch only, with pragma codeopt + %io:format(" 3"), + {Module,Type}; + List -> %% Multiple branches with pragma codeopt + %io:format(" 4"), + flatten(List) + end. + + +%% Special treatment when X is an operation +getBrokerDataInh(G,S,X,RS,Scope,CSF,InhList) when is_record(X,op)-> + %io:format(" 8"), + case ets:match(S,{op,get_id2(X),'$1','_','_'}) of + [] -> + %io:format(" 10"), + CleanList = remove_inherited(S,InhList), + getBrokerDataLoop(G,S,X,RS,CleanList,CSF); + + [[Scope]] -> + %io:format(" 11"), + CleanList = remove_inherited(S,InhList), + getBrokerDataLoop(G,S,X,RS,CleanList,CSF); + + [[OpScope]] -> + %io:format(" 12"), + case member([OpScope],InhList) of + true -> + %io:format(" 14"), + %% No inherited scopes + getBrokerData(G,X,RS,OpScope,CSF); + false -> + %io:format(" 15"), + %% Inherited scopes + CleanList = remove_inherited(S,InhList), + getBrokerDataLoop(G,S,X,RS,CleanList,CSF) + end; + + ListOfOpScopes -> + %io:format(" 13"), + case get_inherited(S,Scope,ListOfOpScopes) of + [[OpScope]] -> + case member([OpScope],InhList) of + true -> + getBrokerData(G,X,RS,OpScope,CSF); + false -> + CleanList = remove_inherited(S,InhList), + getBrokerDataLoop(G,S,X,RS,CleanList,CSF) + end; + _ -> + CleanList = remove_inherited(S,InhList), + getBrokerDataLoop(G,S,X,RS,CleanList,CSF) + end + end; +%% Just add InhList after removing all inherited +getBrokerDataInh(G,S,X,RS,_Scope,CSF,InhList) -> + %io:format(" 9"), + CleanList = remove_inherited(S,InhList), + getBrokerDataLoop(G,S,X,RS,CleanList,CSF). + + + + +%% Loops over a list of scopes +getBrokerDataLoop(G,S,X,RS,List,CSF) -> + getBrokerDataLoop(G,S,X,RS,List,[],CSF). + +getBrokerDataLoop(G,_,_X,_RS,[],BrokerDataList,_CSF) -> + case no_doubles(BrokerDataList) of + [BrokerData] -> %% No pragma codeopt / Multiple branches with pragma codeopt + BrokerData; + List -> + DefaultBD = defaultBrokerData(G), + case member(DefaultBD,List) of + true -> + %% Remove default, choose codeoption + NewList = delete(DefaultBD,List), + case NewList of + [BData] -> %% A branch only, with pragma codeopt + BData; + _Other -> %% Multiple branches with pragma codeopt + %%io:format("Multiple branches ~p~n",[Other]), + NewList + end; + false -> %% Multiple branches with pragma codeopt + flatten(List) + end + end; + +getBrokerDataLoop(G,S,X,RS,[[Scope]|Scopes],_Found,CSF) when is_integer(Scope) -> + getBrokerData(G,S,X,RS,[[Scope]|Scopes],CSF); + +getBrokerDataLoop(G,S,X,RS,[[Scope]|Scopes],Found,CSF) -> + %% Start from the beginning, check for overridings + case member(overridedFrom(S,RS,Scope),CSF) of %% Avoid infinite loops + true -> + getBrokerDataLoop(G,S,X,RS,Scopes,Found,CSF); + false -> + BrokerData = getBrokerData(G,X,RS,Scope,CSF), + getBrokerDataLoop(G,S,X,RS,Scopes,[BrokerData|Found],[Scope|CSF]) + end. + + + + +%%%-------------------------------------- +%%% Finds out the overrider of a scope +%%%-------------------------------------- +overridedFrom(S,RS,Scope) -> + overridedFrom(S,RS,Scope,Scope). + +overridedFrom(S,RS,Last,Scope) -> + case ets:match(S,{inherits,'$0',Scope}) of + [] -> + %% No inheritence, no pragma codeopt, + %% choose the last scope. + Last; + + [[RS]] -> + %% Garbage, unused interface with pragma + %% code option ! Danger ! + Last; + + [[InhScope]] -> + case ets:match(S,{codeopt,InhScope,'$1','_','_','_'}) of + [] -> + %% InhScope has no code options, keep Last. + overridedFrom(S,RS,Scope,InhScope); + _ -> + %% InhScope has code option, Last = InhScope. + overridedFrom(S,RS,InhScope,InhScope) + end; + List -> + %% Several inherit from Scope, choose the one feeseble, + %% the one DIRECTLY inherited by Scope and not through + %% other interface. + case remove_inheriters(S,RS,List) of + [] -> + Scope; + Removed -> + Removed + end + end. + +%%%------------------------------------------------------ +%%% Removes all the scopes that inherit from others +%%%------------------------------------------------------ +remove_inheriters(S,RS,InheriterList) -> + DominantList = + dominantList(S,InheriterList), + ReducedInhList = + [X || X <- InheriterList, + member(X,DominantList)], + + case ReducedInhList of + [] -> + []; + [_OneOnly] -> + ReducedInhList; + _Other -> + EtsList = ets:tab2list(S), + CleanList = + [X || X <- EtsList, element(1,X) == inherits], +% CodeOptList = +% [X || X <- EtsList, element(1,X) == codeopt], + NoInheriters =remove_inheriters2(S,ReducedInhList,CleanList), + + [ [X] || [X] <- NoInheriters, + inherits(RS,X,CleanList)] + end. + +remove_inheriters2(_,[A],_) -> + [A]; +remove_inheriters2(_S,[A,B],EtsList) -> + case remove_inh(A,B,[A,B],EtsList) of + [[X]] -> + X; + List -> + List + end; +remove_inheriters2(S,[A,B|Rest],EtsList) -> + case remove_inh(A,B,[A,B|Rest],EtsList) of + [A,B|Rest] -> + [A,B|Rest]; + NewList -> + remove_inheriters2(S,NewList,EtsList) + end. + +remove_inh([X],[Y],List,EtsList) -> + case inherits(X,Y,EtsList) of + true -> + delete([X],List); + false -> + case inherits(Y,X,EtsList) of + true -> + delete([Y],List); + false -> + List + end + end. + + + +%%%---------------------------------------------- +%%% Should remove all scope links that inherit +%%% from others in the list +%%%---------------------------------------------- +remove_inherited(S,InheriterList) -> + EtsList = ets:tab2list(S), + CleanList = + [X || X <- EtsList, element(1,X) == inherits], + remove_inherited(S,InheriterList,CleanList). + + +remove_inherited(_S,[A,B],EtsList) -> + case remove_inhed(A,B,[A,B],EtsList) of + [[X]] -> + [[X]]; + List -> + List + end; +remove_inherited(S,[A,B|Rest],EtsList) -> + case remove_inhed(A,B,[A,B|Rest],EtsList) of + [A,B|Rest] -> + [A,B|Rest]; + NewList -> + remove_inherited(S,NewList,EtsList) + end. + + +remove_inhed([X],[Y],List,EtsList) -> + case inherits(X,Y,EtsList) of + true -> + delete([Y],List); + false -> + case inherits(Y,X,EtsList) of + true -> + delete([X],List); + false -> + List + end + end. + + + + + + + +%%%---------------------------------------------- +%%% Should return all scope links that is +%% are inherited from scope in the list +%%%---------------------------------------------- +get_inherited(S,Scope,OpScopeList) -> + EtsList = ets:tab2list(S), + [[element(3,X)] || X <- EtsList, + element(1,X) == inherits, + element(2,X) == Scope, + member([element(3,X)],OpScopeList)]. + + + + + + + +%%%--------------------------------------------------- +%%% Returns a the list of scopes that have codeoption +%%% from a list of scopes +%%%--------------------------------------------------- +dominantList(S,IL) -> + dominantList(S,IL,[]). + +dominantList(_S,[],Found) -> + Found; +dominantList(S,[[X]|Xs],Found) -> + case ets:match(S,{codeopt,X,'$1','_','_','_'}) of + [] -> + dominantList(S,Xs,Found); + _ -> + dominantList(S,Xs,[[X]|Found]) + end. + + + + +%%%--------------------------------------------------- +%%% Returns true if X direct or indirect inherits Y +%%%--------------------------------------------------- +inherits(X,Y,EtsList) -> + case member({inherits,X,Y},EtsList) of + true -> + %% Direct inherited + true; + false -> + %% Indirectly inherited + AllInh = [ B || {inherits,A,B} <- EtsList, A == X ], + inherits(X,Y,AllInh,EtsList) + end. + +inherits(_X,_Y,[],_EtsList) -> + false; +inherits(X,Y,[Z|Zs],EtsList) -> + case inherits2(X,Y,Z,EtsList) of + true -> + true; + false -> + inherits(X,Y,Zs,EtsList) + end. + +inherits2(_X,Y,Z,EtsList) -> + case member({inherits,Z,Y},EtsList) of + true -> + true; + false -> + inherits(Z,Y,EtsList) + end. + + + +%% +%% is_inherited_by/3 +%% +%% Returns : +%% +%% true if the first parameter is +%% inherited by the second one +%% +%% false otherwise +%% +is_inherited_by(Interface1,Interface2,PragmaTab) -> + FullList = ets:tab2list(PragmaTab), + InheritsList = + [X || X <- FullList, element(1,X) == inherits], + inherits(Interface2,Interface1,InheritsList). + + + + +%% Filters all pragma prefix from list not in same file +%% the object + +filter_pragma_prefix_list(PragmaTab, Name, Scope, List) -> + IdlFile = scoped_names_idl_file(PragmaTab, Name, Scope), + filter_pragma_prefix_list2(PragmaTab,IdlFile,List,[]). + + +filter_pragma_prefix_list2(_,_,[],Found) -> + Found; +filter_pragma_prefix_list2(PT, IdlFile, [PP|PPs], Found) -> + case PP of + {prefix,_,_,_,IdlFile,_} -> %% Same file as the Object, keep + filter_pragma_prefix_list2(PT, IdlFile, PPs, [PP|Found]); + + _Other -> %% NOT in same file as the Object, throw away + filter_pragma_prefix_list2(PT, IdlFile, PPs, Found) + end. + +scoped_names_idl_file(PragmaTab, Name, Scope) -> + case ets:match(PragmaTab,{'_','$0','_','$2',Scope,Name,'_','_','_'}) of + [[IdlFile, _Type]] -> %% Usual case + IdlFile; + [[_File,module]|_Files] -> %% Multiple modules, get LOCAL file + case ets:match(PragmaTab,{file_data_local,'$0','_',module,Scope,Name,'_','_','_'}) of + [[LocalIdlFile]] -> + LocalIdlFile; + _ -> %% Should NEVER occur + error + end; + + _ -> + error %% Should NEVER occur + end. + + + + + + +%%------------------------------------------------- +%% +%% Register specific pragma code options +%% +%% If there is an operation with that +%% scope, denote this as {codeopt_specific,Scope} +%% +%%------------------------------------------------- +denote_specific_code_opts(G) -> + case ic_options:get_opt(G, be) of + noc -> + S = ic_genobj:pragmatab(G), + COList = ets:match(S,{codeopt,'$0','_','_','_','_'}), + OPList = ets:match(S,{op,'$0','$1','_','_'}), + denote_specific_code_opts(S,COList,OPList); + _ -> + ok + end. + +denote_specific_code_opts(_,_,[]) -> + ok; +denote_specific_code_opts(S,COList,[[OpN,OpS]|OPSs]) -> + case lists:member([[OpN|OpS]],COList) of + true -> + insert(S, {codeopt_specific,[OpN|OpS]}); + false -> + ok + end, + denote_specific_code_opts(S,COList,OPSs). + + + +%%--------------------------------------------- +%% +%% Returns true/false if it denotes an operation +%% +%%--------------------------------------------- +isOperation(_S,[]) -> + false; +isOperation(_S,[_]) -> + false; +isOperation(S,[H|T]) -> + case ets:match(S,{op,H,T,'$2','$3'}) of + [] -> + false; + _ -> + true + end. + + +hasSpecificCodeoptionOnTopFile(S,File,Scope) -> + case ets:match(S,{codeopt,Scope,'_','$2',File,[File]}) of + [] -> + false; + _ -> + true + end. + + +hasNonSpecificCodeoptionOnTopFile(S,File) -> + case ets:match(S,{codeopt,'_','_','$2',File,[File]}) of + [] -> + false; + _ -> + true + end. + + + +%%--------------------------------------------- +%% +%% Returns {ok,IfrId}/error when searching a random local type +%% +%%--------------------------------------------- + + +fetchRandomLocalType(G) -> + + S = ic_genobj:pragmatab(G), + + case ets:match(S,{file_data_local,'_','_','$2','$3','$4','_','_','_'}) of + [] -> + false; + + List -> + fetchRandomLocalType(S,List) + end. + + +fetchRandomLocalType(_,[]) -> + false; +fetchRandomLocalType(S,[[module|_]|Tail]) -> + fetchRandomLocalType(S,Tail); +fetchRandomLocalType(S,[[_,Scope,Name]|Tail]) -> + case ets:match(S,{alias,[Name|Scope],'$1'}) of + [] -> + fetchRandomLocalType(S,Tail); + [[IfrId]] -> + {ok,IfrId} + end. + + + +%%--------------------------------------------- +%% +%% Returns A list of local operation mapping +%% for a given scope +%% +%%--------------------------------------------- + + +fetchLocalOperationNames(G,I) -> + S = ic_genobj:pragmatab(G), + case ets:match(S,{file_data_local,'_','_',op,I,'$4','_','_','_'}) of + [] -> + []; + List -> + fetchLocalOperationNames2(List,[]) + end. + +fetchLocalOperationNames2([],Found) -> + lists:reverse(Found); +fetchLocalOperationNames2([[Name]|Names],Found) -> + fetchLocalOperationNames2(Names,[Name|Found]). + + + +%%------------------------------------------------ +%% +%% Returns a true if this scoped id is a local +%% one, false otherwise +%% +%%------------------------------------------------ +is_local(G,ScopedId) -> + S = ic_genobj:pragmatab(G), + Name = ic_util:to_undersc(ScopedId), + case ets:match(S,{file_data_local,'_','_','_',tl(ScopedId),'_',Name,'_','_'}) of + [[]] -> + true; + _ -> + false + end. diff --git a/lib/ic/src/ic_sequence_java.erl b/lib/ic/src/ic_sequence_java.erl new file mode 100644 index 0000000000..b57652fb82 --- /dev/null +++ b/lib/ic/src/ic_sequence_java.erl @@ -0,0 +1,239 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_sequence_java). + + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([gen/4]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: gen/4 +%%----------------------------------------------------------------- +gen(G, N, X, SequenceName) when is_record(X, sequence) -> + emit_holder_class(G, N, X, SequenceName), + emit_helper_class(G, N, X, SequenceName); +gen(_G, _N, _X, _SequenceName) -> + ok. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + + +%%----------------------------------------------------------------- +%% Func: emit_holder_class/4 +%%----------------------------------------------------------------- +emit_holder_class(G, N, X, SequenceName) -> + SName = string:concat(SequenceName, "Holder"), + {Fd, _}= ic_file:open_java_file(G, N, SName), + + SequenceType = ic_java_type:getType(G, N, X), + + ic_codegen:emit(Fd, ["final public class ",SequenceName,"Holder {\n" + " // instance variables\n" + " public ",SequenceType," value;\n\n" + " // constructors\n" + " public ",SequenceName,"Holder() {}\n" + " public ",SequenceName,"Holder(",SequenceType," initial) {\n" + " value = initial;\n" + " }\n\n" + + " // methods\n" + + " public void _marshal(",?ERLANGPACKAGE,"OtpOutputStream out) throws java.lang.Exception{\n" + " ",SequenceName,"Helper.marshal(out, value);\n" + " }\n\n" + + " public void _unmarshal(",?ERLANGPACKAGE,"OtpInputStream in) throws java.lang.Exception {\n" + " value = ",SequenceName,"Helper.unmarshal(in);\n" + " }\n\n" + "}\n"]), + file:close(Fd). + + + +emit_helper_class(G, N, X, SequenceName) -> + SName = string:concat(SequenceName, "Helper"), + {Fd, _}= ic_file:open_java_file(G, N, SName), + + SequenceType = ic_java_type:getType(G, N, X), + ElementType = ic_forms:get_type(X), + + ic_codegen:emit(Fd, ["public class ",SequenceName,"Helper {\n" + + " // constructors\n" + " private ",SequenceName,"Helper() {}\n\n" + + " // methods\n" + " public static void marshal(",?ERLANGPACKAGE,"OtpOutputStream _out, ",SequenceType," _value) \n" + " throws java.lang.Exception {\n\n"]), + + emit_sequence_marshal_function(G, N, X, Fd, SequenceName, ElementType), + + ic_codegen:emit(Fd, [" }\n\n" + + " public static ",SequenceType," unmarshal(",?ERLANGPACKAGE,"OtpInputStream _in) \n" + " throws java.lang.Exception {\n\n"]), + + emit_sequence_unmarshal_function(G, N, X, Fd, SequenceName, ElementType), + + ic_codegen:emit(Fd, [" }\n\n" + + " public static String id() {\n" + " return \"",ic_pragma:scope2id(G, [SequenceName | N]),"\";\n" + " }\n\n" + + " public static String name() {\n" + " return \"",SequenceName,"\";\n" + " }\n\n"]), + + ic_jbe:emit_type_function(G, N, X, Fd), + + ic_codegen:emit(Fd, [" public static void insert(",?ICPACKAGE,"Any _any, ",SequenceType," _this)\n" + " throws java.lang.Exception {\n\n" + + " ",?ERLANGPACKAGE,"OtpOutputStream _os = \n" + " new ",?ERLANGPACKAGE,"OtpOutputStream();\n\n" + + " _any.type(type());\n" + " marshal(_os, _this);\n" + " _any.insert_Streamable(_os);\n" + " }\n\n" + + " public static ",SequenceType," extract(",?ICPACKAGE,"Any _any)\n" + " throws java.lang.Exception {\n\n" + + " return unmarshal(_any.extract_Streamable());\n" + " }\n\n" + + + %% In corba mapping there is also a _type function here. + "}\n\n"]), + file:close(Fd). + + +%%----------------------------------------------------------------- +%% Func: emit_sequence_marshal_function/6 +%%----------------------------------------------------------------- +emit_sequence_marshal_function(G, N, X, Fd, _SequenceName, ElementType) -> + ic_codegen:emit(Fd, [" int _length = _value.length;\n\n" + + " _out.write_list_head(_length);\n\n" + + " if (_length > 0) {\n" + " for(int _tmp = 0; _tmp < _length; _tmp++)\n"]), + + case ic_java_type:isBasicType(G, N, ElementType) of + true -> + ic_codegen:emit(Fd, [" _out",ic_java_type:marshalFun(G, N, X, ElementType),"(_value[_tmp]);\n\n"]); + false -> + ic_codegen:emit(Fd, [" ",ic_java_type:marshalFun(G, N, X, ElementType),"(_out, _value[_tmp]);\n\n"]) + end, + + ic_codegen:emit(Fd, [" _out.write_nil();\n" + " }\n\n"]). + + + + +%%----------------------------------------------------------------- +%% Func: emit_sequence_unmarshal_function/6 +%%----------------------------------------------------------------- +emit_sequence_unmarshal_function(G, N, X, Fd, _SequenceName, ElementType) -> + + SequenceElementType = ic_java_type:getType(G, N, ElementType), + + ic_codegen:emit(Fd, [" int _tag,_length;\n" + " ",SequenceElementType," _sequence[];\n" + " _tag = _in.peek();\n\n"]), + + case ic_java_type:isIntegerType(G, N, ElementType) of + true -> + ic_codegen:emit(Fd, [" switch(_tag) {\n" + " case ",?ERLANGPACKAGE,"OtpExternal.stringTag:\n" + " byte _compressed[] = (_in.read_string()).getBytes();\n" + " _length = _compressed.length;\n" + " _sequence = new ",ic_java_type:getFullType(G,N,X),";\n\n" + + " for(int _tmp = 0; _tmp < _length; _tmp++)\n" + " _sequence[_tmp] = (",ic_java_type:getType(G, N, ElementType),")(_compressed[_tmp] & 0xff);\n\n" + + " break;\n" + " default:\n" + " _length = _in.read_list_head();\n" + " _sequence = new ",ic_java_type:getFullType(G,N,X),";\n\n" + + " if(_length > 0) {\n" + " for(int _tmp = 0; _tmp < _length; _tmp++)\n" + " _sequence[_tmp] = _in",ic_java_type:unMarshalFun(G, N, X, ElementType),";\n\n" + + " _in.read_nil();\n" + " }\n" + " }\n"]); + false -> + ic_codegen:emit(Fd, [" _length = _in.read_list_head();\n" + " _sequence = new ",ic_java_type:getFullType(G,N,X),";\n\n" + + " if(_length > 0) {\n" + " for(int _tmp = 0; _tmp < _length; _tmp++)\n"]), + case ic_java_type:isBasicType(G, N, ElementType) of + true -> + ic_codegen:emit(Fd, [" _sequence[_tmp] = _in",ic_java_type:unMarshalFun(G, N, X, ElementType),";\n\n"]); + _ -> + ic_codegen:emit(Fd, [" _sequence[_tmp] = ",ic_java_type:getUnmarshalType(G, N, X, ElementType),".unmarshal(_in);\n\n"]) + end, + + ic_codegen:emit(Fd, [" _in.read_nil();\n" + " }\n\n"]) + end, + + ic_codegen:emit(Fd, " return _sequence;\n"). + + + + +%%--------------------------------------------------- +%% Utilities +%%--------------------------------------------------- + + + + + + + + + diff --git a/lib/ic/src/ic_struct_java.erl b/lib/ic/src/ic_struct_java.erl new file mode 100644 index 0000000000..e577fd64a3 --- /dev/null +++ b/lib/ic/src/ic_struct_java.erl @@ -0,0 +1,314 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_struct_java). + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([gen/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- +gen(G, N, X) when is_record(X, struct) -> + StructName = ic_forms:get_java_id(X), + WireStructName = ic_forms:get_id2(X), + emit_struct_class(G, N, X, StructName), + emit_holder_class(G, N, X, StructName), + emit_helper_class(G, N, X, StructName, WireStructName), + N2 = [StructName ++ "Package" |N], + ic_jbe:gen(G, N2, ic_forms:get_body(X)); +gen(_G, _N, _X) -> + ok. + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: emit_struct_class/4 +%%----------------------------------------------------------------- +emit_struct_class(G, N, X, StructName) -> + {Fd, _}= ic_file:open_java_file(G, N, StructName), + + MList = struct_member_list(G, N, X), + ArgList = gen_parameter_list(G, [ StructName ++ "Package" |N], X, MList), + + ic_codegen:emit(Fd, ["final public class ",StructName," {\n" + " // instance variables\n"]), + + emit_struct_members_declarations(G, [StructName ++ "Package" |N], + X, Fd, MList), + + ic_codegen:emit(Fd, ["\n // constructors\n" + " public ",StructName,"() {}\n\n" + + " public ",StructName,"(",ArgList,") {\n"]), + + emit_struct_members_initialisation(G, N, X, Fd, MList), + + ic_codegen:emit(Fd, [" }\n\n" + + "}\n\n"]), + file:close(Fd). + + +%%----------------------------------------------------------------- +%% Func: emit_holder_class/4 +%%----------------------------------------------------------------- +emit_holder_class(G, N, _X, StructName) -> + SName = string:concat(StructName, "Holder"), + {Fd, _}= ic_file:open_java_file(G, N, SName), + + ic_codegen:emit(Fd, ["final public class ",StructName,"Holder {\n" + + " // instance variables\n" + " public ",StructName," value;\n\n" + + " // constructors\n" + " public ",StructName,"Holder() {}\n\n" + + " public ",StructName,"Holder(",StructName," initial) {\n" + " value = initial;\n" + " }\n\n" + + " // methods\n"]), + + ic_codegen:emit(Fd, [" public void _marshal(",?ERLANGPACKAGE,"OtpOutputStream out) throws java.lang.Exception {\n" + " ",StructName,"Helper.marshal(out, value);\n" + " }\n\n" + + " public void _unmarshal(",?ERLANGPACKAGE,"OtpInputStream in) throws java.lang.Exception {\n" + " value = ",StructName,"Helper.unmarshal(in);\n" + " }\n" + + "}\n\n"]), + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_helper_class/5 +%%----------------------------------------------------------------- +emit_helper_class(G, N, X, StructName, WireStructName) -> + SName = string:concat(StructName, "Helper"), + {Fd, _}= ic_file:open_java_file(G, N, SName), + + ic_codegen:emit(Fd, ["public class ",StructName,"Helper {\n" + + " // constructors\n" + " private ",StructName,"Helper() {}\n\n" + + " // methods\n"]), + + MList = struct_member_list(G, N, X), + + ic_codegen:emit(Fd, [" public static void marshal(",?ERLANGPACKAGE,"OtpOutputStream _out, ",StructName," _value)\n" + " throws java.lang.Exception {\n\n"]), + + emit_struct_marshal_function(G, N, X, Fd, StructName, WireStructName, MList), + + ic_codegen:emit(Fd, [" }\n\n" + + " public static ",StructName," unmarshal(",?ERLANGPACKAGE,"OtpInputStream _in)\n" + " throws java.lang.Exception {\n\n"]), + + emit_struct_unmarshal_function(G, N, X, Fd, StructName, WireStructName, MList), + + ic_codegen:emit(Fd, [" }\n\n" + + " public static String id() {\n" + " return \"",ictk:get_IR_ID(G, N, X),"\";\n" + " }\n\n" + + " public static String name() {\n" + " return \"",StructName,"\";\n" + " }\n\n"]), + + ic_jbe:emit_type_function(G, N, X, Fd), + + ic_codegen:emit(Fd, [" public static void insert(",?ICPACKAGE,"Any _any, ",StructName," _this)\n" + " throws java.lang.Exception {\n\n" + + " ",?ERLANGPACKAGE,"OtpOutputStream _os = \n" + " new ",?ERLANGPACKAGE,"OtpOutputStream();\n\n" + + " _any.type(type());\n" + " marshal(_os, _this);\n" + " _any.insert_Streamable(_os);\n" + " }\n\n" + + " public static ",StructName," extract(",?ICPACKAGE,"Any _any)\n" + " throws java.lang.Exception {\n\n" + + " return unmarshal(_any.extract_Streamable());\n" + " }\n\n" + + + %% In corba mapping there is also a _type function here. + "}\n"]), + file:close(Fd). + + +%%----------------------------------------------------------------- +%% Func: emit_struct_members_declarations/ +%%----------------------------------------------------------------- +emit_struct_members_declarations(_, _, _, _, []) -> + ok; +emit_struct_members_declarations(G, N, X, Fd, [{Member, _Type, Id} | MList]) -> + ic_codegen:emit(Fd, [" public ",ic_java_type:getType(G, N, Member)," ",Id,";\n"]), + emit_struct_members_declarations(G, N, X, Fd, MList). + + + +%%----------------------------------------------------------------- +%% Func: emit_struct_members_initialisation/5 +%%----------------------------------------------------------------- +emit_struct_members_initialisation(_, _, _, _, []) -> + ok; +emit_struct_members_initialisation(G, N, X, Fd, [{_Member, _Type, Id} | MList]) -> + ic_codegen:emit(Fd, [" ",Id," = _",Id,";\n"]), + emit_struct_members_initialisation(G, N, X, Fd, MList). + + + + +%%----------------------------------------------------------------- +%% Func: emit_struct_marshal_function/7 +%%----------------------------------------------------------------- +emit_struct_marshal_function(G, N, X, Fd, StructName, WireStructName, MList) -> + + ic_codegen:emit(Fd, [" _out.write_tuple_head(",integer_to_list(length(MList) + 1),");\n" + " _out.write_atom(\"",ic_util:to_undersc([WireStructName|N]),"\");\n\n"]), + + emit_struct_marshal_function_loop(G, [StructName ++ "Package" |N], + X, Fd, MList, 1). + +%%----------------------------------------------------------------- +%% Func: emit_struct_marshal_function_loop/6 +%%----------------------------------------------------------------- +emit_struct_marshal_function_loop(_, _, _, Fd, [], _) -> + ic_codegen:nl(Fd); +emit_struct_marshal_function_loop(G, N, X, Fd, [{Member, Type, Id} |MList], Num) -> + + case ic_java_type:isBasicType(G, N, Member) of + true -> + ic_codegen:emit(Fd, [" _out",ic_java_type:marshalFun(G, N, Member, Type),"(_value.",Id,");\n"]); + _ -> + if (element(1,hd(element(3,Member))) == array) -> + ic_codegen:emit(Fd, + [" ", + ic_util:to_dot(G,[ic_forms:get_id2(Member)|N]), + "Helper.marshal(_out, _value.",Id,");\n"]); + true -> + ic_codegen:emit(Fd, [" ", + ic_java_type:marshalFun(G, N, Member, Type), + "(_out, _value.",Id,");\n"]) + end + end, + + emit_struct_marshal_function_loop(G, N, X, Fd, MList, Num+1). + + + + +%%----------------------------------------------------------------- +%% Func: emit_struct_unmarshal_function/7 +%%----------------------------------------------------------------- +emit_struct_unmarshal_function(G, N, X, Fd, StructName, WireStructName, MList) -> + + ic_codegen:emit(Fd, [" _in.read_tuple_head();\n\n" + + " if ((_in.read_atom()).compareTo(\"", + ic_util:to_undersc([WireStructName|N]), + "\") != 0)\n" + " throw new java.lang.Exception(\"\");\n\n" + + " ",StructName," _value = new ",StructName,"();\n"]), + + emit_struct_unmarshal_function_loop(G, [StructName ++ "Package"|N], + X, Fd, MList, 1), + + ic_codegen:emit(Fd, " return _value;\n"). + +%%----------------------------------------------------------------- +%% Func: emit_union_unmarshal_function_loop/6 +%%----------------------------------------------------------------- +emit_struct_unmarshal_function_loop(_, _, _, Fd, [], _) -> + ic_codegen:nl(Fd); +emit_struct_unmarshal_function_loop(G, N, X, Fd, [{Member, Type, Id} |MList], Num) -> + + case ic_java_type:isBasicType(G, N, Member) of + true -> + ic_codegen:emit(Fd, [" _value.",Id," = _in",ic_java_type:unMarshalFun(G, N, Member, Type),";\n"]); + _ -> + if (element(1,hd(element(3,Member))) == array) -> + ic_codegen:emit(Fd, + [" _value.",Id," = ",ic_util:to_dot(G,[ic_forms:get_id2(Member)|N]),"Helper.unmarshal(_in);\n"]); + true -> + ic_codegen:emit(Fd, + [" _value.",Id," = ",ic_java_type:getUnmarshalType(G, N, Member, Type),".unmarshal(_in);\n"]) + end + end, + + emit_struct_unmarshal_function_loop(G, N, X, Fd, MList, Num +1). + + + +%%----------------------------------------------------------------- +%% Func: gen_parameter_list/4 +%%----------------------------------------------------------------- +gen_parameter_list(G, N, _X, [{Member, _Type, Id}]) -> + ic_java_type:getType(G,N,Member) ++ + " _" ++ + ic_util:to_list(Id); +gen_parameter_list(G, N, X, [{Member, _Type, Id} | MList]) -> + ic_java_type:getType(G,N,Member) ++ + " _" ++ + ic_util:to_list(Id) ++ + ", " ++ + gen_parameter_list(G, N, X, MList). + + +%%----------------------------------------------------------------- +%% Func: struct_member_list/3 +%%----------------------------------------------------------------- +struct_member_list(_G, _N, X) -> + M = lists:map( + fun(Member) -> + lists:map( + fun(Id) -> + Type = ic_forms:get_type(Member), + { Member, Type, ic_forms:get_java_id(Id)} + end, + ic_forms:get_idlist(Member)) + end, + ic_forms:get_body(X)), + lists:flatten(M). + + + diff --git a/lib/ic/src/ic_symtab.erl b/lib/ic/src/ic_symtab.erl new file mode 100644 index 0000000000..889c75e3a2 --- /dev/null +++ b/lib/ic/src/ic_symtab.erl @@ -0,0 +1,232 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_symtab). + + +-include_lib("ic/src/ic.hrl"). +-include_lib("ic/src/icforms.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([new/0, store/3, retrieve/2, soft_retrieve/2, intf_resolv/3]). +-export([get_full_scoped_name/3, scoped_id_new_global/1, scoped_id_new/1]). +-export([scoped_id_strip/1,symtab_add_faked_included_types/1]). +-export([scoped_id_is_global/1, scoped_id_add/2]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + + +%%-------------------------------------------------------------------- +%% +%% Symbol table routines +%% +%% Symbol tables handles mappings Id -> Value, where Id is an +%% ordinary Id from the parser (or a string) and value is an +%% arbitrary term. +%% +%%-------------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: new/0 (used to be symtab_new) +%%----------------------------------------------------------------- +new() -> + ets:new(symtab, [set, public]). + +%%----------------------------------------------------------------- +%% Func: store/3 (used to be symtab_store) +%%----------------------------------------------------------------- +store(G, N, X) -> + Name = [ic_forms:get_id2(X) | N], + %%io:format("Adding id: ~p~n", [N]), + case soft_retrieve(G, Name) of + {error, _} -> + ets:insert(G#genobj.symtab, {Name, X}); + {ok, Y} when is_record(Y, forward) -> + ets:insert(G#genobj.symtab, {Name, X}); + {ok, _Y} -> + ic_error:error(G, {multiply_defined, X}) + end. + + +%%----------------------------------------------------------------- +%% Func: retrieve/2 (used to be symtab_retrieve) +%% +%% Makes a lookup in the symbol table for Id. Will throw +%% not_found if it fails. +%%----------------------------------------------------------------- +retrieve(G, Id) -> + case ets:lookup(G#genobj.symtab, Id) of + [{_, Val}] -> Val; + [] -> ic_error:error(G, {symtab_not_found, Id}) + end. + + +%%----------------------------------------------------------------- +%% Func: soft_retrieve/2 (used to be symtab_soft_retrieve) +%% +%% Same as retrieve but will use tagged return values. +%% +%%----------------------------------------------------------------- +soft_retrieve(G, Id) -> + case ets:lookup(G#genobj.symtab, Id) of + [{_, Val}] -> {ok, Val}; + [] -> {error, {symtab_not_found, Id}} + end. + + +%%----------------------------------------------------------------- +%% Func: intf_resolv/3 and resolv2/3 +%% (used to be symtab_intf_resolv and symtab_intf_resolv2) +%% +%% Tries to resolv the interface identifier reference. The id can +%% be either a scoped name or an standard identifier. The +%% function returns a global reference to the id. +%% +%% Will throw not_found if the id really cannot be found. Will +%% throw illegal_forward if any forward references are founf in +%% the inheritance list. +%% +%%----------------------------------------------------------------- +intf_resolv(G, Scope, Id) -> + case scoped_id_is_global(Id) of + true -> + retrieve(G, Id), + Id; + false -> + intf_resolv2(G, Scope, Id) + end. + +intf_resolv2(G, Scope, Id) -> + N = scoped_id_add(Scope, Id), + case soft_retrieve(G, scoped_id_strip(N)) of + {ok, F} when is_record(F, forward) -> + ic_error:error(G, {illegal_forward, Id}), []; + {ok, _Val} -> + scoped_id_mk_global(N); + _ -> + case scoped_id_is_top(Scope) of + false -> + intf_resolv2(G, scoped_id_up_one(Scope), Id); + true -> + ic_error:error(G, {symtab_not_found, Id}), [] + end + end. + + + +%%-------------------------------------------------------------------- +%% +%% Scoped id routines +%% +%% A scoped id is an id written as M::Id in IDL. Scoped ids are +%% implemented as lists of id in reverse order, so M1::F1 becomes +%% [F1, M1]. +%% +%%-------------------------------------------------------------------- + +get_full_scoped_name(G, N, S) when element(1, S) == scoped_id -> + ictype:scoped_lookup(G, ic_genobj:tktab(G), N, S). + +scoped_id_new_global(Id) -> + X=scoped_id_new(Id), X#scoped_id{type=global}. + +scoped_id_new(Id) -> + #scoped_id{line=ic_forms:get_line(Id), id=[ic_forms:get_id(Id)]}. + +%% Adds one more id to the list of ids +scoped_id_add(S1, S2) when is_record(S2, scoped_id) -> + S1#scoped_id{id=S2#scoped_id.id ++ S1#scoped_id.id, + line=S2#scoped_id.line}; +scoped_id_add(S, Id) -> + S#scoped_id{id=[ic_forms:get_id(Id) | S#scoped_id.id], line=ic_forms:get_line(Id)}. + + +scoped_id_mk_global(S) -> S#scoped_id{type=global}. + +scoped_id_is_global(S) when is_record(S, scoped_id), S#scoped_id.type==global -> + true; +scoped_id_is_global(_) -> false. + +%% Top level scope (i.e no more cd ..) +scoped_id_is_top(S) when S#scoped_id.id==[] -> true; +scoped_id_is_top(_) -> false. + + +scoped_id_up_one(S) -> S#scoped_id{id=tl(S#scoped_id.id)}. % cd .. in scope +%%scoped_id_get_def(S) -> hd(S#scoped_id.id). % Last added id +scoped_id_strip(S) -> S#scoped_id.id. % Strips all junk + + + + +% Add CORBA::<Types> that as if they +% were defined in an included file. +% This is only supported in the case +% of Corba backend +symtab_add_faked_included_types(G) -> + case ic_options:get_opt(G, be) of + false -> + %% Add TypeCode as if it were defiend in included file + ets:insert(G#genobj.symtab, {["CORBA"], + {interface,{'<identifier>',0,"TypeCode"}, + [], + [], + [], + {tk_objref, + "IDL:omg.org/CORBA/TypeCode:1.0", + "TypeCode"}}}); + erl_corba -> + %% Add TypeCode as if it were defiend in included file + ets:insert(G#genobj.symtab, {["CORBA"], + {interface,{'<identifier>',0,"TypeCode"}, + [], + [], + [], + {tk_objref, + "IDL:omg.org/CORBA/TypeCode:1.0", + "TypeCode"}}}); + erl_template -> + %% Add TypeCode as if it were defiend in included file + ets:insert(G#genobj.symtab, {["CORBA"], + {interface,{'<identifier>',0,"TypeCode"}, + [], + [], + [], + {tk_objref, + "IDL:omg.org/CORBA/TypeCode:1.0", + "TypeCode"}}}); + _ -> + ok + end. + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- diff --git a/lib/ic/src/ic_union_java.erl b/lib/ic/src/ic_union_java.erl new file mode 100644 index 0000000000..4be93f3c1f --- /dev/null +++ b/lib/ic/src/ic_union_java.erl @@ -0,0 +1,754 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + + +-module(ic_union_java). + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([gen/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: gen/3 +%%----------------------------------------------------------------- +gen(G, N, X) when is_record(X, union) -> + + %% Create a TK value if not existed + %% Should be integrated in fetchTk + %% instead + NewX = case ic_forms:get_tk(X) of + undefined -> + S = ic_genobj:tktab(G), + Tk = ictype:tk(G, S, N, X), + #union{ id = X#union.id, + type = X#union.type, + body = X#union.body, + tk = Tk }; + _Tk -> + X + end, + + UnionName = ic_forms:get_java_id(NewX), + WiredUnionName = ic_forms:get_id2(NewX), + N2 = [UnionName ++ "Package"|N], + %%?PRINTDEBUG2("Recursive call over type ~p", + %% [[ic_forms:get_type(NewX)]]), + ic_jbe:gen(G, N, [ic_forms:get_type(NewX)]), + %%?PRINTDEBUG2("Recursive call over body: ~p", + %% [ic_forms:get_body(NewX)]), + ic_jbe:gen(G, N2, ic_forms:get_body(NewX)), + + emit_union_class(G, N, NewX, UnionName), + emit_holder_class(G, N, NewX, UnionName), + emit_helper_class(G, N, NewX, UnionName, WiredUnionName); +gen(_G, _N, _X) -> + ok. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: emit_union_class/4 +%%----------------------------------------------------------------- +emit_union_class(G, N, X, UnionName) -> + {Fd, _} = ic_file:open_java_file(G, N, UnionName), + + DiscrType = ic_java_type:getType(G, [UnionName ++ "Package"|N], + ic_forms:get_type(X)), + + MList = union_member_list(G, N, X, DiscrType), + + ic_codegen:emit(Fd, "final public class ~s {\n",[UnionName]), + + ic_codegen:emit(Fd, " // instance variables\n", []), + ic_codegen:emit(Fd, " private boolean _initialized;\n", []), + ic_codegen:emit(Fd, " private ~s _discriminator;\n", [DiscrType]), + ic_codegen:emit(Fd, " private java.lang.Object _value;\n", []), + + {tk_union,_, _,DiscrTk, _, _} = ic_forms:get_tk(X), + + DV = get_default_val(G, [UnionName |N], DiscrType, DiscrTk, MList), + + case DV of + none -> %% all values in case + ok; + _ -> + ic_codegen:emit(Fd, " private ~s _default = ~s;\n", + [DiscrType, DV]) + end, + + ic_codegen:nl(Fd), + ic_codegen:emit(Fd, " // constructors\n", []), + + ic_codegen:emit(Fd, " public ~s() {\n", [UnionName]), + ic_codegen:emit(Fd, " _initialized = false;\n", []), + ic_codegen:emit(Fd, " _value = null;\n", []), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, " // discriminator access\n", []), + + ic_codegen:emit(Fd, " public ~s discriminator() " + "throws java.lang.Exception {\n", [DiscrType]), + ic_codegen:emit(Fd, " if (!_initialized) {\n", []), + ic_codegen:emit(Fd, " throw new java.lang.Exception(\"\");\n",[]), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:emit(Fd, " return _discriminator;\n", []), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:nl(Fd), + + emit_union_members_functions(G, [UnionName ++ "Package"|N], X, + Fd, UnionName, DiscrType, MList, MList), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, "}\n", []), + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_holder_class/4 +%%----------------------------------------------------------------- +emit_holder_class(G, N, _X, UnionName) -> + UName = string:concat(UnionName, "Holder"), + {Fd, _} = ic_file:open_java_file(G, N, UName), + + ic_codegen:emit(Fd, "final public class ~sHolder {\n",[UnionName]), + + ic_codegen:emit(Fd, " // instance variables\n"), + ic_codegen:emit(Fd, " public ~s value;\n", [UnionName]), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, " // constructors\n"), + ic_codegen:emit(Fd, " public ~sHolder() {}\n", [UnionName]), + ic_codegen:emit(Fd, " public ~sHolder(~s initial) {\n", + [UnionName, UnionName]), + ic_codegen:emit(Fd, " value = initial;\n"), + ic_codegen:emit(Fd, " }\n"), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, " // methods\n"), + + ic_codegen:emit(Fd, " public void _marshal(~sOtpOutputStream out) throws java.lang.Exception {\n", + [?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " ~sHelper.marshal(out, value);\n", [UnionName]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public void _unmarshal(~sOtpInputStream in) throws java.lang.Exception {\n", + [?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " value = ~sHelper.unmarshal(in);\n", [UnionName]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, "}\n"), + file:close(Fd). + + +%%----------------------------------------------------------------- +%% Func: emit_helper_class/4 +%%----------------------------------------------------------------- +emit_helper_class(G, N, X, UnionName, WiredUnionName) -> + UName = string:concat(UnionName, "Helper"), + {Fd, _} = ic_file:open_java_file(G, N, UName), + + DiscrType = ic_java_type:getType(G, [ UnionName ++ "Package" |N], + ic_forms:get_type(X)), + + ic_codegen:emit(Fd, "public class ~sHelper {\n",[UnionName]), + + ic_codegen:emit(Fd, " // constructors\n", []), + ic_codegen:emit(Fd, " private ~sHelper() {}\n", [UnionName]), + ic_codegen:nl(Fd), + + ic_codegen:emit(Fd, " // methods\n", []), + MList = union_member_list(G, N, X, DiscrType), + + ic_codegen:emit(Fd, " public static void marshal(~sOtpOutputStream _out, ~s _value)\n", + [?ERLANGPACKAGE, UnionName]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + emit_union_marshal_function(G, N, X, Fd, UnionName, WiredUnionName, MList), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static ~s unmarshal(~sOtpInputStream _in)\n", + [UnionName, ?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + emit_union_unmarshal_function(G, N, X, Fd, UnionName, WiredUnionName, MList), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static String id() {\n"), + ic_codegen:emit(Fd, " return ~p;\n",[ictk:get_IR_ID(G, N, X)]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static String name() {\n"), + ic_codegen:emit(Fd, " return ~p;\n",[UnionName]), + ic_codegen:emit(Fd, " }\n\n"), + + ic_jbe:emit_type_function(G, N, X, Fd), + + + ic_codegen:emit(Fd, " public static void insert(~sAny _any, ~s _this)\n", + [?ICPACKAGE,UnionName]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " ~sOtpOutputStream _os = \n",[?ERLANGPACKAGE]), + ic_codegen:emit(Fd, " new ~sOtpOutputStream();\n\n",[?ERLANGPACKAGE]), + + ic_codegen:emit(Fd, " _any.type(type());\n"), + ic_codegen:emit(Fd, " marshal(_os, _this);\n"), + ic_codegen:emit(Fd, " _any.insert_Streamable(_os);\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static ~s extract(~sAny _any)\n", + [UnionName,?ICPACKAGE]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n\n"), + + ic_codegen:emit(Fd, " return unmarshal(_any.extract_Streamable());\n"), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " public static int discriminatorAsInt(~s _discriminator)\n", + [DiscrType]), + ic_codegen:emit(Fd, " throws java.lang.Exception {\n"), + emit_discriminator_as_int(G, N, ic_forms:get_type(X), Fd), + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, "}\n"), + file:close(Fd). + +%%----------------------------------------------------------------- +%% Func: emit_union_members_functions/7 +%%----------------------------------------------------------------- +emit_union_members_functions(_, _, _, _, _, _, [], _) -> + ok; +emit_union_members_functions(G, N, X, Fd, UnionName, DiscrType, + [{Label, Case, TypeDef, Id, Ls} | MList], MListTot) -> + + CaseId = Case#case_dcl.id, %% Maybe Array + CaseType = Case#case_dcl.type, %% Maybe Sequence + + Type = if element(1,CaseId) == array -> + ic_java_type:getType(G, N, TypeDef) ++ + ic_java_type:getdim(CaseId#array.size); + true -> + ic_java_type:getType(G, N, TypeDef) + end, + + HolderType = + if element(1,CaseId) == array -> + ic_java_type:getHolderType(G, N, CaseId); + true -> + if element(1,CaseType) == sequence -> + ic_util:to_dot(G,[Id|N]) ++"Holder"; + true -> + ic_java_type:getHolderType(G, N, TypeDef) + end + end, + + %% + %% Set method + %% + ic_codegen:emit(Fd, " // ~s access and set functions\n",[Id]), + ic_codegen:emit(Fd, " public void ~s(~s value) " + "throws java.lang.Exception {\n", + [Id, Type]), + ic_codegen:emit(Fd, " _initialized = true;\n", []), + case Label of + "default" -> + ic_codegen:emit(Fd, " _discriminator = (~s) _default;\n", + [DiscrType]); + _ -> + case ic_java_type:isBasicType(G, N, ic_forms:get_type(X)) of + true -> + ic_codegen:emit(Fd, " _discriminator = (~s) " + "~s;\n", + [DiscrType, Label]); + _ -> + ic_codegen:emit(Fd, " _discriminator = (~s) " + "~s.~s;\n", + [DiscrType, DiscrType, Label]) + end + end, + ic_codegen:emit(Fd, " _value = new ~s(value);\n", + [HolderType]), + ic_codegen:emit(Fd, " }\n", []), + + %% + %% Check this entry has more than one label and the generate an extra set method. + %% + case Ls of + [] -> + ok; + _ -> + ic_codegen:emit(Fd, " public void ~s(~s discriminator, ~s value) " + "throws java.lang.Exception {\n", + [Id, DiscrType, Type]), + ic_codegen:emit(Fd, " _initialized = true;\n", []), + ic_codegen:emit(Fd, " _discriminator = (~s) discriminator;\n", + [DiscrType]), + ic_codegen:emit(Fd, " _value = new ~s(value);\n", + [HolderType]), + ic_codegen:emit(Fd, " }\n", []) + end, + + %% + %% Get method + %% + ic_codegen:emit(Fd, " public ~s ~s() throws java.lang.Exception {\n", + [Type, Id]), + ic_codegen:emit(Fd, " if (!_initialized) {\n", []), + ic_codegen:emit(Fd, " throw new java.lang.Exception(\"\");\n",[]), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:emit(Fd, " switch (~sHelper.discriminatorAsInt" + "(discriminator())) {\n", + [UnionName]), + if + Label == "default" -> + ic_codegen:emit(Fd, " default:\n", []), + ic_codegen:emit(Fd, " break;\n", []), + emit_default_access_fun_switch_cases(G, N, X, Fd, DiscrType, + MListTot), + ic_codegen:emit(Fd, " throw new java.lang.Exception(\"\");\n", []); + true -> + ic_codegen:emit(Fd, " case ~s:\n", + [get_case_as_int(G, N, ic_forms:get_type(X), + DiscrType, Label)]), + ic_codegen:emit(Fd, " break;\n", []), + ic_codegen:emit(Fd, " default:\n", []), + ic_codegen:emit(Fd, " throw new java.lang.Exception(\"\");\n", []) + end, + ic_codegen:emit(Fd, " }\n", []), + + ic_codegen:emit(Fd, " return ((~s) _value).value;\n", + [HolderType]), + ic_codegen:emit(Fd, " }\n", []), + ic_codegen:nl(Fd), + emit_union_members_functions(G, N, X, Fd, UnionName, DiscrType, MList, + MListTot). + + +%%----------------------------------------------------------------- +%% Func: emit_default_access_fun_switch_cases/6 +%%----------------------------------------------------------------- +emit_default_access_fun_switch_cases(_G, _N, _X, _Fd, _DiscrType, []) -> + ok; +emit_default_access_fun_switch_cases(G, N, X, Fd, DiscrType, + [{"default", _, _, _, _} |MList]) -> + emit_default_access_fun_switch_cases(G, N, X, Fd, DiscrType, MList); +emit_default_access_fun_switch_cases(G, N, X, Fd, DiscrType, + [{Label, _Case, _TypeDef, _Id, _} | MList]) -> + ic_codegen:emit(Fd, " case ~s:\n", + [get_case_as_int(G, N, ic_forms:get_type(X), + DiscrType, Label)]), + emit_default_access_fun_switch_cases(G, N, X, Fd, DiscrType, MList). + + + +%%----------------------------------------------------------------- +%% Func: emit_union_unmarshal_function/5 +%%----------------------------------------------------------------- +emit_union_unmarshal_function(G, N, X, Fd, UnionName, WiredUnionName, MList) -> + DiscrTypeForm = ic_forms:get_type(X), + DiscrType = ic_java_type:getType(G, [UnionName ++ "Package"|N], + DiscrTypeForm), + + ic_codegen:emit(Fd, " _in.read_tuple_head();\n\n"), + + ic_codegen:emit(Fd, " if ((_in.read_atom()).compareTo(~p) != 0)\n", + [ic_util:to_undersc([WiredUnionName|N])]), + ic_codegen:emit(Fd, " throw new java.lang.Exception(\"\");\n\n",[]), + + ic_codegen:emit(Fd, " ~s _value = new ~s();\n", [UnionName, UnionName]), + + %% Decode discriminator + case ic_java_type:isBasicType(G, N, DiscrTypeForm) of + true -> + ic_codegen:emit(Fd, " ~s _discriminator = _in~s;\n\n", + [DiscrType, + ic_java_type:unMarshalFun(G, N, X, DiscrTypeForm)]); + _ -> + ic_codegen:emit(Fd, " ~s _discriminator = ~s.unmarshal(_in);\n\n", + [DiscrType,ic_java_type:getUnmarshalType(G, N, X, DiscrTypeForm)]) + end, + + ic_codegen:emit(Fd, " switch (~sHelper.discriminatorAsInt(_discriminator)) {\n", + [UnionName]), + + emit_union_unmarshal_function_loop(G, [UnionName ++ "Package"|N], X, + Fd, DiscrType, MList), + + ic_codegen:emit(Fd, " }\n\n"), + + ic_codegen:emit(Fd, " return _value;\n"). + +%%----------------------------------------------------------------- +%% Func: emit_union_unmarshal_function_loop/6 +%%----------------------------------------------------------------- +emit_union_unmarshal_function_loop(_, _, _, _, _, []) -> + ok; +emit_union_unmarshal_function_loop(G, N, X, Fd, DiscrType, + [{Label, Case, Type, Id, Ls} |MList]) -> + case Label of + "default" -> + ic_codegen:emit(Fd, " default:\n"); + _ -> + ic_codegen:emit(Fd, " case ~s:\n", + [get_case_as_int(G, N, ic_forms:get_type(X), + DiscrType, Label)]) + end, + + gen_multiple_cases(G, N, X, Fd, DiscrType, Ls), + + CaseId = Case#case_dcl.id, %% Maybe Array + CaseType = Case#case_dcl.type, %% Maybe Sequence + + case element(1,CaseId) of + array -> + ic_codegen:emit(Fd, " _value.~s(~s.unmarshal(_in));\n", + [Id, + ic_java_type:getUnmarshalType(G, N, Case, CaseId)]); + + _ -> + case element(1, CaseType) of + sequence -> + ic_codegen:emit(Fd, " _value.~s(~s.unmarshal(_in));\n", + [Id, + ic_java_type:getUnmarshalType(G, N, Case, CaseType)]); + _ -> + case ic_java_type:isBasicType(G, N, CaseType) of + true -> + ic_codegen:emit(Fd, " _value.~s(_in~s);\n", + [Id, + ic_java_type:unMarshalFun(G, N, X, Type)]); + false -> + ic_codegen:emit(Fd, " _value.~s(~s.unmarshal(_in));\n", + [Id, + ic_java_type:getUnmarshalType(G, N, X, Type)]) + end + end + end, + + ic_codegen:emit(Fd, " break;\n", []), + emit_union_unmarshal_function_loop(G, N, X, Fd, DiscrType, MList). + + + + + +%%----------------------------------------------------------------- +%% Func: emit_union_marshal_function/6 +%%----------------------------------------------------------------- +emit_union_marshal_function(G, N, X, Fd, UnionName, WiredUnionName, MList) -> + + DiscrTypeForm = ic_forms:get_type(X), + DiscrType = ic_java_type:getType(G, [UnionName ++ "Package" |N], + DiscrTypeForm), + + ic_codegen:emit(Fd, " _out.write_tuple_head(3);\n"), + ic_codegen:emit(Fd, " _out.write_atom(~p);\n", + [ic_util:to_undersc([WiredUnionName|N])]), + + case ic_java_type:isBasicType(G, N, DiscrTypeForm) of + true -> + ic_codegen:emit(Fd, " _out~s(_value.discriminator());\n\n", + [ic_java_type:marshalFun(G, N, X, DiscrTypeForm)]); + false -> + ic_codegen:emit(Fd, " ~s(_out, _value.discriminator());\n\n", + [ic_java_type:marshalFun(G, N, X, DiscrTypeForm)]) + end, + + ic_codegen:emit(Fd, " switch(~sHelper.discriminatorAsInt(_value.discriminator())) {\n", + [UnionName]), + + emit_union_marshal_function_loop(G, + [ UnionName ++ "Package"|N], + X, + Fd, + DiscrType, + MList), + + ic_codegen:emit(Fd, " }\n\n", []). + + +%%----------------------------------------------------------------- +%% Func: emit_union_marshal_function_loop/ +%%----------------------------------------------------------------- +emit_union_marshal_function_loop(_, _, _, _, _, []) -> + ok; +emit_union_marshal_function_loop(G, N, X, Fd, DiscrType, + [{Label, Case, Type, Id, Ls} |MList]) -> + case Label of + "default" -> + ic_codegen:emit(Fd, " default:\n", + []); + _ -> + ic_codegen:emit(Fd, " case ~s:\n", + [get_case_as_int(G, N, ic_forms:get_type(X), + DiscrType, Label)]) + end, + + gen_multiple_cases(G, N, X, Fd, DiscrType, Ls), + + + CaseId = Case#case_dcl.id, %% Maybe Array + CaseType = Case#case_dcl.type, %% Maybe Sequence + + case element(1,CaseId) of + array -> + ic_codegen:emit(Fd, " ~s(_out, _value.~s());\n", + [ic_java_type:marshalFun(G, N, Case, CaseId), + Id]); + _ -> + case element(1, CaseType) of + sequence -> + ic_codegen:emit(Fd, " ~s.marshal(_out, _value.~s());\n", + [ic_util:to_dot(G,[Id|N]) ++ "Helper", + Id]); + _ -> + case ic_java_type:isBasicType(G, N, CaseType) of + true -> + ic_codegen:emit(Fd, " _out~s(_value.~s());\n", + [ic_java_type:marshalFun(G, N, X, Type), + Id]); + false -> + ic_codegen:emit(Fd, " ~s(_out, _value.~s());\n", + [ic_java_type:marshalFun(G, N, X, Type), + Id]) + end + end + end, + + ic_codegen:emit(Fd, " break;\n", []), + emit_union_marshal_function_loop(G, N, X, Fd, DiscrType, MList). + + + +gen_multiple_cases(_G, _N, _X, _Fd, _DiscrType, []) -> + ok; +gen_multiple_cases(G, N, X, Fd, DiscrType, [Label |Ls]) -> + ic_codegen:emit(Fd, " case ~s:\n", + [get_case_as_int(G, N, ic_forms:get_type(X), + DiscrType, getLabel(DiscrType, Label))]), + gen_multiple_cases(G, N, X, Fd, DiscrType, Ls). + + +%%----------------------------------------------------------------- +%% Func: union_member_list/3 +%%----------------------------------------------------------------- +union_member_list(G, N, X, DiscrType) -> + M = lists:map( + fun(Case) -> + {Label, LabelList} = case check_default(ic_forms:get_idlist(Case)) of + {{default, C}, List} -> + {{default, C}, List}; + {L, []} -> + {L, []}; + {_, [L |Ls]} -> + {L, Ls} + end, + + CName = ic_forms:get_java_id(Case), + CId = Case#case_dcl.id, + CType = Case#case_dcl.type, + + if element(1,CId) == array -> + N2 = [ic_forms:get_id2(X) ++ "Package" |N], + ic_array_java:gen(G, N2, Case, CId); + true -> + if element(1,Case#case_dcl.type) == sequence -> + N2 = [ic_forms:get_id2(X) ++ "Package" |N], + ic_sequence_java:gen(G, N2, CType, CName); + true -> + ok + end + end, + + {getLabel(DiscrType, Label), + Case, + ic_forms:get_type(Case), + CName, + LabelList} + end, + ic_forms:get_body(X)), + lists:flatten(M). + +check_default([]) -> + {false, []}; +check_default([{default, X} |Ls]) -> + {{default, X}, Ls}; +check_default([L]) -> + {false, [L]}; +check_default([L |Ls]) -> + {X, Y} = check_default(Ls), + {X, [L | Y]}. + +getLabel(_, {'<integer_literal>', _, N}) -> + N; +getLabel(_, {'<character_literal>', _, N}) -> + "'" ++ N ++ "'"; +getLabel(_, {'<wcharacter_literal>', _, N}) -> + "'" ++ N ++ "'"; +getLabel(_, {'TRUE',_}) -> + "true"; +getLabel(_, {'FALSE',_}) -> + "true"; +getLabel(_, {default, _}) -> + "default"; +getLabel(_DiscrType, X) -> %%DiscrType ++ "." ++ + ic_util:to_dot(ic_forms:get_id(X)). + +get_default_val(G, N, _, tk_short, MList) -> + integer_default_val(G, N, 1, lists:map(fun({V, _, _, _, _}) -> V end, MList)); +get_default_val(G, N, _, tk_long, MList) -> + integer_default_val(G, N, 1, lists:map(fun({V, _, _, _, _}) -> V end, MList)); +get_default_val(G, N, _, tk_ushort, MList) -> + integer_default_val(G, N, 1, lists:map(fun({V, _, _, _, _}) -> V end, MList)); +get_default_val(G, N, _, tk_ulong, MList) -> + integer_default_val(G, N, 1, lists:map(fun({V, _, _, _, _}) -> V end, MList)); +get_default_val(G, N, _, tk_char, MList) -> + char_default_val(G, N, $a, lists:map(fun({V, _, _, _, _}) -> V end, MList)); +get_default_val(G, N, _, tk_boolean, MList) -> + boolean_default_val(G, N, lists:map(fun({V, _, _, _, _}) -> V end, MList)); +get_default_val(G, N, DiscrType, {tk_enum, _, _, Values}, MList) -> + enum_default_val(G, N, DiscrType, Values, MList). + +integer_default_val(G, N, Num, MList) -> + Num2 = integer_to_list(Num), + case lists:member(Num2, MList) of + true -> + integer_default_val(G, N, Num + 1, MList); + false -> + Num2 + end. + +char_default_val(G, N, CharNum, MList) -> + Str = "'", + CharNum2 = Str ++ [CharNum | Str], + case lists:member(CharNum2, MList) of + true -> + char_default_val(G, N, CharNum + 1, MList); + false -> + CharNum2 + end. + +boolean_default_val(G, N, MList) -> + if + length(MList) > 2 -> + ic_error:error(G, {plain_error_string, + lists:flatten( + io_lib:format("Default value found while all values have label on ~s", + [ic_util:to_colon(N)]))}), + none; + true -> + case MList of + ["true"] -> + "false"; + ["false"] -> + "true"; + ["default","true"] -> + "false"; + ["true","default"] -> + "false"; + ["default","false"] -> + "true"; + ["false","default"] -> + "true"; + _ -> + none + end + end. + + + + +enum_default_val(G, N, DiscrType, Values, Mlist) -> + + VLen = length(Values), + MLen = length(Mlist), + + case MLen > VLen of + true -> + ic_error:error(G, {plain_error_string, + lists:flatten( + io_lib:format("Default value found while all values have label on ~s", + [ic_util:to_colon(N)]))}), + none; + false -> + enum_default_val_loop(G, N, DiscrType, Values, Mlist) + end. + +enum_default_val_loop(_G, _N, _, [], []) -> + none; +enum_default_val_loop(_G, _N, DiscrType, [Value| _], []) -> + DiscrType ++ "." ++ Value; +enum_default_val_loop(G, N, DiscrType, Values, [Case | MList]) when is_tuple(Case) -> + NewValues = lists:delete(element(1,Case), Values), + enum_default_val_loop(G, N, DiscrType, NewValues, MList). + + + +emit_discriminator_as_int(G, N, T, Fd) -> + case ictype:isBoolean(G,N,T) of + true -> + ic_codegen:emit(Fd, " if(_discriminator)\n", []), + ic_codegen:emit(Fd, " return 1;\n", []), + ic_codegen:emit(Fd, " else\n", []), + ic_codegen:emit(Fd, " return 0;\n", []); + false -> + case ictype:isEnum(G, N, T) of + true -> + ic_codegen:emit(Fd, " return _discriminator.value();\n", + []); + false -> + ic_codegen:emit(Fd, " return _discriminator;\n", []) + end + end. + + +get_case_as_int(G, N, T, DiscrJavaTypeName, Label) -> + case ictype:isBoolean(G,N,T) of + true -> + case Label of + "true" -> + "1"; + "false" -> + "0" + end; + false -> + case ictype:isEnum(G, N, T) of + true -> + DiscrJavaTypeName ++ "._" ++ Label; + false -> + "(" ++ DiscrJavaTypeName ++ ") " ++ Label + end + end. + + + diff --git a/lib/ic/src/ic_util.erl b/lib/ic/src/ic_util.erl new file mode 100644 index 0000000000..1a6acb286a --- /dev/null +++ b/lib/ic/src/ic_util.erl @@ -0,0 +1,313 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ic_util). + + +-include("icforms.hrl"). +-include("ic.hrl"). +-include("ic_debug.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- + +-export([mk_align/1, mk_list/1, join/2, chain/2, mk_name/2, + mk_OE_name/2, mk_oe_name/2, mk_var/1]). + +-export([to_atom/1, to_colon/1, to_list/1, to_undersc/1, to_dot/1, + to_dot/2]). +-export([to_uppercase/1, adjustScopeToJava/2, eval_java/3, eval_c/3]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%% mk_list produces a nice comma separated string of variable names +mk_list([]) -> []; +mk_list([Arg | Args]) -> + Arg ++ mk_list2(Args). +mk_list2([Arg | Args]) -> + ", " ++ Arg ++ mk_list2(Args); +mk_list2([]) -> []. + +%% Produce a list of items separated by S. +join([E1, E2| Es], S) -> + [E1, S| join([E2| Es], S)]; +join([E], _) -> + [E]; +join([], _) -> + []. + +%% Produce a list of items, each terminated by T. +chain([E| Es], T) -> + [E, T| chain(Es, T)]; +chain([], _) -> + []. + + +%% Shall convert a string to a Erlang variable name (Capitalise) +mk_var( [N | Str] ) when N >= $a, N =< $z -> + [ N+$A-$a | Str ]; +mk_var( [N | Str] ) when N >= $A, N =< $Z -> [N | Str]. + +%% Shall produce a "public" name for name. When we introduce new +%% identifiers in the mapping that must not collide with those from +%% the IDL spec. +%% +%% NOTE: Change name of IFR ID in system exceptions in corba.hrl when +%% prefix is changed here. +%% +mk_name(_Gen, Name) -> lists:flatten(["OE_" | Name]). +mk_OE_name(_Gen, Name) -> lists:flatten(["OE_" | Name]). +mk_oe_name(_Gen, Name) -> lists:flatten(["oe_" | Name]). + +mk_align(String) -> + io_lib:format("OE_ALIGN(~s)",[String]). + +to_atom(A) when is_atom(A) -> A; +to_atom(L) when is_list(L) -> list_to_atom(L). + +to_list(A) when is_list(A) -> A; +to_list(L) when is_atom(L) -> atom_to_list(L); +to_list(X) when is_integer(X) -> integer_to_list(X). + + + +%% Produce a colon (or under score) separated string repr of the name +%% X +%% +to_colon(X) when element(1, X) == scoped_id -> + to_colon2(ic_symtab:scoped_id_strip(X)); +to_colon(L) -> to_colon2(L). + +to_colon2([X]) -> X; +to_colon2([X | Xs]) -> to_colon2(Xs) ++ "::" ++ X; +to_colon2([]) -> "". + + +to_undersc(X) when element(1, X) == scoped_id -> + to_undersc2(ic_symtab:scoped_id_strip(X)); +to_undersc(L) -> to_undersc2(L). + +to_undersc2([X]) -> X; +to_undersc2([X | Xs]) -> to_undersc2(Xs) ++ "_" ++ X; +to_undersc2([]) -> "". + + +%% Z is a single name +to_uppercase(Z) -> + lists:map(fun(X) when X>=$a, X=<$z -> X-$a+$A; + (X) -> X end, Z). + + +%% +to_dot(X) when element(1, X) == scoped_id -> + to_dotLoop(ic_symtab:scoped_id_strip(X)); +to_dot(L) -> to_dotLoop(L). + +to_dotLoop([X]) -> ic_forms:get_java_id(X); +to_dotLoop([X | Xs]) -> to_dotLoop(Xs) ++ "." ++ ic_forms:get_java_id(X); +to_dotLoop([]) -> "". + + + +%% +to_dot(G,X) when element(1, X) == scoped_id -> + S = ic_genobj:pragmatab(G), + ScopedId = ic_symtab:scoped_id_strip(X), + case isConstScopedId(S, ScopedId) of %% Costants are left as is + true -> + to_dotLoop(ScopedId) ++ addDotValue(S, ScopedId); + false -> + to_dotLoop(S,ScopedId) + end; +to_dot(G,ScopedId) -> + S = ic_genobj:pragmatab(G), + case isConstScopedId(S, ScopedId) of %% Costants are left as is + true -> + to_dotLoop(ScopedId) ++ addDotValue(S, ScopedId); + false -> + to_dotLoop(S,ScopedId) + end. + +addDotValue(S, [_C | Ss]) -> + case isInterfaceScopedId(S, Ss) of + true -> + ""; + false -> + ".value" + end. + +to_dotLoop(S,[X]) -> + case isInterfaceScopedId(S, [X]) of + true -> + ic_forms:get_java_id(X) ++ "Package"; + false -> + ic_forms:get_java_id(X) + end; +to_dotLoop(S,[X | Xs]) -> + case isInterfaceScopedId(S, [X | Xs]) of + true -> + to_dotLoop(S,Xs) ++ "." ++ ic_forms:get_java_id(X) ++ "Package"; + false -> + to_dotLoop(S,Xs) ++ "." ++ ic_forms:get_java_id(X) + end; +to_dotLoop(_S,[]) -> "". + +isInterfaceScopedId(_S,[]) -> + false; +isInterfaceScopedId(S,[X|Xs]) -> + case ets:match(S,{file_data_local,'_','_',interface,Xs,X,'_','_','_'}) of + [] -> + case ets:match(S,{file_data_included,'_','_',interface,Xs,X,'_','_','_'}) of + [] -> + false; + _ -> + true + end; + _ -> + true + end. + +isConstScopedId(_S,[]) -> + false; +isConstScopedId(S,[X|Xs]) -> + case ets:match(S,{file_data_local,'_','_',const,Xs,X,'_','_','_'}) of + [] -> + case ets:match(S,{file_data_included,'_','_',const,Xs,X,'_','_','_'}) of + [] -> + false; + _ -> + true + end; + _ -> + true + end. + + + +%% +adjustScopeToJava(G,X) when element(1, X) == scoped_id -> + S = ic_genobj:pragmatab(G), + ScopedId = ic_symtab:scoped_id_strip(X), + case isConstScopedId(S, ScopedId) of %% Costants are left as is + true -> + ic_forms:get_java_id(ScopedId); + false -> + adjustScopeToJavaLoop(S,ScopedId) + end; +adjustScopeToJava(G,ScopedId) -> + S = ic_genobj:pragmatab(G), + case isConstScopedId(S, ScopedId) of %% Costants are left as is + true -> + ic_forms:get_java_id(ScopedId); + false -> + adjustScopeToJavaLoop(S,ScopedId) + end. + + + +adjustScopeToJavaLoop(_S,[]) -> + []; +adjustScopeToJavaLoop(S,[X | Xs]) -> + case isInterfaceScopedId(S, [X | Xs]) of + true -> + [ic_forms:get_java_id(X) ++ "Package" | adjustScopeToJavaLoop(S,Xs)]; + false -> + [ic_forms:get_java_id(X) | adjustScopeToJavaLoop(S,Xs)] + end. + + +%% +%% Expression evaluator for java +%% +%% Well, this is not an evaluator, it just +%% prints the hole operation, sorry. +%% +eval_java(G,N,Arg) when is_record(Arg, scoped_id) -> + {FSN, _, _, _} = + ic_symtab:get_full_scoped_name(G, N, Arg), + ic_util:to_dot(G,FSN); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<integer_literal>' -> + element(3,Arg); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<character_literal>' -> + element(3,Arg); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<wcharacter_literal>' -> + element(3,Arg); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<boolean_literal>' -> + element(3,Arg); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<floating_pt_literal>' -> + element(3,Arg); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<string_literal>' -> + element(3,Arg); +eval_java(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<wstring_literal>' -> + element(3,Arg); +eval_java(G,N,{Op,Arg1,Arg2}) -> + "(" ++ eval_java(G,N,Arg1) ++ + ic_forms:get_java_id(Op) ++ + eval_java(G,N,Arg2) ++ ")". + + + +%% +%% Expression evaluator for c +%% +%% Well, this is not an evaluator, it just +%% prints the hole operation, sorry. +%% +eval_c(G,N,Arg) when is_record(Arg, scoped_id) -> + {FSN, _, _, _} = + ic_symtab:get_full_scoped_name(G, N, Arg), + ic_util:to_undersc(FSN); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<integer_literal>' -> + element(3,Arg); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<character_literal>' -> + element(3,Arg); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<wcharacter_literal>' -> + element(3,Arg); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<boolean_literal>' -> + element(3,Arg); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<floating_pt_literal>' -> + element(3,Arg); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<string_literal>' -> + element(3,Arg); +eval_c(_G,_N,Arg) when is_tuple(Arg) andalso element(1,Arg) == '<wstring_literal>' -> + element(3,Arg); +eval_c(G,N,{Op,Arg1,Arg2}) -> + "(" ++ eval_c(G,N,Arg1) ++ + atom_to_list(Op) ++ + eval_c(G,N,Arg2) ++ ")". + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + + + + + + diff --git a/lib/ic/src/icenum.erl b/lib/ic/src/icenum.erl new file mode 100644 index 0000000000..0af200e229 --- /dev/null +++ b/lib/ic/src/icenum.erl @@ -0,0 +1,205 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%----------------------------------------------------------------- +%% File: icenum.erl +%% +%% +%%----------------------------------------------------------------- +%% +%% Code generation for enum's. +%%----------------------------------------------------------------- +-module(icenum). + +-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"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([enum_gen/4]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +enum_gen(G, N, X, c) when is_record(X, enum) -> + emit_c_enum(G, N, X); +enum_gen(_G, _N, _X, _L) -> + ok. + + +emit_c_enum(G, N, X) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + EnumName = [ic_forms:get_id2(X) | N], + + case ic_pragma:is_local(G,EnumName) of + true -> + + Fd = ic_genobj:hrlfiled(G), + EnumNameStr = ic_util:to_undersc(EnumName), + ic_code:insert_typedef(G, EnumNameStr, {enum, EnumNameStr}), + {tk_enum,_,_,EList} = ic_forms:get_tk(X), + emit(Fd, "\n#ifndef __~s__\n",[ic_util:to_uppercase(EnumNameStr)]), + emit(Fd, "#define __~s__\n",[ic_util:to_uppercase(EnumNameStr)]), + ic_codegen:mcomment_light(Fd, + [io_lib:format("Enum definition: ~s", + [EnumNameStr])], + c), + emit(Fd, "typedef CORBA_enum {", []), + emit_c_enum_values(G, N, Fd, EList), + emit(Fd, "} ~s ;\n\n", [EnumNameStr]), + create_c_enum_file(G, N, EnumNameStr, EList), + emit(Fd, "\n#endif\n\n"); + + false -> %% Do not generate included types att all. + ok + end; + + false -> + ok + end. + + +emit_c_enum_values(_G, N, Fd, [E]) -> + emit(Fd, "~s", [ic_util:to_undersc([E| N])]); +emit_c_enum_values(G, N, Fd, [E |Es]) -> + emit(Fd, "~s, ", [ic_util:to_undersc([E| N])]), + emit_c_enum_values(G, N, Fd, Es). + + +open_c_coding_file(G, Name) -> + SName = string:concat(ic_util:mk_oe_name(G, "code_"), Name), + FName = + ic_file:join(ic_options:get_opt(G, stubdir),ic_file:add_dot_c(SName)), + case file:open(FName, [write]) of + {ok, Fd} -> + {Fd, SName}; + Other -> + exit(Other) + end. + + +create_c_enum_file(G, N, Name, Elist) -> + + {Fd , SName} = open_c_coding_file(G, Name), + HFd = ic_genobj:hrlfiled(G), %% Write on stubfile header + HrlFName = filename:basename(ic_genobj:include_file(G)), + ic_codegen:emit_stub_head(G, Fd, SName, c), + emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% Fd = ic_genobj:stubfiled(G), %% Write on stubfile + %% HFd = ic_genobj:hrlfiled(G), %% Write on stubfile header + %% HrlFName = filename:basename(ic_genobj:include_file(G)), + %% emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + emit(Fd, "char* ~s[~p] = {\n", [ic_util:mk_oe_name(G, Name), + length(Elist)]), + emit_c_enum_array_values(Fd, Elist), + emit(Fd, "};\n\n",[]), + emit_sizecount(G, N, Fd, HFd, Name, Elist), + emit_encode(G, N, Fd, HFd, Name, Elist), + emit_decode(G, N, Fd, HFd, Name, Elist), + file:close(Fd). + +emit_c_enum_array_values(Fd, [E]) -> + emit(Fd, " ~p\n", [E]); +emit_c_enum_array_values(Fd, [E |Es]) -> + emit(Fd, " ~p,\n", [E]), + emit_c_enum_array_values(Fd, Es). + + +emit_sizecount(G, _N, Fd, HFd, Name, _Elist) -> + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, int*, int*);\n", + [ic_util:mk_oe_name(G, "sizecalc_"), Name]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, int* oe_size_count_index, int* oe_size)\n" + "{\n", + [ic_util:mk_oe_name(G, "sizecalc_"), Name]), + emit(Fd, " int oe_error_code = 0;\n\n",[]), + + AlignName = lists:concat(["*oe_size + sizeof(",Name,")"]), + emit(Fd, " *oe_size = ~s;\n\n",[ic_util:mk_align(AlignName)]), + + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n\n",[]). + + +emit_encode(G, _N, Fd, HFd, Name, _Elist) -> + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, ~s);\n", + [ic_util:mk_oe_name(G, "encode_"), Name, Name]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, ~s oe_rec) {\n", + [ic_util:mk_oe_name(G, "encode_"), Name, Name]), + emit(Fd, " int oe_error_code = 0;\n\n",[]), + + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, ~s[oe_rec])) < 0) {\n", + [ic_util:mk_oe_name(G, Name)]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n\n",[]). + +emit_decode(G, _N, Fd, HFd, Name, Elist) -> + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, char *, int*, ~s *);\n", + [ic_util:mk_oe_name(G, "decode_"), Name, Name]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, char *oe_first, int* oe_outindex, " + "~s *oe_out) {\n\n", + [ic_util:mk_oe_name(G, "decode_"), Name, Name]), + emit(Fd, " int oe_error_code = 0;\n",[]), + emit(Fd, " int oe_i;\n",[]), + emit(Fd, " char oe_atom[256];\n\n",[]), + + AlignName = lists:concat(["*oe_outindex + sizeof(",Name,")"]), + emit(Fd, " *oe_outindex = ~s;\n\n",[ic_util:mk_align(AlignName)]), + + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_atom)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + + Len = length(Elist), + emit(Fd, " for(oe_i = 0; oe_i < ~p && strcmp(oe_atom, ~s[oe_i]); oe_i++);\n", + [Len, ic_util:mk_oe_name(G, Name)]), + emit(Fd, " *oe_out = oe_i;\n\n", []), + + emit(Fd, " if (oe_i == ~p) {\n",[Len]), + emit_c_enc_rpt(Fd, " ", "decode atom failure", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " return 0;\n",[]), + emit(Fd, "}\n\n",[]). + + + + + diff --git a/lib/ic/src/iceval.erl b/lib/ic/src/iceval.erl new file mode 100644 index 0000000000..81093dcd5b --- /dev/null +++ b/lib/ic/src/iceval.erl @@ -0,0 +1,555 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(iceval). + +-include("icforms.hrl"). + +-export([eval_const/5, eval_e/5]). + +-export([check_tk/3, get_val/1, mk_val/1]). + +-define(get_max(__X, __Y), if __X > __Y -> __X; true -> __Y end). +-define(get_min(__X, __Y), if __X > __Y -> __Y; true -> __X end). + +-define(BASE, 100000000000000000000000000000000). +-define(FIXED_MAX, 9999999999999999999999999999999). + +%% Called fr: ictype 99, 522, 533 +%% Fixed constants can be declared as: +%% (1) const fixed pi = 3.14D; or +%% (2) typedef fixed<3,2> f32; +%% const f32 pi = 3.14D; +%% Hence, if fixed is declared as (1) we must handle it especially. +eval_const(G, S, N, tk_fixed, Expr) -> + case catch eval_e(G, S, N, tk_fixed, Expr) of + T when element(1, T) == error -> 0; + V when is_record(V, fixed) -> + {ok, {tk_fixed, V#fixed.digits, V#fixed.scale}, V}; + V -> + ic_error:error(G, {bad_tk_match, Expr, tk_fixed, get_val(V)}) + end; +eval_const(G, S, N, TK, Expr) -> + case catch eval_e(G, S, N, TK, Expr) of + T when element(1, T) == error -> 0; + V -> + case check_tk(G, TK, V) of + true -> ok; + false -> + ic_error:error(G, {bad_tk_match, Expr, TK, get_val(V)}) + end, + get_val(V) + end. + + +check_op(G, S, N, Tk, Types, Op, E1, E2) -> + V1 = eval_e(G, S, N, Tk, E1), + V2 = eval_e(G, S, N, Tk, E2), + check_types(G, Op, E1, Types, V1), + check_types(G, Op, E2, Types, V2), + case check_comb(V1, V2) of + true -> + {V1, V2}; + false -> + Err = {bad_type_combination, E1, get_val(V1), get_val(V2)}, + ic_error:error(G, Err), + throw({error, Err}) + end. + +check_op(G, S, N, Tk, Types, Op, E1) -> + V1 = eval_e(G, S, N, Tk, E1), + check_types(G, Op, E1, Types, V1), + V1. + +%% Match the declared type TK against the factual value of an constant +%% +check_tk(_G, _Any, default) -> true; % Default case in union +check_tk(_G, positive_int, V) when is_integer(V) andalso V >= 0 -> true; +check_tk(_G, tk_long, V) when is_integer(V) -> true; +check_tk(_G, tk_longlong, V) when is_integer(V) -> true; %% LLON_G +check_tk(_G, tk_short, V) when is_integer(V) -> true; +check_tk(_G, tk_ushort, V) when is_integer(V) andalso V >= 0 -> true; +check_tk(_G, tk_ulong, V) when is_integer(V) andalso V >= 0 -> true; +check_tk(_G, tk_ulonglong, V) when is_integer(V) andalso V >= 0 -> true; %% ULLON_G +check_tk(_G, tk_float, V) when is_float(V) -> true; +check_tk(_G, tk_double, V) when is_float(V) -> true; +check_tk(_G, tk_boolean, V) -> is_bool(V); +check_tk(_G, tk_char, {char, _V}) -> true; +check_tk(_G, tk_wchar, {wchar, _V}) -> true; %% WCHAR +check_tk(_G, {tk_string, _Len}, {string, _V}) -> true; +check_tk(_G, {tk_wstring, _Len}, {wstring, _V}) -> true; %% WSTRING +check_tk(_G, {tk_fixed, Digits, Scale}, {fixed, Digits, Scale, _V}) -> true; +check_tk(_G, tk_octet, V) when is_integer(V) -> true; +%%check_tk(_G, tk_null, V) when integer(V) -> true; +%%check_tk(_G, tk_void, V) when integer(V) -> true; +%%check_tk(_G, tk_any, V) when integer(V) -> true; +%%check_tk(_G, {tk_objref, "", "Object"}, V) when integer(V) -> true. +check_tk(_G, {tk_enum, _, _, Body}, {enum_id, Id}) -> + until(fun(X) when X == Id -> true; + (_X) -> + false + end, Body); +check_tk(_G, _TK, _V) -> + false. + +get_val({string, X}) -> X; +get_val({wstring, X}) -> X; %% WCHAR +get_val({char, X}) -> X; +get_val({wchar, X}) -> X; %% WSTRING +get_val({enum_id, X}) -> X; +get_val(X) -> X. + +check_types(G, Op, Expr, TypeList, V) -> + case until(fun(int) when is_integer(V) -> true; + (float) when is_float(V) -> true; + (bool) when V==true -> true; + (bool) when V==false -> true; + (fixed) when is_record(V, fixed) -> true; + (_) -> false end, + TypeList) of + true -> true; + false -> + Err = {bad_type, Expr, Op, TypeList, V}, + ic_error:error(G, Err), + throw({error, Err}) + end. + +%%get_op(T) when tuple(T) -> element(1, T). + +%% Should be in lists +until(F, [H|T]) -> + case F(H) of + true -> true; + false -> until(F, T) + end; +until(_F, []) -> false. + +%% Section of all the boolean operators (because Erlang ops don't like +%% boolean values. +e_or(X, Y) when is_integer(X) andalso is_integer(Y) -> X bor Y; +e_or(true, _) -> true; +e_or(_, true) -> true; +e_or(_, _) -> false. + +e_and(X, Y) when is_integer(X) andalso is_integer(Y) -> X band Y; +e_and(true, true) -> true; +e_and(_, _) -> false. + +e_xor(X, Y) when is_integer(X) andalso is_integer(Y) -> X bxor Y; +e_xor(X, X) -> false; +e_xor(_, _) -> true. + +%% Handling infix operators (+,-,*,/) for fixed type. +%% Boundries determined as fixed<max(d1-s1,d2-s2) + max(s1,s2) + 1, max(s1,s2)> +e_fixed_add(#fixed{digits = D1, scale = S1, value = V1}, + #fixed{digits = D2, scale = S2, value = V2}) -> + Scale = ?get_max(S1, S2), + Digits = ?get_max((D1-S1), (D2-S2)) + Scale +1, + %% We must normalize the values before adding. Why? + %% 4.23 and 5.2 are represented as 423 and 52. To be able to get the + %% correct result we must add 4230 and 5200 == 9430. + {PV1, PV2} = normalize(S1, V1, S2, V2), + check_fixed_overflow(#fixed{digits = Digits, + scale = Scale, + value = (PV1 + PV2)}). + +%% Boundries determined as fixed<max(d1-s1,d2-s2) + max(s1,s2) + 1, max(s1,s2)> +e_fixed_sub(#fixed{digits = D1, scale = S1, value = V1}, + #fixed{digits = D2, scale = S2, value = V2}) -> + Scale = ?get_max(S1, S2), + Digits = ?get_max((D1-S1), (D2-S2)) + Scale +1, + {PV1, PV2} = normalize(S1, V1, S2, V2), + check_fixed_overflow(#fixed{digits = Digits, + scale = Scale, + value = (PV1 - PV2)}). + +%% Boundries determined as fixed<d1+d2, s1+s2> +e_fixed_mul(#fixed{digits = D1, scale = S1, value = V1}, + #fixed{digits = D2, scale = S2, value = V2}) -> + check_fixed_overflow(#fixed{digits = (D1+D2), + scale = (S1+S2), + value = V1*V2}). + +%% Boundries determined as fixed<(d1-s1+s2) + s inf ,s inf> +e_fixed_div(#fixed{digits = D1, scale = S1, value = V1}, + #fixed{digits = _D2, scale = S2, value = V2}) -> + {PV1, PV2} = normalize(S1, V1, S2, V2), + DigitsMin = (D1-S1+S2), + R1 = (PV1 div PV2), + R2 = (R1*?BASE + (PV1 rem PV2) * (?BASE div PV2)), + {Result2, Sinf} = delete_zeros_value(R2, 0, R1), + check_fixed_overflow(#fixed{digits = DigitsMin + Sinf, scale = Sinf, + value = Result2}). + + +%% Checks combination of argument types, basically floats and ints are +%% interchangeable, and all types are allowed with themselves. No +%% other combinations are allowed +%% +check_comb(X, Y) when is_integer(X) andalso is_integer(Y) -> true; +check_comb(X, Y) when is_float(X) andalso is_integer(Y) -> true; +check_comb(X, Y) when is_integer(X) andalso is_float(Y) -> true; +check_comb(X, Y) when is_float(X) andalso is_float(Y) -> true; +check_comb({X, _}, {X, _}) -> true; % Strings and chars are tuples +check_comb({fixed, _, _, _}, {fixed, _, _, _}) -> true; +check_comb(X, Y) -> + case {is_bool(X), is_bool(Y)} of + {true, true} -> + true; + _ -> + false + end. + +is_bool(true) -> true; +is_bool(false) -> true; +is_bool(_) -> false. + + +%%%% (15) +eval_e(G, S, N, Tk, {'or', T1, T2}) -> + {E1, E2} = check_op(G, S, N, Tk, [int, bool], 'or', T1, T2), + e_or(E1, E2); + +%%%% (16) +eval_e(G, S, N, Tk, {'xor', T1, T2}) -> + {E1, E2} = check_op(G, S, N, Tk, [int, bool], 'xor', T1, T2), + e_xor(E1, E2); + +%%%% (17) +eval_e(G, S, N, Tk, {'and', T1, T2}) -> + {E1, E2} = check_op(G, S, N, Tk, [int, bool], 'and', T1, T2), + e_and(E1, E2); + +%%%% (18) +eval_e(G, S, N, Tk, {'rshift', T1, T2}) -> + {E1, E2} = check_op(G, S, N, Tk, [int], 'rshift', T1, T2), + E1 bsr E2; +eval_e(G, S, N, Tk, {'lshift', T1, T2}) -> + {E1, E2} = check_op(G, S, N, Tk, [int], 'lshift', T1, T2), + E1 bsl E2; + +%%%% (19) +eval_e(G, S, N, Tk, {'+', T1, T2}) -> + case check_op(G, S, N, Tk, [int, float, fixed], '+', T1, T2) of + {F1, F2} when is_record(F1,fixed) andalso is_record(F2,fixed) -> + e_fixed_add(F1, F2); + {E1, E2} -> + E1 + E2 + end; +eval_e(G, S, N, Tk, {'-', T1, T2}) -> + case check_op(G, S, N, Tk, [int, float, fixed], '-', T1, T2) of + {F1, F2} when is_record(F1,fixed) andalso is_record(F2,fixed) -> + e_fixed_sub(F1, F2); + {E1, E2} -> + E1 - E2 + end; + +%%%% (20) +eval_e(G, S, N, Tk, {'*', T1, T2}) -> + case check_op(G, S, N, Tk, [int, float, fixed], '*', T1, T2) of + {F1, F2} when is_record(F1,fixed) andalso is_record(F2,fixed) -> + e_fixed_mul(F1, F2); + {E1, E2} -> + E1 * E2 + end; +eval_e(G, S, N, Tk, {'/', T1, T2}) -> + case check_op(G, S, N, Tk, [int, float, fixed], '/', T1, T2) of + {F1, F2} when is_record(F1,fixed) andalso is_record(F2,fixed) -> + e_fixed_div(F1, F2); + {E1, E2} -> + E1 / E2 + end; +eval_e(G, S, N, Tk, {'%', T1, T2}) -> + {E1, E2} = check_op(G, S, N, Tk, [int], '%', T1, T2), + E1 rem E2; + +%%%% (21) +eval_e(G, S, N, Tk, {{'-', _Line}, T}) -> + case check_op(G, S, N, Tk, [int, float, fixed], '-', T) of + F when is_record(F,fixed) -> + F#fixed{value = -(F#fixed.value)}; + Number -> + -Number + end; +eval_e(G, S, N, Tk, {{'+', _Line}, T}) -> + check_op(G, S, N, Tk, [int, float, fixed], '+', T); +eval_e(G, S, N, Tk, {{'~', Line}, T}) -> + ic_error:error(G, {unsupported_op, {'~', Line}}), + eval_e(G, S, N, Tk, T); + + +%% Ints are repr. by an Erlang integer val, floats and doubles by +%% Erlang floats, chars and strings must be tuplerized for type +%% checking. These tuples are removed just before returning from top +%% function. +%% +eval_e(_G, _S, _N, tk_fixed, {'<fixed_pt_literal>', _Line, X}) -> + create_fixed(X); +eval_e(G, _S, _N, {tk_fixed, Digits, Scale}, {'<fixed_pt_literal>', Line, X}) + when Digits < 32, Digits >= Scale -> + case convert_fixed(X, [], Digits, Digits-Scale) of + {error, Format, Args} -> + ic_error:error(G, {bad_fixed, Format, Args, Line}); + FixedData -> + {fixed, Digits, Scale, FixedData} + end; +eval_e(_G, _S, _N, _Tk, {'<integer_literal>', _Line, X}) -> list_to_integer(X); +eval_e(_G, _S, _N, {tk_string,_}, {'<string_literal>', _Line, X}) -> {string, X}; +eval_e(_G, _S, _N, {tk_wstring,_}, {'<wstring_literal>', _Line, X}) -> {wstring, X}; %% WSTRING +eval_e(_G, _S, _N, tk_char, {'<character_literal>', _Line, X}) -> {char, hd(X)}; +eval_e(_G, _S, _N, tk_wchar, {'<wcharacter_literal>', _Line, X}) -> {wchar, hd(X)}; %% WCHAR +eval_e(_G, _S, _N, _Tk, {'TRUE', _Line}) -> true; +eval_e(_G, _S, _N, _Tk, {'FALSE', _Line}) -> false; +eval_e(_G, _S, _N, _Tk, {'<floating_pt_literal>', _Line, X}) -> to_float(X); +%% Some possible error conditions +eval_e(_G, _S, _N, _Tk, {'<character_literal>', _Line, X}) -> {char, hd(X)}; %% ERROR? +%% +eval_e(G, S, N, _Tk, X) when element(1, X) == scoped_id -> + mk_val(ictype:scoped_lookup(G, S, N, X)); +eval_e(_G, _S, _N, _Tk, {default, _}) -> default; % Default case in union +eval_e(G, _S, _N, Tk, Val) -> + ic_error:error(G, {plain_error_string, Val, + io_lib:format("value and declared type ~p differ", [Tk])}). + +%% A fixed type can be 123.45 or 123 but we represent it as integers (i.e. 12345 or 123). +convert_fixed([], Acc, 0, _) -> + list_to_integer(lists:reverse(Acc)); +convert_fixed([], _Acc, _, _) -> + {error, "Fixed type do not match the digits field", []}; +convert_fixed([$.|Rest], Acc, Digits, 0) -> + convert_fixed(Rest, Acc, Digits, -1); +convert_fixed([$.|_Rest], _Acc, _, _) -> + {error, "Fixed decimal point placed incorrectly", []}; +convert_fixed([X|Rest], Acc, Digits, Position) -> + convert_fixed(Rest, [X|Acc], Digits-1, Position-1). + + +create_fixed([$0|Rest]) -> + %% Leading zeros shall be ignored. + create_fixed(Rest); +create_fixed(Fixed) -> + create_fixed(Fixed, [], 0, 0, false). + +create_fixed([], Acc, Total, Frac, true) -> + {Fixed, N} = remove_trailing_zeros(Acc, 0), + Digits = Total-N, + Scale = Frac-N, + #fixed{digits = Digits, scale = Scale, value = list_to_integer(Fixed)}; +create_fixed([], Acc, Total, _Frac, false) -> + %% A '.' never found. Hence, must be 2000D + #fixed{digits = Total, scale = 0, value = list_to_integer(lists:reverse(Acc))}; +create_fixed([$.|Rest], Acc, Total, _, _) -> + create_fixed(Rest, Acc, Total, 0, true); +create_fixed([X|Rest], Acc, Total, Frac, FoundDot) -> + create_fixed(Rest, [X|Acc], Total+1, Frac+1, FoundDot). + +remove_trailing_zeros([$0|Rest], N) -> + remove_trailing_zeros(Rest, N+1); +remove_trailing_zeros(Fixed, N) -> + {lists:reverse(Fixed), N}. + +%% Make the newly looked up value a value that can be type checked. +mk_val({_, _, {tk_string, _}, V}) -> {string, V}; +mk_val({_, _, {tk_wstring, _}, V}) -> {wstring, V}; %% WSTRING +mk_val({_, _, tk_char, V}) -> {char, V}; +mk_val({_, _, tk_wchar, V}) -> {wchar, V}; %% WCHAR +mk_val({_, _, enum_val, V}) -> + {enum_id, ic_forms:get_id2(V)}; +mk_val(X) when element(1, X) == error -> X; +mk_val({_, _, _TK, V}) -> + V; +mk_val(V) -> V. + + + +%% Floating point numbers +%% +%% Conversion to Erlang floating points is neccessary because +%% list_to_float BIF differs from IDL floats. "1e2" ".4e2" is +%% allowed in IDL and must be translated to "1.0e2" and "0.4e2" + +to_float(X) -> + list_to_float(erlangify(X)). + +erlangify([$. | R]) -> + [$0, $. | R]; +erlangify(R) -> + look_for_dot(R). + +look_for_dot([$. | R]) -> [$. | dot_pending(R)]; +look_for_dot([$e | R]) -> [$., $0, $e | R]; +look_for_dot([$E | R]) -> [$., $0, $E | R]; +look_for_dot([X | R]) -> [X | look_for_dot(R)]. + +dot_pending([$e | R]) -> [$0, $e | R]; +dot_pending([$E | R]) -> [$0, $E | R]; +dot_pending([]) -> [$0]; +dot_pending(R) -> R. + + +%%------------------------------------------------------------------ +%%--------------- Fixed Datatype Helper Functions ------------------ +%%------------------------------------------------------------------ +%% Pretty?! No, but since we now the upper-limit this is the fastest way +%% to calculate 10^x +power(0) -> 1; +power(1) -> 10; +power(2) -> 100; +power(3) -> 1000; +power(4) -> 10000; +power(5) -> 100000; +power(6) -> 1000000; +power(7) -> 10000000; +power(8) -> 100000000; +power(9) -> 1000000000; +power(10) -> 10000000000; +power(11) -> 100000000000; +power(12) -> 1000000000000; +power(13) -> 10000000000000; +power(14) -> 100000000000000; +power(15) -> 1000000000000000; +power(16) -> 10000000000000000; +power(17) -> 100000000000000000; +power(18) -> 1000000000000000000; +power(19) -> 10000000000000000000; +power(20) -> 100000000000000000000; +power(21) -> 1000000000000000000000; +power(22) -> 10000000000000000000000; +power(23) -> 100000000000000000000000; +power(24) -> 1000000000000000000000000; +power(25) -> 10000000000000000000000000; +power(26) -> 100000000000000000000000000; +power(27) -> 1000000000000000000000000000; +power(28) -> 10000000000000000000000000000; +power(29) -> 100000000000000000000000000000; +power(30) -> 1000000000000000000000000000000; +power(31) -> 10000000000000000000000000000000; +power(_) -> 10000000000000000000000000000000. + + + +%% If the result of an operation (+, -, * or /) causes overflow we use this +%% operation. However, since these calculations are performed during compiletime, +%% shouldn't the IDL-specification be changed to not cause overflow?! But, since +%% the OMG standard allows this we must support it. +check_fixed_overflow(#fixed{digits = Digits, scale = Scale, value = Value}) -> + case count_digits(abs(Value)) of + overflow -> + {N, NewVal} = cut_overflow(0, Value), +% NewDigits = Digits - N, + if + N > Scale -> + #fixed{digits = 31, scale = 0, value = NewVal}; + true -> + NewScale = Scale - N, + {NewVal2, Removed} = delete_zeros(NewVal, NewScale), + #fixed{digits = 31, scale = NewScale-Removed, value = NewVal2} + end; + Count when Count > Digits -> + Diff = Count-Digits, + if + Diff > Scale -> + #fixed{digits = Digits, scale = 0, + value = (Value div power(Diff))}; + true -> + NewScale = Scale-Diff, + {NewVal, Removed} = delete_zeros((Value div power(Diff)), NewScale), + #fixed{digits = Digits-Removed, + scale = NewScale-Removed, + value = NewVal} + end; + Count -> + {NewVal, Removed} = delete_zeros(Value, Scale), + #fixed{digits = Count-Removed, scale = Scale-Removed, value = NewVal} + end. + +%% This function see to that the values are of the same baase. +normalize(S, V1, S, V2) -> + {V1, V2}; +normalize(S1, V1, S2, V2) when S1 > S2 -> + {V1, V2*power(S1-S2)}; +normalize(S1, V1, S2, V2) -> + {V1*power(S2-S1), V2}. + +%% If we have access to the integer part of the fixed type we use this +%% operation to remove all trailing zeros. If we know the scale, length of +%% fraction part, we can use delete_zeros as well. But, after a division +%% it's hard to know the scale and we don't need to calcluate the integer part. +delete_zeros_value(0, N, _) -> + {0, 32-N}; +delete_zeros_value(X, N, M) when X > M, (X rem 10) == 0 -> + delete_zeros_value((X div 10), N+1, M); +delete_zeros_value(X, N, _) -> + {X, 32-N}. + +%% If we know the exact scale of a fixed type we can use this operation to +%% remove all trailing zeros. +delete_zeros(0, _) -> + {0,0}; +delete_zeros(X, Max) -> + delete_zeros(X, 0, Max). +delete_zeros(X, Max, Max) -> + {X, Max}; +delete_zeros(X, N, Max) when (X rem 10) == 0 -> + delete_zeros((X div 10), N+1, Max); +delete_zeros(X, N, _) -> + {X, N}. + +cut_overflow(N, X) when X > ?FIXED_MAX -> + cut_overflow(N+1, (X div 10)); +cut_overflow(N, X) -> + {N, X}. + +%% A fast way to check the size of a fixed data type. +count_digits(X) when X > ?FIXED_MAX -> overflow; +count_digits(X) when X >= 1000000000000000000000000000000 -> 31; +count_digits(X) when X >= 100000000000000000000000000000 -> 30; +count_digits(X) when X >= 10000000000000000000000000000 -> 29; +count_digits(X) when X >= 1000000000000000000000000000 -> 28; +count_digits(X) when X >= 100000000000000000000000000 -> 27; +count_digits(X) when X >= 10000000000000000000000000 -> 26; +count_digits(X) when X >= 1000000000000000000000000 -> 25; +count_digits(X) when X >= 100000000000000000000000 -> 24; +count_digits(X) when X >= 10000000000000000000000 -> 23; +count_digits(X) when X >= 1000000000000000000000 -> 22; +count_digits(X) when X >= 100000000000000000000 -> 21; +count_digits(X) when X >= 10000000000000000000 -> 20; +count_digits(X) when X >= 1000000000000000000 -> 19; +count_digits(X) when X >= 100000000000000000 -> 18; +count_digits(X) when X >= 10000000000000000 -> 17; +count_digits(X) when X >= 1000000000000000 -> 16; +count_digits(X) when X >= 100000000000000 -> 15; +count_digits(X) when X >= 10000000000000 -> 14; +count_digits(X) when X >= 1000000000000 -> 13; +count_digits(X) when X >= 100000000000 -> 12; +count_digits(X) when X >= 10000000000 -> 11; +count_digits(X) when X >= 1000000000 -> 10; +count_digits(X) when X >= 100000000 -> 9; +count_digits(X) when X >= 10000000 -> 8; +count_digits(X) when X >= 1000000 -> 7; +count_digits(X) when X >= 100000 -> 6; +count_digits(X) when X >= 10000 -> 5; +count_digits(X) when X >= 1000 -> 4; +count_digits(X) when X >= 100 -> 3; +count_digits(X) when X >= 10 -> 2; +count_digits(_X) -> 1. + +%%------------------------------------------------------------------ +%%--------------- END Fixed Datatype Helper Functions -------------- +%%------------------------------------------------------------------ diff --git a/lib/ic/src/icforms.hrl b/lib/ic/src/icforms.hrl new file mode 100644 index 0000000000..d1869e6330 --- /dev/null +++ b/lib/ic/src/icforms.hrl @@ -0,0 +1,68 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%% +%% Module documentation: +%% --------------------- +%% +%% Header file for the Erlang IDL compiler. Contains all records +%% used in the parse tree +%% +%% +%%------------------------------------------------------------ + + + +%%------------------------------------------------------------ + +-record(module, {id, body}). +-record(interface, {id, inherit, body, inherit_body, tk}). +-record(forward, {id, tk}). +-record(const, {type, id, val, tk}). +-record(type_dcl, {type, tk}). +-record(typedef, {type, id, tk}). +-record(struct, {id, body, tk}). +-record(member, {type, id}). +-record(union, {id, type, body, tk}). +-record(case_dcl, {label, id, type}). +-record(enum, {id, body, tk}). +-record(enumerator, {id}). +-record(sequence, {type, length=0}). +-record(string, {length=0}). +-record(wstring, {length=0}). %% WSTRING +-record(array, {id, size}). +-record(attr, {readonly, type, id, tk}). +-record(except, {id, body, tk}). +-record(op, {oneway, type, id, params, raises, ctx, tk}). +-record(param, {inout, type, id, tk}). +-record(fixed, {digits, scale, value}). + +%% NON-STANDARD +-record(preproc, {cat, id, aux}). +-record(pragma, {type, to, apply}). + + + + + + + + + + diff --git a/lib/ic/src/icparse.yrl b/lib/ic/src/icparse.yrl new file mode 100644 index 0000000000..25b0f452e7 --- /dev/null +++ b/lib/ic/src/icparse.yrl @@ -0,0 +1,864 @@ +%%<copyright> +%% <year>1997-2007</year> +%% <holder>Ericsson AB, All Rights Reserved</holder> +%%</copyright> +%%<legalnotice> +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson AB. +%%</legalnotice> +%% +%%------------------------------------------------------------ +%% Yecc spec for IDL +%% +%% +%% +%% Implementation Detail: +%% OorM_ means OneORMany and is used instead of +%% the "+" BNF notation +%% ZorM_ means ZeroORMany and is used instead of +%% the "*" BNF notation +%% +%% All the reverse/1 calls are because yecc+lists naturally leads +%% to reversed lists, which then have to be reversed. Maybe fix +%% this? +%% +%% Implementation history +%% +%% The IDL language supported is not the complete IDL. We skipped +%% the multiple declarator syntax allowed (i.e. typedef long T1, +%% T2). This also applies to attributes members in structs, +%% unions and exceptions, and to case labels in unions. The cases +%% where IDL has been altered is marked with comments containing +%% NIY. +%% +%% Above is chaging. Whenever we change a clause, we put (FIXED) in +%% its comment. +%% +%%------------------------------------------------------------ + + + + + +Nonterminals + '<op_type_spec>' + '<enumerator>' + '<switch_body>' + 'OorM_<case>' + '<member_list>' + '<struct_type>' + '<unsigned_int>' + '<constr_type_spec>' + '<shift_expr>' + '<or_expr>' + '<inheritance_spec>' + 'ZorM_<param_dcl>' + 'Opt_<context_expr>' + '<attr_dcl>' + '<array_declarator>' + '<element_spec>' + '<signed_int>' + '<primary_expr>' + '<interface_dcl>' + 'ZorM_<string_literal>' + 'Opt_<raises_expr>' + '<integer_type>' + '<signed_long_int>' + '<literal>' + '<export>' + '<forward_dcl>' + 'OorM_<definition>' + '<base_type_spec>' + '<op_dcl>' + '<const_exp>' + '<case>' + '<any_type>' + '<signed_short_int>' + '<unary_expr>' + '<context_expr>' + 'ZorM_<scoped_name>' + '<switch_type_spec>' + '<complex_declarator>' + '<declarators>' + 'OorM_<member>' + '<interface>' + '<parameter_dcls>' + '<op_attribute>' + '<positive_int_const>' + 'OorM_<fixed_array_size>' + '<sequence_type>' + '<case_label>' + '<octet_type>' + '<type_dcl>' + '<module>' + '<specification>' + '<declarator>' + '<boolean_type>' + '<union_type>' + '<add_expr>' + '<interface_body>' + '<except_dcl>' + '<fixed_array_size>' + '<unsigned_short_int>' + '<boolean_literal>' + '<and_expr>' + 'Opt_<inheritance_spec>' + '<scoped_name>' + '<param_type_spec>' + 'ZorM_<member>' + '<char_type>' + '<const_dcl>' + '<param_dcl>' + 'ZorM_<simple_declarator>' + 'ZorM_<declarator>' + '<const_type>' + '<definition>' + '<param_attribute>' + '<simple_declarator>' + 'Opt_readonly' + '<simple_type_spec>' + '<enum_type>' + '<type_spec>' + 'OorM_<case_label>' + '<floating_pt_type>' + '<template_type_spec>' + '<mult_expr>' + '<xor_expr>' + '<string_type>' + '<raises_expr>' + 'Opt_<op_attribute>' + 'ZorM_<enumerator>' + '<member>' + '<unsigned_long_int>' + '<type_declarator>' + '<unary_operator>' + 'ZorM_<export>' + '<interface_header>' + 'OE_preproc' % NON standard + 'OE_pragma' % NON standard + 'Ugly_pragmas' % NON standard + 'ZorM_<integer_literal>' + '<fixed_pt_type>' + '<fixed_pt_const_type>' + . + + +Terminals + '#' + 'in' + '[' + 'interface' + '(' + 'case' + 'union' + 'struct' + '<character_literal>' + '<wcharacter_literal>' + ')' + ']' + 'any' + 'long' + 'float' + 'out' + '*' + '^' + 'enum' + 'double' + '+' + 'context' + 'oneway' + 'sequence' + ',' + 'FALSE' + '<identifier>' + '{' + 'readonly' + ':' + '-' + 'void' + ';' + 'char' + 'wchar' %% WCHAR + '|' + 'inout' + '}' + 'attribute' + '<' + 'octet' + '/' + 'TRUE' + '~' + '=' + '>' + 'switch' + 'unsigned' + 'typedef' + '>>' + 'const' + '<string_literal>' + '<wstring_literal>' + 'raises' + 'string' + 'wstring' + 'fixed' + 'default' + 'short' + '%' + '<<' + 'module' + 'exception' + 'boolean' + '<integer_literal>' + '<fixed_pt_literal>' + '<floating_pt_literal>' + '&' + '::' + 'Object' + . + + +Rootsymbol '<specification>'. + + +%%------------------------------------------------------------ +%% Clauses +%% + +%% Handling of pragmas. +%% Pragma prefix, id and version are not standard. + +%% pragma prefix, or codeopt +OE_pragma -> '#' '<integer_literal>' '<identifier>' + '<identifier>' '<string_literal>' '#' + : #pragma{type='$4', to=followed, apply='$5'} . + +%% pragma id +OE_pragma -> '#' '<integer_literal>' '<identifier>' + '<identifier>' '<identifier>' '<string_literal>' '#' + : #pragma{type='$4', to='$5', apply='$6'} . + +%% pragma version +OE_pragma -> '#' '<integer_literal>' '<identifier>' + '<identifier>' '<identifier>' '<floating_pt_literal>' '#' + : #pragma{type='$4', to='$5', apply=ic_options:float_to_version('$6')} . + + + + + + + +%% Ugly pragmas +Ugly_pragmas -> '$empty' : []. +Ugly_pragmas -> 'Ugly_pragmas' 'OE_pragma' : ['$2'|'$1']. + + + +%% (0) Handling of preprocessor stuff. + +OE_preproc -> '#' '#' . + +OE_preproc -> '#' '<integer_literal>' '<string_literal>' + 'ZorM_<integer_literal>' '#' + : case '$4' of + [] -> + case '$2' of + {_,_,"1"} -> + #preproc{cat=line_nr, id='$3', aux='$4'}; + _ -> + [] + end; + _ -> + #preproc{cat=line_nr, id='$3', aux='$4'} + end. + +%% (0b) Non-standard +'ZorM_<integer_literal>' -> '$empty' : [] . +'ZorM_<integer_literal>' -> '<integer_literal>' 'ZorM_<integer_literal>' + : ['$1' | '$2'] . + +%% (1) +'<specification>' -> 'OorM_<definition>' : reverse('$1') . + + +%% Added clause +'OorM_<definition>' -> '<definition>' : ['$1'] . +'OorM_<definition>' -> 'OorM_<definition>' '<definition>' +: ['$2' | '$1'] . + + +%% (2) +'<definition>' -> '<type_dcl>' ';' : '$1' . +'<definition>' -> '<const_dcl>' ';' : '$1' . +'<definition>' -> '<except_dcl>' ';' : '$1' . +'<definition>' -> '<interface>' ';' : '$1' . +'<definition>' -> '<module>' ';' : '$1' . +'<definition>' -> 'OE_preproc' : '$1' . +'<definition>' -> 'OE_pragma' : '$1' . + + +%% (3) +'<module>' -> 'module' '<identifier>' '{' 'OorM_<definition>' '}' +: #module{ id='$2', body=reverse('$4')}. + + +%% (4) +'<interface>' -> '<interface_dcl>' : '$1' . +'<interface>' -> '<forward_dcl>' : '$1' . + + +%% (5) +'<interface_dcl>' -> '<interface_header>' '{' '<interface_body>' '}' + : #interface{id=element(1, '$1'), inherit=element(2, '$1'), + body=lists:reverse('$3')} . + + +%% (6) +'<forward_dcl>' -> 'interface' '<identifier>' +: #forward{id='$2'} . + + +%% (7) +'<interface_header>' -> 'interface' '<identifier>' 'Opt_<inheritance_spec>' +: {'$2', '$3'} . + + +%% (8) +'<interface_body>' -> 'ZorM_<export>' : '$1' . + + +%% Added clause +'ZorM_<export>' -> '$empty' : [] . +'ZorM_<export>' -> 'ZorM_<export>' '<export>' + %% Complicated because <export> might be a list (of type defs for instance) + : if is_list('$2') -> '$2' ++ '$1'; + true -> ['$2' | '$1'] + end . + + +%% (9) +'<export>' -> '<type_dcl>' ';' : '$1' . +'<export>' -> '<const_dcl>' ';' : '$1' . +'<export>' -> '<except_dcl>' ';' : '$1' . +'<export>' -> '<attr_dcl>' ';' : '$1' . +'<export>' -> '<op_dcl>' ';' : '$1' . +'<export>' -> 'OE_preproc' : '$1' . +'<export>' -> 'OE_pragma' : '$1' . + +%% Added clause +'Opt_<inheritance_spec>' -> '$empty' : []. +'Opt_<inheritance_spec>' -> '<inheritance_spec>' : '$1'. + +%% (10) +'<inheritance_spec>' -> ':' '<scoped_name>' 'ZorM_<scoped_name>' + : ['$2' | reverse('$3')] . + + +%% Added clause +'ZorM_<scoped_name>' -> '$empty' : [] . +'ZorM_<scoped_name>' -> 'ZorM_<scoped_name>' ',' '<scoped_name>' + : ['$3' | '$1'] . + + +%% (11) +'<scoped_name>' -> '<identifier>' : ic_symtab:scoped_id_new('$1') . +'<scoped_name>' -> '::' '<identifier>' : ic_symtab:scoped_id_new_global('$2') . +'<scoped_name>' -> '<scoped_name>' '::' '<identifier>' + : ic_symtab:scoped_id_add('$1', '$3') . + + +%% (12) +'<const_dcl>' -> 'const' '<const_type>' '<identifier>' '=' '<const_exp>' + : #const{type='$2', id='$3', val='$5'} . + + +%% (13) +'<const_type>' -> '<integer_type>' : '$1' . +'<const_type>' -> '<char_type>' : '$1' . +'<const_type>' -> '<boolean_type>' : '$1' . +'<const_type>' -> '<floating_pt_type>' : '$1' . +'<const_type>' -> '<string_type>' : '$1' . +'<const_type>' -> '<fixed_pt_const_type>' : '$1' . +'<const_type>' -> '<scoped_name>' : '$1' . +'<const_type>' -> '<octet_type>' : '$1' . + + +%% (14) +'<const_exp>' -> '<or_expr>' : '$1' . + + +%% (15) +'<or_expr>' -> '<xor_expr>' : '$1' . +'<or_expr>' -> '<or_expr>' '|' '<xor_expr>' : {'or', '$1', '$3'} . + + +%% (16) +'<xor_expr>' -> '<and_expr>' : '$1' . +'<xor_expr>' -> '<xor_expr>' '^' '<and_expr>' : {'xor', '$1', '$3'} . + + +%% (17) +'<and_expr>' -> '<shift_expr>' : '$1' . +'<and_expr>' -> '<and_expr>' '&' '<shift_expr>' : {'and', '$1', '$3'} . + + +%% (18) +'<shift_expr>' -> '<add_expr>' : '$1' . +'<shift_expr>' -> '<shift_expr>' '>>' '<add_expr>' : {'rshift', '$1', '$3'} . +'<shift_expr>' -> '<shift_expr>' '<<' '<add_expr>' : {'lshift', '$1', '$3'} . + + +%% (19) +'<add_expr>' -> '<mult_expr>' : '$1' . +'<add_expr>' -> '<add_expr>' '+' '<mult_expr>' : {'+', '$1', '$3'} . +'<add_expr>' -> '<add_expr>' '-' '<mult_expr>' : {'-', '$1', '$3'} . + + +%% (20) +'<mult_expr>' -> '<unary_expr>' : '$1' . +'<mult_expr>' -> '<mult_expr>' '*' '<unary_expr>' : {'*', '$1', '$3'} . +'<mult_expr>' -> '<mult_expr>' '/' '<unary_expr>' : {'/', '$1', '$3'} . +'<mult_expr>' -> '<mult_expr>' '%' '<unary_expr>' : {'%', '$1', '$3'} . + + +%% (21) +'<unary_expr>' -> '<unary_operator>' '<primary_expr>' : {'$1', '$2'} . +'<unary_expr>' -> '<primary_expr>' : '$1' . + + +%% (22) +'<unary_operator>' -> '-' : '$1' . +'<unary_operator>' -> '+' : '$1' . +'<unary_operator>' -> '~' : '$1' . + + +%% (23) +'<primary_expr>' -> '<scoped_name>' : '$1' . +'<primary_expr>' -> '<literal>' : '$1' . +'<primary_expr>' -> '(' '<const_exp>' ')' : '$2' . + + +%% (24) +'<literal>' -> '<integer_literal>' : '$1' . +'<literal>' -> '<wstring_literal>' : '$1' . +'<literal>' -> '<string_literal>' : '$1' . +'<literal>' -> '<character_literal>' : '$1' . +'<literal>' -> '<wcharacter_literal>' : '$1' . +'<literal>' -> '<fixed_pt_literal>' : '$1' . +'<literal>' -> '<floating_pt_literal>' : '$1' . +'<literal>' -> '<boolean_literal>' : '$1' . + + +%% (25) +'<boolean_literal>' -> 'TRUE' : '$1' . +'<boolean_literal>' -> 'FALSE' : '$1' . + + +%% (26) +'<positive_int_const>' -> '<const_exp>' : '$1' . + + +%% (27) +'<type_dcl>' -> 'typedef' '<type_declarator>' : '$2' . +'<type_dcl>' -> '<struct_type>' : '$1' . +'<type_dcl>' -> '<union_type>' : '$1' . +'<type_dcl>' -> '<enum_type>' : '$1' . + +%% (28) NIY multiple declarators (FIXED) +'<type_declarator>' -> '<type_spec>' '<declarators>' + : #typedef{type='$1', id='$2'} . %%%ic:unfold(#typedef{type='$1', id='$2'}) . +%%'<type_declarator>' -> '<type_spec>' '<declarator>' +%% : #typedef{type='$1', id='$2'} . + +%% (29) +'<type_spec>' -> '<simple_type_spec>' : '$1' . +'<type_spec>' -> '<constr_type_spec>' : '$1' . + + +%% (30) +'<simple_type_spec>' -> '<base_type_spec>' : '$1' . +'<simple_type_spec>' -> '<template_type_spec>' : '$1' . +'<simple_type_spec>' -> '<scoped_name>' : '$1' . + + +%% (31) +'<base_type_spec>' -> '<floating_pt_type>' : '$1' . +'<base_type_spec>' -> '<integer_type>' : '$1' . +'<base_type_spec>' -> '<char_type>' : '$1' . +'<base_type_spec>' -> '<boolean_type>' : '$1' . +'<base_type_spec>' -> '<octet_type>' : '$1' . +'<base_type_spec>' -> '<any_type>' : '$1' . +'<base_type_spec>' -> 'Object' : '$1' . %% NON Standard, isn't a base type + + +%% (32) +'<template_type_spec>' -> '<sequence_type>' : '$1' . +'<template_type_spec>' -> '<string_type>' : '$1' . +'<template_type_spec>' -> '<fixed_pt_type>' : '$1' . + + +%% (33) +'<constr_type_spec>' -> '<struct_type>' : '$1' . +'<constr_type_spec>' -> '<union_type>' : '$1' . +'<constr_type_spec>' -> '<enum_type>' : '$1' . + + +%% (34) +'<declarators>' -> '<declarator>' 'ZorM_<declarator>' +: ['$1' | reverse('$2')] . + +%% Added clause +'ZorM_<declarator>' -> '$empty' : [] . +'ZorM_<declarator>' -> 'ZorM_<declarator>' ',' '<declarator>' +: ['$3' | '$1'] . + + +%% (35) +'<declarator>' -> '<simple_declarator>' : '$1' . +'<declarator>' -> '<complex_declarator>' : '$1' . + + +%% (36) +'<simple_declarator>' -> '<identifier>' : '$1' . + + +%% (37) +'<complex_declarator>' -> '<array_declarator>' : '$1' . + + +%% (38) +'<floating_pt_type>' -> 'float' : '$1' . +'<floating_pt_type>' -> 'double' : '$1' . + + +%% (39) +'<integer_type>' -> '<signed_int>' : '$1' . +'<integer_type>' -> '<unsigned_int>' : {'unsigned', '$1'} . + + +%% (40) +'<signed_int>' -> '<signed_long_int>' : '$1' . +'<signed_int>' -> '<signed_short_int>' : '$1' . + + +%% (41) +'<signed_long_int>' -> 'long' : '$1' . +'<signed_long_int>' -> 'long' 'long': {'long long', element(2,'$2')} . + + +%% (42) +'<signed_short_int>' -> 'short' : '$1' . + + +%% (43) +'<unsigned_int>' -> '<unsigned_long_int>' : '$1' . +'<unsigned_int>' -> '<unsigned_short_int>' : '$1' . + + +%% (44) +'<unsigned_long_int>' -> 'unsigned' 'long' : '$2' . +'<unsigned_long_int>' -> 'unsigned' 'long' 'long' : {'long long', element(2,'$2')} . %% ULLONG + + +%% (45) +'<unsigned_short_int>' -> 'unsigned' 'short' : '$2' . + + +%% (46) +'<char_type>' -> 'char' : '$1' . +'<char_type>' -> 'wchar' : '$1' . %% WCHAR + + +%% (47) +'<boolean_type>' -> 'boolean' : '$1' . + + +%% (48) +'<octet_type>' -> 'octet' : '$1' . + + +%% (49) +'<any_type>' -> 'any' : '$1' . + +%% +'<fixed_pt_const_type>' -> 'fixed' : '$1'. + +%% (50) NIY: unfolding of struct decls (FIXED) +%%'<struct_type>' -> 'struct' '<identifier>' '{' '<member_list>' '}' +%% : #struct{id='$2', body=ic:unfold('$4')} . +'<struct_type>' -> 'struct' '<identifier>' '{' '<member_list>' '}' + : #struct{id='$2', body='$4'} . + + +%% (51) +'<member_list>' -> 'OorM_<member>' : reverse('$1') . + + +%% Added clause +%%'OorM_<member>' -> '<member>' : ['$1'] . +%%'OorM_<member>' -> 'OorM_<member>' '<member>' +%% : ['$2' | '$1'] . + +'OorM_<member>' -> '<member>' : '$1' . +'OorM_<member>' -> 'OorM_<member>' '<member>' + : '$2' ++ '$1' . + + + +%% (52) NIY: member multiple declarators (FIXED) +%%'<member>' -> '<type_spec>' '<declarators>' ';' +%% : #member{type='$1', id='$2'} . + +'<member>' -> 'Ugly_pragmas' '<type_spec>' '<declarators>' 'Ugly_pragmas' ';' 'Ugly_pragmas' + : '$1' ++ '$4' ++ '$6' ++ [#member{type='$2', id='$3'}] . + + +%% (53) NIY: unfolding of union cases (FIXED) +%%'<union_type>' -> 'union' '<identifier>' 'switch' +%% '(' '<switch_type_spec>' ')' '{' '<switch_body>' '}' +%% : #union{id='$2', type='$5', body=ic:unfold('$8')} . +'<union_type>' -> 'union' '<identifier>' 'switch' + '(' '<switch_type_spec>' ')' '{' '<switch_body>' '}' + : #union{id='$2', type='$5', body='$8'} . + + +%% (54) +'<switch_type_spec>' -> '<integer_type>' : '$1' . +'<switch_type_spec>' -> '<char_type>' : '$1' . +'<switch_type_spec>' -> '<boolean_type>' : '$1' . +'<switch_type_spec>' -> '<enum_type>' : '$1' . +'<switch_type_spec>' -> '<scoped_name>' : '$1' . + + +%% (55) +'<switch_body>' -> 'OorM_<case>' : reverse(lists:flatten('$1')) . + +%%'<switch_body>' -> 'OorM_<case>' : '$1' . + + +%% Added clause +'OorM_<case>' -> '<case>' : ['$1'] . +'OorM_<case>' -> 'OorM_<case>' '<case>' : ['$2' | '$1'] . + + +%% (56) NIY thing: multiple case labels (FIXED) +%%'<case>' -> 'OorM_<case_label>' '<element_spec>' ';' +%% : '$2'#case_dcl{label=reverse('$1')} . + +'<case>' -> + 'Ugly_pragmas' 'OorM_<case_label>' + 'Ugly_pragmas' '<element_spec>' + 'Ugly_pragmas' ';' 'Ugly_pragmas' + : '$1' ++ '$3' ++ '$5' ++ '$7' ++ [ '$4'#case_dcl{label=reverse('$2')} ] . + + +%% Added clause +%%'OorM_<case_label>' -> '<case_label>' : ['$1'] . +%%'OorM_<case_label>' -> 'OorM_<case_label>' '<case_label>' : ['$2' | '$1'] . + +'OorM_<case_label>' -> 'Ugly_pragmas' '<case_label>' 'Ugly_pragmas' + : '$1' ++ ['$2'] ++ '$3' . +'OorM_<case_label>' -> 'OorM_<case_label>' 'Ugly_pragmas' '<case_label>' 'Ugly_pragmas' + : '$2' ++ ['$3'|'$1'] ++ '$4'. + + +%% (57) +'<case_label>' -> 'case' '<const_exp>' ':' : '$2' . +'<case_label>' -> 'default' ':' : '$1' . + + +%% (58) +'<element_spec>' -> '<type_spec>' '<declarator>' +: #case_dcl{type='$1', id='$2'} . + + +%% (59) +%%'<enum_type>' -> 'enum' '<identifier>' +%%'{' '<enumerator>' 'ZorM_<enumerator>' '}' +%%: #enum{id='$2', body=['$4' | reverse('$5')]} . + +'<enum_type>' -> 'enum' '<identifier>' +'{' 'Ugly_pragmas' '<enumerator>' 'Ugly_pragmas' 'ZorM_<enumerator>' 'Ugly_pragmas' '}' +: #enum{id='$2', body='$4'++'$6'++'$8'++['$5' | reverse('$7')]} . + + + +%% Added clause +%%'ZorM_<enumerator>' -> '$empty' : [] . +%%'ZorM_<enumerator>' -> 'ZorM_<enumerator>' ',' '<enumerator>' : ['$3' | '$1'] . + +'ZorM_<enumerator>' -> '$empty' : [] . +'ZorM_<enumerator>' -> 'ZorM_<enumerator>' 'Ugly_pragmas' ',' 'Ugly_pragmas' '<enumerator>' + : '$2'++'$4'++['$5' | '$1'] . + +%% (60) +'<enumerator>' -> '<identifier>' : #enumerator{id='$1'} . + + +%% (61) +'<sequence_type>' -> 'sequence' '<' '<simple_type_spec>' ',' + '<positive_int_const>' '>' + : #sequence{type='$3', length='$5'} . +'<sequence_type>' -> 'sequence' '<' '<simple_type_spec>' '>' + : #sequence{type='$3'} . + + +%% (62) +'<string_type>' -> 'string' '<' '<positive_int_const>' '>' + : #string{length='$3'} . +'<string_type>' -> 'string' : #string{} . + +'<string_type>' -> 'wstring' '<' '<positive_int_const>' '>' %% WSTRING + : #wstring{length='$3'} . +'<string_type>' -> 'wstring' : #wstring{} . %% WSTRING + + +%% (63) +'<array_declarator>' -> '<identifier>' 'OorM_<fixed_array_size>' + : #array{id='$1', size=reverse('$2')} . + + +%% Added clause +'OorM_<fixed_array_size>' -> '<fixed_array_size>' : ['$1'] . +'OorM_<fixed_array_size>' -> 'OorM_<fixed_array_size>' '<fixed_array_size>' + : ['$2' | '$1'] . + + +%% (64) +'<fixed_array_size>' -> '[' '<positive_int_const>' ']' : '$2' . + + +%% (65) NIY: multiple attribute declarators (FIXED) +'<attr_dcl>' -> 'Opt_readonly' 'attribute' '<param_type_spec>' + '<simple_declarator>' 'ZorM_<simple_declarator>' + : #attr{readonly='$1', type='$3', id=['$4' | reverse('$5')]} . +%% : ic:unfold(#attr{readonly='$1', type='$3', id=['$4' | reverse('$5')]}) . +%%'<attr_dcl>' -> 'Opt_readonly' 'attribute' '<param_type_spec>' +%% '<simple_declarator>' + + +%% (66) NIY: unfolding of exception bodies (FIXED) +%%'<except_dcl>' -> 'exception' '<identifier>' '{' 'ZorM_<member>' '}' +%% : #except{id='$2', body=ic:unfold('$4')} . +'<except_dcl>' -> 'exception' '<identifier>' '{' 'ZorM_<member>' '}' + : #except{id='$2', body=reverse('$4')} . + +%% (67) +'<op_dcl>' -> 'Opt_<op_attribute>' '<op_type_spec>' '<identifier>' '<parameter_dcls>' 'Opt_<raises_expr>' 'Opt_<context_expr>' + : #op{oneway='$1', type='$2', id='$3', params='$4', raises='$5', ctx='$6'} . + +%% Added clause +'Opt_<op_attribute>' -> '$empty' : nil. +'Opt_<op_attribute>' -> '<op_attribute>' : '$1'. + +%% (68) +'<op_attribute>' -> 'oneway' : '$1' . + + +%% (69) +'<op_type_spec>' -> '<param_type_spec>' : '$1' . +'<op_type_spec>' -> 'void' : '$1' . + + +%% (70) Rewritten +%'<parameter_dcls>' -> '(' '<param_dcl>' 'ZorM_<param_dcl>' ')' +% : ['$2' | reverse('$3')] . +%'<parameter_dcls>' -> '(' ')' : [] . + +'<parameter_dcls>' -> '(' 'Ugly_pragmas' '<param_dcl>' 'ZorM_<param_dcl>' ')' + : '$2' ++ ['$3' | reverse('$4')] . +'<parameter_dcls>' -> '(' 'Ugly_pragmas' ')' : '$2' . + + +%% Added clause +%'ZorM_<param_dcl>' -> '$empty' : [] . +%'ZorM_<param_dcl>' -> 'ZorM_<param_dcl>' ',' '<param_dcl>' : ['$3' | '$1'] . + + +'ZorM_<param_dcl>' -> 'Ugly_pragmas' : '$1' . +'ZorM_<param_dcl>' -> 'ZorM_<param_dcl>' 'Ugly_pragmas' ',' 'Ugly_pragmas' '<param_dcl>' 'Ugly_pragmas' + : '$2' ++ '$4' ++ '$6' ++ ['$5' | '$1'] . + + + + +%% (71) +'<param_dcl>' -> '<param_attribute>' '<param_type_spec>' '<simple_declarator>' + : #param{inout='$1', type='$2', id='$3'} . + + +%% (72) +'<param_attribute>' -> 'in' : '$1' . +'<param_attribute>' -> 'out' : '$1' . +'<param_attribute>' -> 'inout' : '$1' . + + +%% Added clause +'Opt_<raises_expr>' -> '$empty' : [] . +'Opt_<raises_expr>' -> '<raises_expr>' : '$1' . + +%% (73) +'<raises_expr>' -> 'raises' '(' '<scoped_name>' 'ZorM_<scoped_name>' ')' + : ['$3'| reverse('$4')] . + + +%% Added clause +'Opt_<context_expr>' -> '$empty' : [] . +'Opt_<context_expr>' -> '<context_expr>' : '$1'. + +%% (74) +'<context_expr>' -> 'context' '(' '<string_literal>' 'ZorM_<string_literal>'')' + : ['$3' | reverse('$4')] . + + + +%% (75) +'<param_type_spec>' -> '<base_type_spec>' : '$1' . +'<param_type_spec>' -> '<string_type>' : '$1' . +'<param_type_spec>' -> '<scoped_name>' : '$1' . + + +%% (96) +'<fixed_pt_type>' -> 'fixed' '<' '<positive_int_const>' ',' '<positive_int_const>' '>' + : #fixed{digits='$3',scale='$5'} . + + +%% Added clause +'ZorM_<string_literal>' -> '$empty' : [] . +'ZorM_<string_literal>' -> 'ZorM_<string_literal>' ',' '<string_literal>' + : ['$3' | '$1'] . + +%% Added clause +'ZorM_<simple_declarator>' -> '$empty' : [] . +'ZorM_<simple_declarator>' -> 'ZorM_<simple_declarator>' ',' +'<simple_declarator>' : ['$3' | '$1'] . + +%% Added clause +%%'ZorM_<member>' -> '$empty' : [] . +%%'ZorM_<member>' -> 'ZorM_<member>' '<member>' : ['$2' | '$1'] . + +'ZorM_<member>' -> 'Ugly_pragmas' : '$1' . +'ZorM_<member>' -> 'ZorM_<member>' '<member>' : '$2' ++ '$1' . + + +%% Added clause +'Opt_readonly' -> '$empty' : nil. +'Opt_readonly' -> 'readonly' : '$1'. + + + +Erlang code. +%%----------------------------------------------------------- + + + diff --git a/lib/ic/src/icpreproc.erl b/lib/ic/src/icpreproc.erl new file mode 100644 index 0000000000..0ed7813ebd --- /dev/null +++ b/lib/ic/src/icpreproc.erl @@ -0,0 +1,111 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(icpreproc). + + + +-export([preproc/2]). + + +-import(lists, [filter/2]). + + +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- + + +preproc(G, File) -> + Cmd = ic_options:get_opt(G, preproc_cmd), + Flags = ic_options:get_opt(G, preproc_flags), + + + case Cmd of + "erl" -> + case ic_pp:run(File,Flags) of + {ok, [$#, $ , $1 | Rest], []} -> + [$#, $ , $1 | Rest]; + {ok, [$#, $ , $1 | Rest], Warning} -> + print_warning(G,Warning), + [$#, $ , $1 | Rest]; + {error,Error} -> + print_error(G,Error) + end; + + _ -> + Line = Cmd++" "++Flags++" "++File, + % FIXME: Check status code of command instead of this test + case os:cmd(Line) of + [$#, $ , C | Rest] when is_integer(C), C > $0, C =< $9 -> + [$#, $ , C | Rest]; + X -> + ic_error:fatal_error(G, {preproc, filter(X)}) + end + end. + + +filter(X) -> + X2 = divide_nl(X, []), + filter_x_switch(X2). + + +divide_nl([10 | Xs], Out) -> + [lists:reverse(Out) | divide_nl(Xs, [])]; +divide_nl([X | Xs], Out) -> divide_nl(Xs, [X|Out]); +divide_nl([], Out) -> lists:reverse(Out). + + +filter_x_switch(L) -> + filter(fun([$g,$c,$c,$:,$ ,$W,$a,$r,$n,$i,$n,$g,$:,$ ,$`,$-,$x,$ | _]) -> + false; + (_) -> true end, L). + + +print_error(_G,[]) -> + ok; +print_error(G,[{File,Line,Text}]) -> + ErrorText = File++":"++integer_to_list(Line)++": "++Text, + ic_error:fatal_error(G, {ic_pp_error, ErrorText}), + ok; +print_error(G,[{File,Line,Text}|T]) -> + ErrorText = File++":"++integer_to_list(Line)++": "++Text, + ic_error:error(G, {ic_pp_error, ErrorText}), + print_error(G,T); +print_error(G,[H]) -> + ErrorText = H++"\n", + ic_error:fatal_error(G, {ic_pp_error, ErrorText}), + ok; +print_error(G,[H|T]) -> + ErrorText = H++"\n", + ic_error:error(G, {ic_pp_error, ErrorText}), + print_error(G,T). + + +print_warning(_G,[]) -> + ok; +print_warning(G,[{File,Line,Text}|T]) -> + WarText = File++":"++integer_to_list(Line)++": "++Text, + ic_error:warn(G, {ic_pp_warning, WarText}), + print_warning(G,T); +print_warning(G,[H|T]) -> + WarText = H++"\n", + ic_error:warn(G, {ic_pp_warning, WarText}), + print_warning(G,T). + + diff --git a/lib/ic/src/icscan.erl b/lib/ic/src/icscan.erl new file mode 100644 index 0000000000..0960ba5d70 --- /dev/null +++ b/lib/ic/src/icscan.erl @@ -0,0 +1,452 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(icscan). + + +-export([scan/2]). + +-include("ic.hrl"). + + +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- + +-import(lists, [reverse/1]). + + +scan(G, File) -> + PL = call_preproc(G, File), + call_scan(G, PL). + +call_preproc(G, File) -> + case ic_options:get_opt(G, use_preproc) of + true -> + icpreproc:preproc(G, File); + false -> + case catch file:read_file(File) of + {ok, Bin} -> + binary_to_list(Bin); + Other -> + exit(Other) + end + end. + +call_scan(G, PL) -> + BE = ic_options:get_opt(G, be), + RSL = scan(G, BE, PL, 1, []), + lists:reverse(RSL). + + +%% Guard macros used at top scan functions only +-define(is_number(X), X >= $0 , X =< $9). +-define(is_upper(X), X >= $A , X =< $Z). +-define(is_lower(X), X >= $a, X =< $z). +-define(is_hex_uc(X), X >= $A , X =< $F). +-define(is_hex_lc(X), X >= $a , X =< $f). +-define(is_octal(X), X >=$0, X =< $7). + +%% Handle: +%% const wchar aWChar = L'X'; +scan(G, BE, [$L, $'|Str], Line, Out) -> + scan_const(G, BE, wchar, Str, [], Line, Out); +scan(G, BE, [$L, $"|Str], Line, Out) -> + scan_const(G, BE, wstring, Str, [], Line, Out); +scan(G, BE, [$_, X|Str], Line, Out) when ?is_upper(X) -> + scan_name(G, BE, Str, [X], false, Line, Out); +scan(G, BE, [$_, X|Str], Line, Out) when ?is_lower(X) -> + scan_name(G, BE, Str, [X], false, Line, Out); +scan(G, BE, [X|Str], Line, Out) when ?is_upper(X) -> + scan_name(G, BE, Str, [X], true, Line, Out); +scan(G, BE, [X|Str], Line, Out) when ?is_lower(X) -> + scan_name(G, BE, Str, [X], true, Line, Out); +scan(G, BE, [X|Str], Line, Out) when ?is_number(X) -> + scan_number(G, BE, Str, [X], Line, Out); +scan(G, BE, [9| T], Line, Out) -> scan(G, BE, T, Line, Out); +scan(G, BE, [32| T], Line, Out) -> scan(G, BE, T, Line, Out); +scan(G, BE, [$\r|Str], Line, Out) -> + scan(G, BE, Str, Line, Out); +scan(G, BE, [$\n|Str], Line, Out) -> + scan(G, BE, Str, Line+1, Out); +scan(G, BE, [$:, $: | Str], Line, Out) -> + scan(G, BE, Str, Line, [{'::', Line} | Out]); +scan(G, BE, [$/, $/ | Str], Line, Out) -> + Rest = skip_to_nl(Str), + scan(G, BE, Rest, Line, Out); +scan(G, BE, [$/, $* | Str], Line, Out) -> + Rest = skip_comment(Str), + scan(G, BE, Rest, Line, Out); +scan(G, BE, [$", $\\|Str], Line, Out) -> + scan_const(G, BE, string, [$\\|Str], [], Line, Out); +scan(G, BE, [$"|Str], Line, Out) -> + scan_const(G, BE, string, Str, [], Line, Out); +scan(G, BE, [$', $\\|Str], Line, Out) -> + scan_const(G, BE, char, [$\\|Str], [], Line, Out); +scan(G, BE, [$'|Str], Line, Out) -> + scan_const(G, BE, char, Str, [], Line, Out); +scan(G, BE, [$\\|Str], Line, Out) -> + scan_const(G, BE, escaped, [$\\|Str], [], Line, Out); +scan(G, BE, [$. | Str], Line, Out) -> + scan_frac(G, BE, Str, [$.], Line, Out); +scan(G, BE, [$# | Str], Line, Out) -> + scan_preproc(G, BE, Str, Line, Out); +scan(G, BE, [$<, $< | Str], Line, Out) -> + scan(G, BE, Str, Line, [{'<<', Line} | Out]); +scan(G, BE, [$>, $> | Str], Line, Out) -> + scan(G, BE, Str, Line, [{'>>', Line} | Out]); +scan(G, BE, [C|Str], Line, Out) -> + scan(G, BE, Str, Line, [{list_to_atom([C]), Line} | Out]); + +scan(_G, _BE, [], _Line, Out) -> + Out. + + +scan_number(G, BE, [X|Str], [$0], Line, Out) when X == $X ; X ==$x -> + case Str of + [D|_TmpStr] when ?is_number(D); ?is_hex_uc(D); ?is_hex_lc(D) -> + {Num,Rest} = scan_hex_number(Str,0), + scan(G, BE, Rest, Line, [{'<integer_literal>', Line, + integer_to_list(Num)} | Out]); + [D|TmpStr] -> + scan(G, BE, TmpStr, Line, [{list_to_atom([D]), Line} | Out]) + end; +scan_number(G, BE, Str, [$0], Line, Out) -> + %% If an integer literal starts with a 0 it may indicate that + %% it is represented as an octal number. But, it can also be a fixed + %% type which must use padding to match a fixed typedef. For example: + %% typedef fixed<5,2> fixed52; + %% 123.45d, 123.00d and 023.00d is all valid fixed values. + %% Naturally, a float can be defined as 0.14 or 00.14. + case pre_scan_number(Str, [], octal) of + octal -> + {Num, Rest} = scan_octal_number(Str,0), + scan(G, BE, Rest, Line, [{'<integer_literal>', Line, + integer_to_list(Num)} | Out]); + {fixed, Fixed, Rest} -> + scan(G, BE, Rest, Line, [{'<fixed_pt_literal>', Line, Fixed} | Out]); + float -> + %% Not very likely that someone defines a constant as 00.14 but ... + NewStr = remove_leading_zeroes(Str), + scan(G, BE, NewStr, Line, Out) + end; +scan_number(G, BE, [X|Str], Accum, Line, Out) when ?is_number(X) -> + scan_number(G, BE, Str, [X|Accum], Line, Out); +scan_number(G, BE, [X|Str], Accum, Line, Out) when X==$. -> + scan_frac(G, BE, Str, [X|Accum], Line, Out); +scan_number(G, BE, [X|Str], Accum, Line, Out) when X==$e ; X==$e -> + scan_exp(G, BE, Str, [X|Accum], Line, Out); +scan_number(G, BE, [X|Str], Accum, Line, Out) when X==$D ; X==$d -> + scan(G, BE, Str, Line, [{'<fixed_pt_literal>', Line, + (lists:reverse(Accum))} | Out]); +scan_number(G, BE, Str, Accum, Line, Out) -> + scan(G, BE, Str, Line, [{'<integer_literal>', Line, + (lists:reverse(Accum))} | Out]). + + +remove_leading_zeroes([$0|Rest]) -> + remove_leading_zeroes(Rest); +remove_leading_zeroes(L) -> + L. + +scan_hex_number([X|Rest],Acc) when X >=$a, X =< $f -> + scan_hex_number(Rest,(Acc bsl 4) + (X - $a + 10)); +scan_hex_number([X|Rest],Acc) when X >=$A, X =< $F -> + scan_hex_number(Rest,(Acc bsl 4) + (X - $A + 10)); +scan_hex_number([X|Rest],Acc) when X >=$0, X =< $9 -> + scan_hex_number(Rest,(Acc bsl 4) + (X-$0)); +scan_hex_number(Rest,Acc) -> + {Acc,Rest}. + +pre_scan_number([$d|Rest], Acc, _) -> + {fixed, [$0|lists:reverse(Acc)], Rest}; +pre_scan_number([$D|Rest], Acc, _) -> + {fixed, [$0|lists:reverse(Acc)], Rest}; +pre_scan_number([$.|Rest], Acc, _) -> + %% Actually, we don't know if it's a float since it can be a fixed. + pre_scan_number(Rest, [$.|Acc], float); +pre_scan_number([X|_], _Acc, _) when X == $E ; X ==$e -> + %% Now we now it's a float. + float; +pre_scan_number([X|Rest], Acc, Type) when ?is_number(X) -> + pre_scan_number(Rest, [X|Acc], Type); +pre_scan_number(_Rest, _Acc, Type) -> + %% At this point we know it's a octal or float. + Type. + +scan_octal_number([X|Rest],Acc) when ?is_octal(X) -> + scan_octal_number(Rest,(Acc bsl 3) + (X-$0)); +scan_octal_number(Rest,Acc) -> + {Acc, Rest}. + +%% Floating point number scan. +%% +%% Non trivial scan. A float consists of an integral part, a +%% decimal point, a fraction part, an e or E and a signed integer +%% exponent. Either the integer part or the fraction part but not +%% both may be missing, and either the decimal point or the +%% exponent part but not both may be missing. The exponent part +%% must consist of an e or E and a possibly signed exponent. +%% +%% Analysis shows that "1." ".7" "1e2" ".5e-3" "1.7e2" "1.7e-2" +%% is allowed and "1" ".e9" is not. The sign is only allowed just +%% after an e or E. The scanner reads a number as an integer +%% until it encounters a "." so the integer part only error case +%% will not be caught in the scanner (but rather in expression +%% evaluation) + +scan_frac(G, _BE, [$e | _Str], [$.], Line, _Out) -> + ic_error:fatal_error(G, {illegal_float, Line}); +scan_frac(G, _BE, [$E | _Str], [$.], Line, _Out) -> + ic_error:fatal_error(G, {illegal_float, Line}); +scan_frac(G, BE, Str, Accum, Line, Out) -> + scan_frac2(G, BE, Str, Accum, Line, Out). + +scan_frac2(G, BE, [X|Str], Accum, Line, Out) when ?is_number(X) -> + scan_frac2(G, BE, Str, [X|Accum], Line, Out); +scan_frac2(G, BE, [X|Str], Accum, Line, Out) when X==$e ; X==$E -> + scan_exp(G, BE, Str, [X|Accum], Line, Out); +%% The following case is for fixed (e.g. 123.45d). +scan_frac2(G, BE, [X|Str], Accum, Line, Out) when X==$d ; X==$D -> + scan(G, BE, Str, Line, [{'<fixed_pt_literal>', Line, + (lists:reverse(Accum))} | Out]); +scan_frac2(G, BE, Str, Accum, Line, Out) -> + scan(G, BE, Str, Line, [{'<floating_pt_literal>', Line, + (lists:reverse(Accum))} | Out]). + +scan_exp(G, BE, [X|Str], Accum, Line, Out) when X==$- -> + scan_exp2(G, BE, Str, [X|Accum], Line, Out); +scan_exp(G, BE, Str, Accum, Line, Out) -> + scan_exp2(G, BE, Str, Accum, Line, Out). + +scan_exp2(G, BE, [X|Str], Accum, Line, Out) when ?is_number(X) -> + scan_exp2(G, BE, Str, [X|Accum], Line, Out); +scan_exp2(G, BE, Str, Accum, Line, Out) -> + scan(G, BE, Str, Line, [{'<floating_pt_literal>', Line, + (lists:reverse(Accum))} | Out]). + + +scan_name(G, BE, [X|Str], Accum, TypeCheck, Line, Out) when ?is_upper(X) -> + scan_name(G, BE, Str, [X|Accum], TypeCheck, Line, Out); +scan_name(G, BE, [X|Str], Accum, TypeCheck, Line, Out) when ?is_lower(X) -> + scan_name(G, BE, Str, [X|Accum], TypeCheck, Line, Out); +scan_name(G, BE, [X|Str], Accum, TypeCheck, Line, Out) when ?is_number(X) -> + scan_name(G, BE, Str, [X|Accum], TypeCheck, Line, Out); +scan_name(G, BE, [$_|Str], Accum, TypeCheck, Line, Out) -> + scan_name(G, BE, Str, [$_|Accum], TypeCheck, Line, Out); +scan_name(G, BE, S, Accum, false, Line, Out) -> + %% The CORBA 2.3 specification allows the user to override typechecking: + %% typedef string _native; + %% interface i { + %% void foo(in _native VT); + %% }; + %% BUT, the IFR-id remains the same ("IDL:native:1.0") etc. The reason for + %% this is that one don't have to re-write a large chunk of IDL- and + %% application-code. + scan(G, BE, S, Line, [{'<identifier>', Line, lists:reverse(Accum)} | Out]); +scan_name(G, BE, S, Accum, _, Line, Out) -> + L = lists:reverse(Accum), + X = case is_reserved(L, BE) of + undefined -> + {'<identifier>', Line, L}; + Yes -> + {Yes, Line} + end, + scan(G, BE, S, Line, [X | Out]). + +%% Shall scan a constant +scan_const(G, BE, string, [$" | Rest], Accum, Line, [{'<string_literal>', _, Str}|Out]) -> + scan(G, BE, Rest, Line, + [{'<string_literal>', Line, Str ++ lists:reverse(Accum)} | Out]); +scan_const(G, BE, string, [$" | Rest], Accum, Line, Out) -> + scan(G, BE, Rest, Line, + [{'<string_literal>', Line, lists:reverse(Accum)} | Out]); +scan_const(G, BE, wstring, [$" | Rest], Accum, Line, [{'<wstring_literal>', _,Wstr}|Out]) -> %% WSTRING + scan(G, BE, Rest, Line, + [{'<wstring_literal>', Line, Wstr ++ lists:reverse(Accum)} | Out]); +scan_const(G, BE, wstring, [$" | Rest], Accum, Line, Out) -> %% WSTRING + scan(G, BE, Rest, Line, + [{'<wstring_literal>', Line, lists:reverse(Accum)} | Out]); +scan_const(G, _BE, string, [], _Accum, Line, Out) -> %% Bad string + ic_error:error(G, {bad_string, Line}), + Out; +scan_const(G, _BE, wstring, [], _Accum, Line, Out) -> %% Bad WSTRING + ic_error:error(G, {bad_string, Line}), + Out; +scan_const(G, BE, char, [$' | Rest], Accum, Line, Out) -> + scan(G, BE, Rest, Line, + [{'<character_literal>', Line, lists:reverse(Accum)} | Out]); +scan_const(G, BE, wchar, [$' | Rest], Accum, Line, Out) -> %% WCHAR + scan(G, BE, Rest, Line, + [{'<wcharacter_literal>', Line, lists:reverse(Accum)} | Out]); +scan_const(G, BE, Mode, [$\\, C | Rest], Accum, Line, Out) -> + case escaped_char(C) of + error -> + ic_error:error(G, {bad_escape_character, Line, C}), %% Bad escape character + scan_const(G, BE, Mode, Rest, [C | Accum], Line, Out); + octal -> + {Num,Rest2} = scan_octal_number([C|Rest], 0), + scan_const(G, BE, Mode, Rest2, [Num|Accum], Line, Out); + hexadecimal -> + {Num,Rest2} = scan_hex_number(Rest, 0), + if + Num > 255 -> %% 16#FF + ic_error:error(G, {bad_escape_character, Line, C}), + scan_const(G, BE, Mode, Rest, [C | Accum], Line, Out); + true -> + scan_const(G, BE, Mode, Rest2, [Num|Accum], Line, Out) + end; + unicode -> + {Num,Rest2} = scan_hex_number(Rest, 0), + if + Num > 65535 -> %% 16#FFFF + ic_error:error(G, {bad_escape_character, Line, C}), + scan_const(G, BE, Mode, Rest, [C | Accum], Line, Out); + true -> + scan_const(G, BE, Mode, Rest2, [Num|Accum], Line, Out) + end; + EC -> + scan_const(G, BE, Mode, Rest, [EC | Accum], Line, Out) + end; +scan_const(G, BE, Mode, [C | Rest], Accum, Line, Out) -> + scan_const(G, BE, Mode, Rest, [C | Accum], Line, Out). + + +%% +%% Preprocessor output handling +%% +%% gcc outputs a line with line number, file name (within \") and +%% one or more integer flags. The scanner scans the line number, +%% the id and all integers up to nl. +%% +%% NOTE: This will have to be enhanced in order to eat #pragma +%% +scan_preproc(G, BE, Str, Line, Out) -> + {List, Rest} = scan_to_nl(strip(Str), []), + NewLine = get_new_line_nr(strip(List), Line+1, []), + case scan_number(G, BE, List, [], Line, [{'#', Line} | Out]) of + L when is_list(L) -> + scan(G, BE, Rest, NewLine, [{'#', Line} | L]) + end. + +get_new_line_nr([C|R], Line, Acc) when C>=$0, C=<$9 -> + get_new_line_nr(R, Line, [C|Acc]); +get_new_line_nr(_, Line, []) -> Line; % No line nr found +get_new_line_nr(_, _, Acc) -> list_to_integer(reverse(Acc)). + +scan_to_nl([], Acc) -> {reverse(Acc), []}; +scan_to_nl([$\n|Str], Acc) -> {reverse(Acc), Str}; +scan_to_nl([$\r|R], Acc) -> scan_to_nl(R, Acc); +scan_to_nl([C|R], Acc) -> scan_to_nl(R, [C|Acc]). + +strip([$ |R]) -> strip(R); +strip(L) -> L. + +%% Escaped character. Escaped chars are repr as two characters in the +%% input list of letters and this is translated into one char. +escaped_char($n) -> $\n; +escaped_char($t) -> $\t; +escaped_char($v) -> $\v; +escaped_char($b) -> $\b; +escaped_char($r) -> $ ; +escaped_char($f) -> $\f; +escaped_char($a) -> $\a; +escaped_char($\\) -> $\\; +escaped_char($?) -> $?; +escaped_char($') -> $'; +escaped_char($") -> $"; +escaped_char($x) -> hexadecimal; +escaped_char($u) -> unicode; +escaped_char(X) when ?is_octal(X) -> octal; +%% Error +escaped_char(_Other) -> error. + +skip_to_nl([]) -> []; +skip_to_nl([$\n | Str]) ->[$\n | Str]; +skip_to_nl([_|Str]) -> + skip_to_nl(Str). + +skip_comment([$\\, _ | Str]) -> + skip_comment(Str); +skip_comment([$*, $/ | Str]) -> Str; +skip_comment([_|Str]) -> + skip_comment(Str). + + +%%---------------------------------------------------------------------- +%% Shall separate keywords from identifiers and numbers + +%% Fill in the ets of reserved words +is_reserved("Object", _) -> 'Object'; +is_reserved("in", _) -> in; +is_reserved("interface", _) -> interface; +is_reserved("case", _) -> 'case'; +is_reserved("union", _) -> union; +is_reserved("struct", _) -> struct; +is_reserved("any", _) -> any; +is_reserved("long", _) -> long; +is_reserved("float", _) -> float; +is_reserved("out", _) -> out; +is_reserved("enum", _) -> enum; +is_reserved("double", _) -> double; +is_reserved("context", _) -> context; +is_reserved("oneway", _) -> oneway; +is_reserved("sequence", _) -> sequence; +is_reserved("FALSE", _) -> 'FALSE'; +is_reserved("readonly", _) -> readonly; +is_reserved("char", _) -> char; +is_reserved("wchar", _) -> wchar; +is_reserved("void", _) -> void; +is_reserved("inout", _) -> inout; +is_reserved("attribute", _) -> attribute; +is_reserved("octet", _) -> octet; +is_reserved("TRUE", _) -> 'TRUE'; +is_reserved("switch", _) -> switch; +is_reserved("unsigned", _) -> unsigned; +is_reserved("typedef", _) -> typedef; +is_reserved("const", _) -> const; +is_reserved("raises", _) -> raises; +is_reserved("string", _) -> string; +is_reserved("wstring", _) -> wstring; +is_reserved("default", _) -> default; +is_reserved("short", _) -> short; +is_reserved("module", _) -> module; +is_reserved("exception", _) -> exception; +is_reserved("boolean", _) -> boolean; +%% --- New keywords Introduced in CORBA-2.3.1 --- +%% For now we cannot add these for all backends right now since it would cause +%% some problems for at least one customer. +is_reserved("fixed", BE) -> check_be(BE, fixed); +%is_reserved("abstract", BE) -> check_be(BE, abstract); +%is_reserved("custom", BE) -> check_be(BE, custom); +%is_reserved("factory", BE) -> check_be(BE, factory); +%is_reserved("local", BE) -> check_be(BE, local); +%is_reserved("native", BE) -> check_be(BE, native); +%is_reserved("private", BE) -> check_be(BE, private); +%is_reserved("public", BE) -> check_be(BE, public); +%is_reserved("supports", BE) -> check_be(BE, supports); +%is_reserved("truncatable", BE) -> check_be(BE, truncatable); +%is_reserved("ValueBase", BE) -> check_be(BE, 'ValueBase'); +%is_reserved("valuetype", BE) -> check_be(BE, valuetype); +is_reserved(_, _) -> undefined. + +check_be(erl_corba, KeyWord) -> + KeyWord; +check_be(_, _) -> + undefined. + diff --git a/lib/ic/src/icstruct.erl b/lib/ic/src/icstruct.erl new file mode 100644 index 0000000000..6058b3c455 --- /dev/null +++ b/lib/ic/src/icstruct.erl @@ -0,0 +1,1916 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(icstruct). + + +-export([struct_gen/4, except_gen/4, create_c_array_coding_file/5]). + +%%------------------------------------------------------------ +%% +%% Internal stuff +%% +%%------------------------------------------------------------ +-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"). + + + +%%------------------------------------------------------------ + +%%------------------------------------------------------------ +%% +%% File handling stuff +%% +%%------------------------------------------------------------ + + + +%%------------------------------------------------------------ +%% +%% Generation loop +%% +%% The idea is to traverse everything and find every struct that +%% may be hiding down in nested types. All structs that are found +%% are generated to a hrl file. +%% +%% struct_gen is entry point for structs and types, except_gen is +%% for exceptions +%% +%%------------------------------------------------------------ + + +except_gen(G, N, X, L) when is_record(X, except) -> + N2 = [ic_forms:get_id2(X) | N], + if + L == c -> + io:format("Warning : Exception not defined for c mapping\n", []); + true -> + emit_struct(G, N, X, L) + end, + struct_gen_list(G, N2, ic_forms:get_body(X), L). + +struct_gen(G, N, X, L) when is_record(X, struct) -> + N2 = [ic_forms:get_id2(X) | N], + struct_gen_list(G, N2, ic_forms:get_body(X), L), + emit_struct(G, N, X, L); +struct_gen(G, N, X, L) when is_record(X, union) -> + N2 = [ic_forms:get_id2(X) | N], + if + L == c -> + %% Produce the "body" first + struct_gen_list(G, N2, ic_forms:get_body(X), L), + icunion:union_gen(G, N, X, c); + true -> + struct_gen(G, N, ic_forms:get_type(X), L), + struct_gen_list(G, N2, ic_forms:get_body(X), L) + end, + emit_union(G, N, X, L); +struct_gen(G, N, X, L) when is_record(X, member) -> + struct_gen(G, N, ic_forms:get_type(X), L); +struct_gen(G, N, X, L) when is_record(X, typedef) -> + struct_gen(G, N, ic_forms:get_body(X), L), + emit_typedef(G, N, X, L); +struct_gen(G, N, X, L) when is_record(X, type_dcl) -> + struct_gen_list(G, N, ic_forms:get_type(X), L); +struct_gen(G, N, X, L) when is_record(X, case_dcl) -> + struct_gen(G, N, ic_forms:get_type(X), L); +struct_gen(G, N, X, L) when is_record(X, sequence) -> + struct_gen(G, N, ic_forms:get_type(X), L), + X; +struct_gen(G, N, X, L) when is_record(X, enum) -> + icenum:enum_gen(G, N, X, L); +struct_gen(_G, _N, _X, _L) -> + ok. + +%% List clause for struct_gen +struct_gen_list(G, N, Xs, L) -> + lists:foreach( + fun(X) -> + R = struct_gen(G, N, X, L), + if + L == c -> + if + is_record(R,sequence) -> + emit_sequence_head_def(G,N,X,R,L); + true -> + ok + end; + true -> + ok + end + end, Xs). + + +%% emit primitive for structs. +emit_struct(G, N, X, erlang) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + %% Make a straight list of all member ids (this is a + %% variant of flatten) + EList = lists:map( + fun(XX) -> + lists:map( + fun(XXX) -> + ic_util:to_atom(ic_forms:get_id2(XXX)) + end, + ic_forms:get_idlist(XX)) + end, + ic_forms:get_body(X)), + ic_codegen:record(G, X, + ic_util:to_undersc([ic_forms:get_id2(X) | N]), + ictk:get_IR_ID(G, N, X), lists:flatten(EList)), + mkFileRecObj(G,N,X,erlang); + false -> + ok + end; +emit_struct(G, N, X, c) -> + + N1 = [ic_forms:get_id2(X) | N], + case ic_pragma:is_local(G,N1) of + true -> + emit_c_struct(G, N, X,local); + false -> + emit_c_struct(G, N, X,included) + end. + + +emit_c_struct(_G, _N, _X, included) -> + %% Do not generate included types att all. + ok; +emit_c_struct(G, N, X, local) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + + N1 = [ic_forms:get_id2(X) | N], + StructName = ic_util:to_undersc(N1), + + %% Make a straight list of all member ids (this is a + %% variant of flatten) + M = lists:map( + fun(XX) -> + lists:map( + fun(XXX) -> + if + is_record(XXX, array) -> + Type = ic_forms:get_type(XX), + Name = element(3,element(2,XXX)), + {_, _, StructTK, _} = + ic_symtab:get_full_scoped_name( + G, + N, + ic_symtab:scoped_id_new( + ic_forms:get_id2(X))), + ArrayTK = + get_structelement_tk(StructTK, + Name), + Dim = extract_dim(ArrayTK), + %% emit array file + emit(Fd, "\n#ifndef __~s__\n", + [ic_util:to_uppercase( + StructName ++ "_" + ++ Name)]), + emit(Fd, "#define __~s__\n\n", + [ic_util:to_uppercase( + StructName ++ "_" + ++ Name)]), + create_c_array_coding_file( + G, + N, + {StructName ++ "_" ++ Name, Dim}, + Type, + no_typedef), + emit(Fd, "\n#endif\n\n"), + {{Type, XXX}, + ic_forms:get_id2(XXX)}; + true -> + %% Ugly work around to fix the ETO + %% return patch problem + Name = + case ic_forms:get_id2(XXX) of + "return" -> + "return1"; + Other -> + Other + end, + {ic_forms:get_type(XX), Name} + end + end, + ic_forms:get_idlist(XX)) + end, + ic_forms:get_body(X)), + EList = lists:flatten(M), + %%io:format("Elist = ~p~n",[EList]), + + emit(Fd, "\n#ifndef __~s__\n",[ic_util:to_uppercase(StructName)]), + emit(Fd, "#define __~s__\n",[ic_util:to_uppercase(StructName)]), + ic_codegen:mcomment_light(Fd, + [io_lib:format("Struct definition: ~s", + [StructName])], + c), + emit(Fd, "typedef struct {\n"), + lists:foreach( + fun({Type, Name}) -> + emit_struct_member(Fd, G, N1, X, Name, Type) + end, + EList), + emit(Fd, "} ~s;\n\n", [StructName]), + create_c_struct_coding_file(G, N, X, nil, StructName, + EList, struct), + emit(Fd, "\n#endif\n\n"); + false -> + ok + end. + +%% Extracts array dimention(s) + +get_structelement_tk({tk_struct, _, _, EList}, EN) -> + {value, {EN, ArrayTK}} = lists:keysearch(EN, 1, EList), + ArrayTK. + +extract_dim({tk_array, {tk_array, T, D1}, D}) -> + [integer_to_list(D) | extract_dim({tk_array, T, D1})]; +extract_dim({tk_array, _, D}) -> + [integer_to_list(D)]. + +%% Makes the array name +mk_array_name(Name,Dim) -> + Name ++ mk_array_name(Dim). + +mk_array_name([]) -> + ""; +mk_array_name([Dim|Dims]) -> + "[" ++ Dim ++ "]" ++ mk_array_name(Dims). + + +emit_struct_member(Fd, G, N, X, Name,{Type,Array}) when is_record(Array, array)-> + {_, _, StructTK, _} = + ic_symtab:get_full_scoped_name( + G, + N, + ic_symtab:scoped_id_new(ic_forms:get_id2(X))), + ArrayTK = get_structelement_tk(StructTK, Name), + Dim = extract_dim(ArrayTK), + emit(Fd, " ~s ~s;\n", + [ic_cbe:mk_c_type(G, N, Type),mk_array_name(Name,Dim)]); +emit_struct_member(Fd, _G, N, _X, Name, Union) when is_record(Union, union)-> + emit(Fd, " ~s ~s;\n", + [ic_util:to_undersc([ic_forms:get_id2(Union) | N]),Name]); +emit_struct_member(Fd, _G, _N, _X, Name, {string, _}) -> + emit(Fd, " CORBA_char *~s;\n", + [Name]); +emit_struct_member(Fd, _G, N, _X, Name, {sequence, _Type, _Length}) -> + %% Sequence used as struct + emit(Fd, " ~s ~s;\n", + [ic_util:to_undersc([Name | N]), Name]); +emit_struct_member(Fd, G, N, X, Name, Type) + when element(1, Type) == scoped_id -> + CType = ic_cbe:mk_c_type(G, N, Type, evaluate_not), + emit_struct_member(Fd, G, N, X, Name, CType); +emit_struct_member(Fd, G, N, _X, Name, {enum, Type}) -> + emit(Fd, " ~s ~s;\n", + [ic_cbe:mk_c_type(G, N, Type), + Name]); +emit_struct_member(Fd, _G, _N, _X, Name, "ETERM*") -> + emit(Fd, " ETERM* ~s;\n", + [Name]); +emit_struct_member(Fd, _G, _N, _X, Name, Type) when is_list(Type) -> + emit(Fd, " ~s ~s;\n", + [Type, Name]); +emit_struct_member(Fd, G, N, _X, Name, Type) -> + emit(Fd, " ~s ~s;\n", + [ic_cbe:mk_c_type(G, N, Type), + Name]). + + +emit_typedef(G, N, X, erlang) -> + case X of + {typedef,_,[{array,_,_}],_} -> %% Array but not a typedef of + %% an array definition + case ic_options:get_opt(G, be) of + noc -> + mkFileArrObj(G,N,X,erlang); + _ -> + %% Search the table to see if the type is local or + %% inherited. + PTab = ic_genobj:pragmatab(G), + Id = ic_forms:get_id2(X), + case ets:match(PTab,{file_data_local,'_','_', + typedef,N,Id, + ic_util:to_undersc([Id | N]), + '_','_'}) of + [[]] -> + %% Local, create erlang file for the array + mkFileArrObj(G,N,X,erlang); + _ -> + %% Inherited, do nothing + ok + end + end; + + {typedef,{sequence,_,_},_,{tk_sequence,_,_}} -> + %% Sequence but not a typedef of + %% a typedef of a sequence definition + case ic_options:get_opt(G, be) of + noc -> + mkFileRecObj(G,N,X,erlang); + _ -> + %% Search the table to see if the type is local or + %% inherited. + PTab = ic_genobj:pragmatab(G), + Id = ic_forms:get_id2(X), + case ets:match(PTab,{file_data_local,'_','_',typedef, + N,Id, + ic_util:to_undersc([Id | N]), + '_','_'}) of + [[]] -> + %% Local, create erlang file for the sequence + mkFileRecObj(G,N,X,erlang); + _ -> + %% Inherited, do nothing + ok + end + end; + _ -> + ok + end; +emit_typedef(G, N, X, c) -> + B = ic_forms:get_body(X), + if + is_record(B, sequence) -> + emit_sequence_head_def(G, N, X, B, c); + true -> + lists:foreach(fun(D) -> + emit_typedef(G, N, D, B, c) + end, + ic_forms:get_idlist(X)) + end. + +emit_typedef(G, N, D, Type, c) when is_record(D, array) -> + emit_array(G, N, D, Type); +emit_typedef(G, N, D, Type, c) -> + Name = ic_util:to_undersc([ic_forms:get_id2(D) | N]), + CType = ic_cbe:mk_c_type(G, N, Type), + TDType = mk_base_type(G, N, Type), + ic_code:insert_typedef(G, Name, TDType), + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + emit(Fd, "\n#ifndef __~s__\n",[ic_util:to_uppercase(Name)]), + emit(Fd, "#define __~s__\n",[ic_util:to_uppercase(Name)]), + ic_codegen:mcomment_light(Fd, + [io_lib:format("Type definition ~s " + "for type ~s", + [Name, CType])], + c), + emit(Fd, "typedef ~s ~s;\n", + [CType, Name]), + emit(Fd, "\n#endif\n\n"), + ic_codegen:nl(Fd); + false -> + ok + end. + + +mk_base_type(G, N, S) when element(1, S) == scoped_id -> + {FullScopedName, _T, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + BT = ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)), + case BT of + "erlang_binary" -> + "erlang_binary"; + "erlang_pid" -> + "erlang_pid"; + "erlang_port" -> + "erlang_port"; + "erlang_ref" -> + "erlang_ref"; + "erlang_term" -> + "ETERM*"; + Type -> + Type + end; +mk_base_type(_G, _N, S) -> + S. + +emit_array(G, N, D, Type) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + Name = ic_util:to_undersc([ic_forms:get_id2(D) | N]), + {_, _, ArrayTK, _} = + ic_symtab:get_full_scoped_name(G, N, + ic_symtab:scoped_id_new( + ic_forms:get_id(D))), + Dim = extract_dim(ArrayTK), + CType = ic_cbe:mk_c_type(G, N, Type), + emit(Fd, "\n#ifndef __~s__\n",[ic_util:to_uppercase(Name)]), + emit(Fd, "#define __~s__\n",[ic_util:to_uppercase(Name)]), + ic_codegen:mcomment_light(Fd, + [io_lib:format("Array definition ~s " + "for type ~s", + [Name, CType])], + c), + emit(Fd, "typedef ~s ~s~s;\n", + [CType, Name, ic_cbe:mk_dim(Dim)]), + emit(Fd, "typedef ~s ~s_slice~s;\n", + [CType, Name, ic_cbe:mk_slice_dim(Dim)]), + ic_codegen:nl(Fd), + create_c_array_coding_file(G, N, {Name, Dim}, Type, typedef), + emit(Fd, "\n#endif\n\n"); + false -> + ok + end. + +open_c_coding_file(G, Name) -> + SName = string:concat(ic_util:mk_oe_name(G, "code_"), Name), + FName = + ic_file:join(ic_options:get_opt(G, stubdir),ic_file:add_dot_c(SName)), + case file:open(FName, [write]) of + {ok, Fd} -> + {Fd, SName}; + Other -> + exit(Other) + end. + + + +create_c_array_coding_file(G, N, {Name, Dim}, Type, TypeDefFlag) -> + + {Fd , SName} = open_c_coding_file(G, Name), + HFd = ic_genobj:hrlfiled(G), %% Write on stubfile header + HrlFName = filename:basename(ic_genobj:include_file(G)), + ic_codegen:emit_stub_head(G, Fd, SName, c), + emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% Fd = ic_genobj:stubfiled(G), %% Write on stubfile + %% HFd = ic_genobj:hrlfiled(G), %% Write on stubfile header + %% HrlFName = filename:basename(ic_genobj:include_file(G)), + %% emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + put(op_variable_count, 0), + put(tmp_declarations, []), + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, int*, int*);\n", + [ic_util:mk_oe_name(G, "sizecalc_"), Name]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, int* oe_size_count_index, " + "int* oe_size) {\n", [ic_util:mk_oe_name(G, "sizecalc_"), Name]), + + emit(Fd, " int oe_malloc_size = 0;\n",[]), + emit(Fd, " int oe_error_code = 0;\n",[]), + emit(Fd, " int oe_type = 0;\n",[]), + emit(Fd, " int oe_array_size = 0;\n",[]), + + {ok, RamFd} = ram_file:open([], [binary, write]), + + emit_sizecount(array, G, N, nil, RamFd, {Name, Dim}, Type), + + ic_cbe:emit_tmp_variables(Fd), + ic_codegen:nl(Fd), + %% Move data from ram file to output file. + {ok, Data} = ram_file:get_file(RamFd), + emit(Fd, Data), + ram_file:close(RamFd), + + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n",[]), + + put(op_variable_count, 0), + put(tmp_declarations, []), + + RefStr = get_refStr(Dim), + + case TypeDefFlag of + typedef -> + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, ~s);\n", + [ic_util:mk_oe_name(G, "encode_"), Name, Name]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, ~s oe_rec) {\n", + [ic_util:mk_oe_name(G, "encode_"), Name, Name]); + no_typedef -> + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, ~s oe_rec~s);\n", + [ic_util:mk_oe_name(G, "encode_"), + Name, + ic_cbe:mk_c_type(G, N, Type), + RefStr]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, ~s oe_rec~s) {\n", + [ic_util:mk_oe_name(G, "encode_"), + Name, + ic_cbe:mk_c_type(G, N, Type), + RefStr]) + end, + + emit(Fd, " int oe_error_code = 0;\n",[]), + + {ok, RamFd1} = ram_file:open([], [binary, write]), + + case TypeDefFlag of + typedef -> + emit_encode(array, G, N, nil, RamFd1, {Name, Dim}, Type); + no_typedef -> + emit_encode(array_no_typedef, G, N, nil, RamFd1, {Name, Dim}, Type) + end, + + ic_cbe:emit_tmp_variables(Fd), + ic_codegen:nl(Fd), + %% Move data from ram file to output file. + {ok, Data1} = ram_file:get_file(RamFd1), + emit(Fd, Data1), + ram_file:close(RamFd1), + + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n",[]), + + put(op_variable_count, 0), + put(tmp_declarations, []), + + case TypeDefFlag of + typedef -> + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, char *, " + "int*, ~s);\n", + [ic_util:mk_oe_name(G, "decode_"), Name, Name]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, char *oe_first, " + "int* oe_outindex, ~s oe_out) {\n", + [ic_util:mk_oe_name(G, "decode_"), Name, Name]); + no_typedef -> + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, char *, int*, " + "~s oe_rec~s);\n", + [ic_util:mk_oe_name(G, "decode_"), + Name, + ic_cbe:mk_c_type(G, N, Type), + RefStr]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, char *oe_first, " + "int* oe_outindex, ~s oe_out~s) {\n", + [ic_util:mk_oe_name(G, "decode_"), + Name, + ic_cbe:mk_c_type(G, N, Type), + RefStr]) + end, + + emit(Fd, " int oe_error_code = 0;\n",[]), + emit(Fd, " int oe_array_size = 0;\n",[]), + + {ok, RamFd2} = ram_file:open([], [binary, write]), + + case TypeDefFlag of + typedef -> + emit_decode(array, G, N, nil, RamFd2, {Name, Dim}, Type); + no_typedef -> + emit_decode(array_no_typedef, G, N, nil, RamFd2, {Name, Dim}, Type) + end, + + + ic_cbe:emit_tmp_variables(Fd), + ic_codegen:nl(Fd), + %% Move data from ram file to output file. + {ok, Data2} = ram_file:get_file(RamFd2), + emit(Fd, Data2), + ram_file:close(RamFd2), + + emit(Fd, " *oe_outindex = ~s;\n\n",[align("*oe_outindex")]), + + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n",[]), + file:close(Fd). + + +get_refStr([]) -> + ""; +get_refStr([X|Xs]) -> + "[" ++ X ++ "]" ++ get_refStr(Xs). + + +emit_sequence_head_def(G, N, X, T, c) -> + %% T is the sequence + case ic_genobj:is_hrlfile_open(G) of + true -> + Fd = ic_genobj:hrlfiled(G), + SeqName = ic_util:to_undersc([ic_forms:get_id2(X) | N]), + emit(Fd, "\n#ifndef __~s__\n",[ic_util:to_uppercase(SeqName)]), + emit(Fd, "#define __~s__\n",[ic_util:to_uppercase(SeqName)]), + ic_codegen:mcomment_light(Fd, + [io_lib:format("Struct definition: ~s", + [SeqName])], + c), + emit(Fd, "typedef struct {\n"), + emit(Fd, " CORBA_unsigned_long _maximum;\n"), + emit(Fd, " CORBA_unsigned_long _length;\n"), + emit_seq_buffer(Fd, G, N, T#sequence.type), + emit(Fd, "} ~s;\n\n", [SeqName]), + create_c_struct_coding_file(G, N, X, T, SeqName, + T#sequence.type, sequence_head), + emit(Fd, "\n#endif\n\n"); + + false -> + ok + end. + +emit_seq_buffer(Fd, G, N, Type) -> + emit(Fd, " ~s* _buffer;\n", + [ic_cbe:mk_c_type(G, N, Type)]). + +%%------------------------------------------------------------ +%% +%% Emit decode bodies for functions in C for array, sequences and +%% structs. +%% +%%------------------------------------------------------------ +emit_decode(array, G, N, _T, Fd, {_Name, Dim}, Type) -> + emit(Fd, " if((char*) oe_out == oe_first)\n",[]), + AlignName = + lists:concat(["*oe_outindex + ", dim_multiplication(Dim), + " * sizeof(", ic_cbe:mk_c_type(G, N, Type),")"]), + emit(Fd, " *oe_outindex = ~s;\n\n",[align(AlignName)]), + array_decode_dimension_loop(G, N, Fd, Dim, "", Type, array); +emit_decode(array_no_typedef, G, N, _T, Fd, {_Name, Dim}, Type) -> + emit(Fd, " if((char*) oe_out == oe_first)\n",[]), + AlignName = + lists:concat(["*oe_outindex + ", dim_multiplication(Dim), + " * sizeof(", ic_cbe:mk_c_type(G, N, Type),")"]), + emit(Fd, " *oe_outindex = ~s;\n\n",[align(AlignName)]), + array_decode_dimension_loop(G, N, Fd, Dim, "", Type, array_no_typedef); +emit_decode(sequence_head, G, N, T, Fd, SeqName, ElType) -> + ic_cbe:store_tmp_decl(" int oe_seq_len = 0;\n", []), + ic_cbe:store_tmp_decl(" int oe_seq_count = 0;\n", []), + ic_cbe:store_tmp_decl(" int oe_seq_dummy = 0;\n", []), + + TmpBuf = + case ictype:isBasicTypeOrEterm(G, N, ElType) of + true -> + Tmp = "oe_seq_tmpbuf", + ic_cbe:store_tmp_decl(" char* ~s = 0;\n", [Tmp]), + Tmp; + false -> + "NOT USED" + end, + + MaxSize = get_seq_max(T), + emit(Fd, " if((char*) oe_out == oe_first)\n",[]), + emit(Fd, " *oe_outindex = ~s;\n\n", + [align(["*oe_outindex + sizeof(", SeqName, ")"])]), + + Ctype = ic_cbe:mk_c_type(G, N, ElType), + emit(Fd, " if ((oe_error_code = ei_decode_list_header(oe_env->_inbuf, " + "&oe_env->_iin, &oe_seq_len)) < 0) {\n"), + case ictype:isBasicTypeOrEterm(G, N, ElType) of + true -> + emit(Fd, " int oe_type = 0;\n"), + emit(Fd, " (int) ei_get_type(oe_env->_inbuf, &oe_env->_iin, " + "&oe_type, &oe_seq_len);\n\n"), + + if + MaxSize == infinity -> + ok; + true -> + emit(Fd, " if (oe_seq_len > ~w) {\n", [MaxSize]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, DATA_CONVERSION, " + "\"Length of sequence `~s' out of bound\");\n" + " return -1;\n }\n", [SeqName]) + end, + emit(Fd, " oe_out->_maximum = oe_seq_len;\n"), + emit(Fd, " oe_out->_length = oe_seq_len;\n"), + emit(Fd, " oe_out->_buffer = (void *) (oe_first + " + "*oe_outindex);\n"), + emit(Fd, " *oe_outindex = ~s;\n", + [align(["*oe_outindex + (sizeof(", Ctype, ") * " + "oe_out->_length)"])]), + emit(Fd, + " if ((~s = malloc(oe_seq_len + 1)) == NULL) {\n" + " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "NO_MEMORY, \"Cannot malloc\");\n" + " return -1;\n" + " }\n", [TmpBuf]), + emit(Fd, " if ((oe_error_code = ei_decode_string(" + "oe_env->_inbuf, &oe_env->_iin, ~s)) < 0) {\n", [TmpBuf]), + emit(Fd, " CORBA_free(~s);\n\n", [TmpBuf]), + emit_c_dec_rpt(Fd, " ", "string1", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " for (oe_seq_count = 0; " + "oe_seq_count < oe_out->_length; oe_seq_count++)\n"), + case ictype:isBasicType(G, N, ElType) of + true -> + emit(Fd, " oe_out->_buffer[oe_seq_count] = (unsigned char) " + "~s[oe_seq_count];\n\n", [TmpBuf]); + false -> %% Term + emit(Fd, " oe_out->_buffer[oe_seq_count] = " + "erl_mk_int(~s[oe_seq_count]);\n\n",[TmpBuf]) % XXXX What? + end, + emit(Fd, " CORBA_free(~s);\n\n", [TmpBuf]); + false -> + emit(Fd, " return oe_error_code;\n") + end, + + emit(Fd, " } else {\n"), + + if + MaxSize == infinity -> + ok; + true -> + emit(Fd, " if (oe_seq_len > ~w) {\n", [MaxSize]), + emit(Fd, " CORBA_exc_set(oe_env, " + "CORBA_SYSTEM_EXCEPTION, DATA_CONVERSION, " + "\"Length of sequence `~s' out of bound\");\n" + " return -1;\n }\n", [SeqName]) + end, + + emit(Fd, " oe_out->_maximum = oe_seq_len;\n"), + emit(Fd, " oe_out->_length = oe_seq_len;\n"), + emit(Fd, " oe_out->_buffer = (void *) (oe_first + *oe_outindex);\n"), + emit(Fd, " *oe_outindex = ~s;\n\n", + [align(["*oe_outindex + (sizeof(", Ctype, ") * oe_out->_length)"])]), + + if + Ctype == "CORBA_char *" -> + emit(Fd, " for (oe_seq_count = 0; " + "oe_seq_count < oe_out->_length; oe_seq_count++) {\n"), + emit(Fd, " oe_out->_buffer[oe_seq_count] = " + "(void*) (oe_first + *oe_outindex);\n\n"), + ic_cbe:emit_decoding_stmt(G, N, Fd, ElType, + "oe_out->_buffer[oe_seq_count]", + "", + "oe_env->_inbuf", 0, "", caller_dyn), + emit(Fd, " *oe_outindex = ~s;", + [align(["*oe_outindex + strlen(oe_out->_buffer[" + "oe_seq_count]) + 1"])]); + true -> + emit(Fd, " for (oe_seq_count = 0; " + "oe_seq_count < oe_out->_length; oe_seq_count++) {\n"), + case ictype:isArray(G, N, ElType) of + %% XXX Silly. There is no real difference between the + %% C statements produced by the following calls. + true -> + ic_cbe:emit_decoding_stmt(G, N, Fd, ElType, + "oe_out->_buffer[oe_seq_count]", + "", + "oe_env->_inbuf", + 0, "oe_outindex", generator); + false -> + ic_cbe:emit_decoding_stmt(G, N, Fd, ElType, + "oe_out->_buffer + oe_seq_count", + "", + "oe_env->_inbuf", + 0, "oe_outindex", generator) + end + end, + emit(Fd, " }\n"), + emit(Fd, " if (oe_out->_length != 0) {\n"), + emit(Fd, " if ((oe_error_code = ei_decode_list_header(" + "oe_env->_inbuf, &oe_env->_iin, &oe_seq_dummy)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_list_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " } else\n"), + emit(Fd, " oe_out->_buffer = NULL;\n"), + emit(Fd, " }\n"); + +emit_decode(struct, G, N, _T, Fd, StructName, ElTypes) -> + Length = length(ElTypes) + 1, + Tname = ic_cbe:mk_variable_name(op_variable_count), + Tname1 = ic_cbe:mk_variable_name(op_variable_count), + + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + ic_cbe:store_tmp_decl(" char ~s[256];\n\n",[Tname1]), + + emit(Fd, " if((char*) oe_out == oe_first)\n",[]), + AlignName = lists:concat(["*oe_outindex + sizeof(",StructName,")"]), + emit(Fd, " *oe_outindex = ~s;\n\n", [align(AlignName)]), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, " + "&oe_env->_iin, &~s)) < 0) {\n", [Tname]), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " if (~s != ~p) {\n",[Tname, Length]), + emit_c_dec_rpt(Fd, " ", "tuple header size != ~p", [Length]), + emit(Fd, " return -1;\n }\n"), + + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, " + "&oe_env->_iin, ~s)) < 0) {\n", [Tname1]), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if (strcmp(~s, ~p) != 0)\n",[Tname1, StructName]), + emit(Fd, " return -1;\n\n"), + lists:foreach( + fun({ET, EN}) -> + case ic_cbe:is_variable_size(G, N, ET) of + true -> + case ET of + + {struct, _, _, _} -> + %% Sequence member = a struct + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + ic_forms:get_id2(ET), + "&oe_out->" ++ EN, + "", "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + + {sequence, _, _} -> + %% Sequence member = a struct XXX ?? + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + EN, + "&oe_out->" ++ EN, + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + {_,{array, _, _}} -> + emit(Fd, " oe_out->~s = (void *) " + "(oe_first+*oe_outindex);\n\n",[EN]), + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + EN, "oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + + {union, _, _, _, _} -> + %% Sequence member = a union + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + ic_forms:get_id2(ET), + "&oe_out->" ++ EN, + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + + {string,_} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator_malloc); + + {scoped_id,_,_,_} -> + case ictype:member2type(G,StructName,EN) of + array -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + struct -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ + EN , + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + sequence -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + union -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + _ -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator) + end; + + _ -> + emit(Fd, " oe_out->~s = (void *) " + "(oe_first+*oe_outindex);\n\n",[EN]), + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, "oe_outindex", + generator) + end; + false -> + case ET of + + {struct, _, _, _} -> + %% A struct member + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + ic_forms:get_id2(ET), + "&oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + + {_,{array, _, _}} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + EN, + "oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + + {union, _, _, _, _} -> + %% Sequence member = a union + ic_cbe:emit_decoding_stmt(G, N, Fd, + StructName ++ "_" ++ + ic_forms:get_id2(ET), + "&oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + + {_,_} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ EN , + "", + "oe_env->_inbuf", + 0, + "oe_outindex", + generator); + {scoped_id,_,_,_} -> + case ic_symtab:get_full_scoped_name(G, N, ET) of + {_FullScopedName, _, {tk_array,_,_}, _} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + {_FullScopedName, _, {tk_string,_}, _} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + {_FullScopedName, _, {tk_struct,_,_,_}, _} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + + {_FullScopedName, _, + {tk_union,_,_,_,_,_}, _} -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator); + + _ -> + ic_cbe:emit_decoding_stmt(G, N, Fd, + ET, + "&oe_out->" ++ + EN, + "", + "oe_env->" + "_inbuf", + 0, + "oe_outindex", + generator) + end + end + end + end, + ElTypes). + + +ref_array_static_dec(array, true) -> + %% Typedef, Static, Basic Type + "&(oe_out)"; +ref_array_static_dec(array, false) -> + %% Typedef, Static, Constr Type + "&(oe_out)"; +ref_array_static_dec(array_no_typedef, true) -> + %% No Typedef, Static, Basic Type + "&oe_out"; +ref_array_static_dec(array_no_typedef, false) -> + %% No Typedef, Static, Constr Type + "&oe_out". + + +ref_array_dynamic_dec(G, N, T, array) -> + case ictype:isString(G, N, T) of + true -> % Typedef, Dynamic, String + "oe_out"; + false -> % Typedef, Dynamic, No String + "&(oe_out)" + end; +ref_array_dynamic_dec(G, N, T, array_no_typedef) -> + case ictype:isString(G, N, T) of + true -> % No Typedef, Dynamic, String + "oe_out"; + false -> % No Typedef, Dynamic, No String + "&oe_out" + end. + + + +array_decode_dimension_loop(G, N, Fd, [Dim], Dimstr, Type, TDFlag) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, " + "&oe_env->_iin, &oe_array_size)) < 0) {\n", + []), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + %% This is disabled due to a bug in erl_interface : + %% tuples inside tuples hae no correct data about the size + %% of the tuple........( allways = 0 ) + %%emit(Fd, " if (oe_array_size != ~s)\n",[Dim]), + %%emit(Fd, " return -1;\n\n"), + + emit(Fd, " for (~s = 0; ~s < ~s; ~s++) {\n", + [Tname, Tname, Dim, Tname]), + + + ArrAccess = + case ic_cbe:is_variable_size(G, N, Type) of + true -> + ref_array_dynamic_dec(G, N, Type, TDFlag) ++ + Dimstr ++ "[" ++ Tname ++ "]"; + false -> + ref_array_static_dec(TDFlag, ictype:isBasicType(G,N,Type)) ++ + Dimstr ++ "[" ++ Tname ++ "]" + end, + + ic_cbe:emit_decoding_stmt(G, N, Fd, Type, + ArrAccess, + "", "oe_env->_inbuf", 0, + "oe_outindex", generator), + + %% emit(Fd, "\n *oe_outindex += + %% sizeof(~s);\n",[ic_cbe:mk_c_type(G, N, Type)]), + emit(Fd, " }\n"); +array_decode_dimension_loop(G, N, Fd, [Dim | Ds], _Dimstr, Type, TDFlag) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, " + "&oe_env->_iin, &oe_array_size)) < 0) {\n", + []), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + %% This is disabled due to a bug in erl_interface : + %% tuples inside tuples hae no correct data about the size + %% of the tuple........( allways = 0 ) + %%emit(Fd, " if (oe_array_size != ~s)\n",[Dim]), + %%emit(Fd, " return -1;\n\n"), + + emit(Fd, " for (~s = 0; ~s < ~s; ~s++) {\n", + [Tname, Tname, Dim, Tname]), + array_decode_dimension_loop(G, N, Fd, Ds, "[" ++ Tname ++ "]" , Type, + TDFlag), + + emit(Fd, " }\n"). + +dim_multiplication([D]) -> + D; +dim_multiplication([D |Ds]) -> + D ++ "*" ++ dim_multiplication(Ds). + +emit_encode(array, G, N, _T, Fd, {_Name, Dim}, Type) -> + array_encode_dimension_loop(G, N, Fd, Dim, {"",""}, Type, array); +emit_encode(array_no_typedef, G, N, _T, Fd, {_Name, Dim}, Type) -> + array_encode_dimension_loop(G, N, Fd, Dim, {"",""}, Type, + array_no_typedef); +emit_encode(sequence_head, G, N, T, Fd, SeqName, ElType) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + ic_cbe:store_tmp_decl(" int ~s = 0;\n\n",[Tname]), + + MaxSize = get_seq_max(T), + if + MaxSize == infinity -> + ok; + true -> + emit(Fd, " if (oe_rec->_length > ~w) {\n", [MaxSize]), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "DATA_CONVERSION, \"Length of sequence `~s' " + "out of bound\");\n" + " return -1;\n }\n", [SeqName]) + end, + + emit(Fd, " if (oe_rec->_length != 0) {\n"), + + emit(Fd, " if ((oe_error_code = oe_ei_encode_list_header(oe_env, " + "oe_rec->_length)) < 0) {\n", + []), + emit_c_enc_rpt(Fd, " ", "oi_ei_encode_list_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " for (~s = 0; ~s < oe_rec->_length; ~s++) {\n", + [Tname, Tname, Tname]), + case ElType of + {_,_} -> %% ElType = elementary type or pointer type + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, "oe_rec->_buffer[" ++ + Tname ++ "]", "oe_env->_outbuf"); + + {scoped_id,local,_,["term","erlang"]} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, "oe_rec->_buffer[" ++ + Tname ++ "]", "oe_env->_outbuf"); + + {scoped_id,_,_,_} -> + case ic_symtab:get_full_scoped_name(G, N, ElType) of + {_, typedef, TDef, _} -> + case TDef of + {tk_struct,_,_,_} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "&oe_rec->_buffer[" ++ + Tname ++ "]", + "oe_env->_outbuf"); + {tk_sequence,_,_} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "&oe_rec->_buffer[" ++ + Tname ++ "]", + "oe_env->_outbuf"); + {tk_union,_,_,_,_,_} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "&oe_rec->_buffer[" ++ + Tname ++ "]", + "oe_env->_outbuf"); + _ -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "oe_rec->_buffer[" ++ + Tname ++ "]", + "oe_env->_outbuf") + end; + {_,enum,_,_} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "oe_rec->_buffer[" ++ + Tname ++ "]", + "oe_env->_outbuf"); + _ -> + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "&oe_rec->_buffer[" ++ + Tname ++ "]", + "oe_env->_outbuf") + end; + + _ -> %% ElType = structure + ic_cbe:emit_encoding_stmt(G, N, Fd, ElType, + "&oe_rec->_buffer[" ++ Tname ++ "]", + "oe_env->_outbuf") + end, + emit(Fd, " }\n"), + emit(Fd, " }\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_empty_list(oe_env)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_empty_list", []), + emit(Fd, " return oe_error_code;\n }\n"); +emit_encode(struct, G, N, _T, Fd, StructName, ElTypes) -> + Length = length(ElTypes) + 1, + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_tuple_header(oe_env, ~p)) < 0) {\n", [Length]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_atom(oe_env, ~p)) < 0) {\n", [StructName]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + lists:foreach( + fun({ET, EN}) -> + case ET of + {sequence, _, _} -> + %% Sequence = struct + ic_cbe:emit_encoding_stmt(G, N, Fd, + StructName ++ "_" ++ EN, + "&oe_rec->" ++ EN, + "oe_env->_outbuf"); + {_,{array, _, _Dims}} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + StructName ++ "_" ++ EN, + "oe_rec->" ++ EN, + "oe_env->_outbuf"); + + {union,_,_,_,_} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + StructName ++ "_" ++ + ic_forms:get_id2(ET), + "&oe_rec->" ++ EN, + "oe_env->_outbuf"); + + {struct,_,_,_} -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + StructName ++ "_" ++ + ic_forms:get_id2(ET), + "&oe_rec->" ++ EN, + "oe_env->_outbuf"); + + {scoped_id,_,_,_} -> + case ictype:member2type(G,StructName,EN) of + struct -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + ET, + "&oe_rec->" ++ EN, + "oe_env->_outbuf"); + sequence -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + ET, + "&oe_rec->" ++ EN, + "oe_env->_outbuf"); + union -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + ET, + "&oe_rec->" ++ EN, + "oe_env->_outbuf"); + array -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + ET, + "oe_rec->" ++ EN, + "oe_env->_outbuf"); + _ -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + ET, + "oe_rec->" ++ EN, + "oe_env->_outbuf") + end; + _ -> + ic_cbe:emit_encoding_stmt(G, N, Fd, + ET, + "oe_rec->" ++ EN, + "oe_env->_outbuf") + end + end, + ElTypes). + +ref_array_static_enc(array, true) -> + %% Typedef, Static, Basic Type + "oe_rec"; +ref_array_static_enc(array, false) -> + %% Typedef, Static, Constr Type + "&(oe_rec)"; +ref_array_static_enc(array_no_typedef, true) -> + %% No Typedef, Static, Basic Type + "oe_rec"; +ref_array_static_enc(array_no_typedef, false) -> + %% No Typedef, Static, Constr Type + "&oe_rec". + + +ref_array_dynamic_enc(G, N, T, array) -> + case ictype:isString(G, N, T) of + true -> % Typedef, Dynamic, String + "oe_rec"; + false -> % Typedef, Dynamic, No String + "&(oe_rec)" + end; +ref_array_dynamic_enc(G, N, T, array_no_typedef) -> + case ictype:isString(G, N, T) of + true -> % No Typedef, Dynamic, String + "oe_rec"; + false -> % No Typedef, Dynamic, No String + "&oe_rec" + end. + + + +array_encode_dimension_loop(G, N, Fd, [Dim], {Str1,_Str2}, Type, TDFlag) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_tuple_header(oe_env, ~s)) < 0) {\n", [Dim]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " for (~s = 0; ~s < ~s; ~s++) {\n", + [Tname, Tname, Dim, Tname]), + + ArrAccess = + case ic_cbe:is_variable_size(G, N, Type) of + true -> + ref_array_dynamic_enc(G, N, Type, TDFlag) ++ + Str1 ++ "[" ++ Tname ++ "]"; + false -> + ref_array_static_enc(TDFlag, ictype:isBasicType(G,N,Type)) ++ + Str1 ++ "[" ++ Tname ++ "]" + end, + + ic_cbe:emit_encoding_stmt(G, N, Fd, Type, ArrAccess, "oe_env->_outbuf"), + emit(Fd, " }\n"); +array_encode_dimension_loop(G, N, Fd, [Dim | Ds],{Str1,Str2}, Type, TDFlag) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + + emit(Fd, " if ((oe_error_code = " + "oe_ei_encode_tuple_header(oe_env, ~s)) < 0) {\n", [Dim]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " for (~s = 0; ~s < ~s; ~s++) {\n", + [Tname, Tname, Dim, Tname]), + array_encode_dimension_loop(G, N, Fd, Ds, + {Str1 ++ "[" ++ Tname ++ "]", Str2}, + Type, TDFlag), + emit(Fd, " }\n"). + + +emit_sizecount(array, G, N, _T, Fd, {_Name, Dim}, Type) -> + emit(Fd, " if(*oe_size == 0)\n",[]), + AlignName = lists:concat(["*oe_size + ", dim_multiplication(Dim), + " * sizeof(", ic_cbe:mk_c_type(G, N, Type),")"]), + emit(Fd, " *oe_size = ~s;\n\n",[align(AlignName)]), + array_size_dimension_loop(G, N, Fd, Dim, Type), + emit(Fd, " *oe_size = ~s;\n\n", + [align("*oe_size + oe_malloc_size")]), + ic_codegen:nl(Fd); + +emit_sizecount(sequence_head, G, N, T, Fd, SeqName, ElType) -> + ic_cbe:store_tmp_decl(" int oe_seq_len = 0;\n", []), + ic_cbe:store_tmp_decl(" int oe_seq_count = 0;\n", []), + + emit(Fd, " if(*oe_size == 0)\n",[]), + emit(Fd, " *oe_size = ~s;\n\n", + [align(["*oe_size + sizeof(", SeqName, ")"])]), + + MaxSize = get_seq_max(T), + + emit(Fd, " if ((oe_error_code = ei_get_type(oe_env->_inbuf, " + "oe_size_count_index, &oe_type, &oe_seq_len)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + + if + MaxSize == infinity -> + ok; + true -> + emit(Fd, " if (oe_seq_len > ~w) {\n", [MaxSize]), + emit(Fd, " CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, " + "DATA_CONVERSION, \"Length of sequence `~s' " + "out of bound\");\n" + " return -1;\n }\n", [SeqName]) + end, + + CType = ic_cbe:mk_c_type(G, N, ElType), + + emit(Fd, " if ((oe_error_code = ei_decode_list_header(oe_env->_inbuf, " + "oe_size_count_index, NULL)) < 0) {\n"), + + case ictype:isBasicTypeOrEterm(G, N, ElType) of + true -> + emit(Fd, " if ((oe_error_code = ei_decode_string(oe_env->" + "_inbuf, oe_size_count_index, NULL)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_string", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " oe_malloc_size = ~s;\n\n", + [align(["sizeof(", CType, ") * oe_seq_len"])]); + false -> + emit_c_dec_rpt(Fd, " ", "non mea culpa", []), + emit(Fd, " return oe_error_code;\n\n") + end, + + emit(Fd, " } else {\n"), + + emit(Fd, " oe_malloc_size = ~s;\n\n", + [align(["sizeof(", CType, ") * oe_seq_len"])]), + + emit(Fd, " for (oe_seq_count = 0; oe_seq_count < oe_seq_len; " + "oe_seq_count++) {\n"), + ic_cbe:emit_malloc_size_stmt(G, N, Fd, ElType, + "oe_env->_inbuf", 0, generator), + emit(Fd, " }\n"), + + emit(Fd, " if (oe_seq_len != 0) \n"), + emit(Fd, " if ((oe_error_code = ei_decode_list_header(oe_env->_inbuf," + "oe_size_count_index, NULL)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_list_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " }\n"), + emit(Fd, " *oe_size = ~s;\n\n", [align("*oe_size + oe_malloc_size")]); + +emit_sizecount(struct, G, N, _T, Fd, StructName, ElTypes) -> + Length = length(ElTypes) + 1, + Tname = ic_cbe:mk_variable_name(op_variable_count), + ic_cbe:store_tmp_decl(" int ~s = 0;\n\n",[Tname]), + + emit(Fd, " if(*oe_size == 0)\n",[]), + AlignName = lists:concat(["*oe_size + sizeof(",StructName,")"]), + emit(Fd, " *oe_size = ~s;\n\n", [align(AlignName)]), + ic_codegen:nl(Fd), + + emit(Fd, " if ((oe_error_code = " + "ei_get_type(oe_env->_inbuf, oe_size_count_index, &oe_type, " + "&~s)) < 0) {\n", [Tname]), + emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " if (~s != ~p) {\n",[Tname, Length]), + emit_c_dec_rpt(Fd, " ", "~s != ~p", [Tname, Length]), + emit(Fd, " return -1;\n }\n"), + + + emit(Fd, " if ((oe_error_code = " + "ei_decode_tuple_header(oe_env->_inbuf, " + "oe_size_count_index, 0)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if ((oe_error_code = " + "ei_decode_atom(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n", []), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + lists:foreach( + fun({ET, EN}) -> + case ic_cbe:is_variable_size(G, N, ET) of + true -> + case ET of + {sequence, _, _} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ EN, + "oe_env->_inbuf", + 0, + generator); + {_,{array, _, _}} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ EN, + "oe_env->_inbuf", + 0, + generator); + {union,_,_,_,_} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ ic_forms:get_id2(ET), + "oe_env->_inbuf", + 0, + generator); + + {struct,_,_,_} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ ic_forms:get_id2(ET), + "oe_env->_inbuf", + 0, + generator); + + _ -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + ET, + "oe_env->_inbuf", + 0, + generator) + end; + false -> + case ET of + {_,{array, _, _}} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ EN, + "oe_env->_inbuf", + 0, + generator); + + {union,_,_,_,_} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ ic_forms:get_id2(ET), + "oe_env->_inbuf", + 0, + generator); + + {struct,_,_,_} -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + StructName ++ "_" ++ ic_forms:get_id2(ET), + "oe_env->_inbuf", + 0, + generator); + _ -> + ic_cbe:emit_malloc_size_stmt( + G, N, Fd, + ET, + "oe_env->_inbuf", + 1, + generator) + end + end + end, + ElTypes), + + emit(Fd, " *oe_size = ~s;\n\n", + [align("*oe_size + oe_malloc_size")]). + + +array_size_dimension_loop(G, N, Fd, [Dim], Type) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + emit(Fd, " if ((oe_error_code = " + "ei_get_type(oe_env->_inbuf, oe_size_count_index, " + "&oe_type, &oe_array_size)) < 0) {\n", + []), + emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " if (oe_array_size != ~s) {\n",[Dim]), + emit_c_dec_rpt(Fd, " ", "array size != ~s", [Dim]), + emit(Fd, " return -1;\n }\n"), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, " + "oe_size_count_index, 0)) < 0) {\n", []), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " for (~s = 0; ~s < ~s; ~s++) {\n", + [Tname, Tname, Dim, Tname]), + ic_cbe:emit_malloc_size_stmt(G, N, Fd, + Type, "oe_env->_inbuf", 0, generator), + emit(Fd, " }\n"); +array_size_dimension_loop(G, N, Fd, [Dim | Ds], Type) -> + Tname = ic_cbe:mk_variable_name(op_variable_count), + + ic_cbe:store_tmp_decl(" int ~s = 0;\n",[Tname]), + emit(Fd, " if ((oe_error_code = " + "ei_get_type(oe_env->_inbuf, oe_size_count_index, " + "&oe_type, &oe_array_size)) < 0) {\n", []), + emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " if (oe_array_size != ~s) {\n",[Dim]), + emit_c_dec_rpt(Fd, " ", "array size != ~s", [Dim]), + emit(Fd, " return -1;\n }\n"), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, " + "oe_size_count_index, 0)) < 0) {\n", + []), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " for (~s = 0; ~s < ~s; ~s++) {\n", + [Tname, Tname, Dim, Tname]), + array_size_dimension_loop(G, N, Fd, Ds, Type), + emit(Fd, " }\n"). + + +create_c_struct_coding_file(G, N, _X, T, StructName, ElTypes, StructType) -> + + {Fd , SName} = open_c_coding_file(G, StructName), % stub file + HFd = ic_genobj:hrlfiled(G), % stub header file + HrlFName = filename:basename(ic_genobj:include_file(G)), + + ic_codegen:emit_stub_head(G, Fd, SName, c), + HrlFName = filename:basename(ic_genobj:include_file(G)), + emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + + %% Size count + + put(op_variable_count, 0), + put(tmp_declarations, []), + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, int*, int*);\n", + [ic_util:mk_oe_name(G, "sizecalc_"), StructName]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, " + "int* oe_size_count_index, int* oe_size)\n{\n", + [ic_util:mk_oe_name(G, "sizecalc_"), StructName]), + + emit(Fd, " int oe_malloc_size = 0;\n",[]), + emit(Fd, " int oe_error_code = 0;\n",[]), + emit(Fd, " int oe_type = 0;\n",[]), + + {ok, RamFd} = ram_file:open([], [binary, write]), + + emit_sizecount(StructType, G, N, T, RamFd, StructName, ElTypes), + + ic_cbe:emit_tmp_variables(Fd), + ic_codegen:nl(Fd), + %% Move data from ram file to output file. + {ok, Data} = ram_file:get_file(RamFd), + emit(Fd, Data), + ram_file:close(RamFd), + + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n\n",[]), + + %% Encode + + put(op_variable_count, 0), + put(tmp_declarations, []), + + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, ~s*);\n", + [ic_util:mk_oe_name(G, "encode_"), StructName, StructName]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, ~s* oe_rec)\n{\n", + [ic_util:mk_oe_name(G, "encode_"), StructName, StructName]), + + emit(Fd, " int oe_error_code = 0;\n",[]), + + {ok, RamFd1} = ram_file:open([], [binary, write]), + + emit_encode(StructType, G, N, T, RamFd1, StructName, ElTypes), + + ic_cbe:emit_tmp_variables(Fd), + ic_codegen:nl(Fd), + %% Move data from ram file to output file. + {ok, Data1} = ram_file:get_file(RamFd1), + emit(Fd, Data1), + ram_file:close(RamFd1), + + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n\n",[]), + + %% Decode + + put(op_variable_count, 0), + put(tmp_declarations, []), + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, char *, int*, ~s *);\n", + [ic_util:mk_oe_name(G, "decode_"), StructName, StructName]), + + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, char *oe_first, " + "int* oe_outindex, " + "~s *oe_out)\n{\n", + [ic_util:mk_oe_name(G, "decode_"), StructName, StructName]), + + emit(Fd, " int oe_error_code = 0;\n",[]), + + {ok, RamFd2} = ram_file:open([], [binary, write]), + + emit_decode(StructType, G, N, T, RamFd2, StructName, ElTypes), + + ic_cbe:emit_tmp_variables(Fd), + ic_codegen:nl(Fd), + %% Move data from ram file to output file. + {ok, Data2} = ram_file:get_file(RamFd2), + emit(Fd, Data2), + ram_file:close(RamFd2), + + emit(Fd, " *oe_outindex = ~s;\n",[align("*oe_outindex")]), + emit(Fd, " return 0;\n\n",[]), + emit(Fd, "}\n\n",[]), + file:close(Fd). + + +%%------------------------------------------------------------ +%% +%% emit primitive for unions. +%% +%%------------------------------------------------------------ +emit_union(G, N, X, erlang) -> + case ic_genobj:is_hrlfile_open(G) of + true -> + ic_codegen:record(G, X, + ic_util:to_undersc([ic_forms:get_id2(X) | N]), + nil,nil), + mkFileRecObj(G,N,X,erlang); + false -> ok + end; +emit_union(_G, _N, _X, c) -> %% Not supported in c backend + true. + + +%%------------------------------------------------------------ +%% +%% emit erlang modules for objects with record definitions +%% (such as unions or structs), or sequences +%% +%% The record files, other than headers are only generated +%% for CORBA...... If wished an option could allows even +%% for other backends ( not necessary anyway ) +%% +%%------------------------------------------------------------ +mkFileRecObj(G,N,X,erlang) -> + case ic_options:get_opt(G, be) of + erl_corba -> + SName = + ic_util:to_undersc([ic_forms:get_id2(X) | N]), + FName = + ic_file:join(ic_options:get_opt(G, stubdir), + ic_file:add_dot_erl(SName)), + + case file:open(FName, [write]) of + {ok, Fd} -> + HrlFName = filename:basename(ic_genobj:include_file(G)), + + ic_codegen:emit_stub_head(G, Fd, SName, erlang), + emit(Fd, "-include(~p).\n\n",[HrlFName]), + emit_exports(G,Fd), + emit_rec_methods(G,N,X,SName,Fd), + ic_codegen:nl(Fd), + ic_codegen:nl(Fd), + file:close(Fd); + Other -> + exit(Other) + end; + _ -> + true + end. + + +%%------------------------------------------------------------ +%% +%% emit erlang modules for objects with array definitions.. +%% +%%------------------------------------------------------------ +mkFileArrObj(G,N,X,erlang) -> + SName = + ic_util:to_undersc([ic_forms:get_id2(X) | N]), + FName = + ic_file:join(ic_options:get_opt(G, stubdir), + ic_file:add_dot_erl(SName)), + + case file:open(FName, [write]) of + {ok, Fd} -> + HrlFName = filename:basename(ic_genobj:include_file(G)), + + ic_codegen:emit_stub_head(G, Fd, SName, erlang), + emit(Fd, "-include(~p).\n\n",[HrlFName]), + emit_exports(G,Fd), + emit_arr_methods(G,N,X,SName,Fd), + ic_codegen:nl(Fd), + ic_codegen:nl(Fd), + file:close(Fd); + Other -> + exit(Other) + end. + + + + +%%------------------------------------------------------------ +%% +%% emit exports for erlang modules which represent records. +%% +%%------------------------------------------------------------ +emit_exports(G,Fd) -> + case ic_options:get_opt(G, be) of + erl_corba -> + emit(Fd, "-export([tc/0,id/0,name/0]).\n\n\n\n",[]); + _ -> + emit(Fd, "-export([id/0,name/0]).\n\n\n\n",[]) + end. + + +%%------------------------------------------------------------ +%% +%% emit erlang module functions which represent records, yields +%% record information such as type code, identity and name. +%% +%%------------------------------------------------------------ +emit_rec_methods(G,N,X,Name,Fd) -> + + IR_ID = ictk:get_IR_ID(G, N, X), + + case ic_options:get_opt(G, be) of + + erl_corba -> + TK = ic_forms:get_tk(X), + + case TK of + undefined -> + STK = ic_forms:search_tk(G,ictk:get_IR_ID(G, N, X)), + emit(Fd, "%% returns type code\n",[]), + emit(Fd, "tc() -> ~p.\n\n",[STK]), + emit(Fd, "%% returns id\n",[]), + emit(Fd, "id() -> ~p.\n\n",[IR_ID]), + emit(Fd, "%% returns name\n",[]), + emit(Fd, "name() -> ~p.\n\n",[Name]); + _ -> + emit(Fd, "%% returns type code\n",[]), + emit(Fd, "tc() -> ~p.\n\n",[TK]), + emit(Fd, "%% returns id\n",[]), + emit(Fd, "id() -> ~p.\n\n",[IR_ID]), + emit(Fd, "%% returns name\n",[]), + emit(Fd, "name() -> ~p.\n\n",[Name]) + end; + + _ -> + emit(Fd, "%% returns id\n",[]), + emit(Fd, "id() -> ~p.\n\n",[IR_ID]), + emit(Fd, "%% returns name\n",[]), + emit(Fd, "name() -> ~p.\n\n",[Name]) + end. + + + +%%------------------------------------------------------------ +%% +%% emit erlang module functions which represent arrays, yields +%% record information such as type code, identity and name. +%% +%%------------------------------------------------------------ +emit_arr_methods(G,N,X,Name,Fd) -> + + IR_ID = ictk:get_IR_ID(G, N, X), + + case ic_options:get_opt(G, be) of + + erl_corba -> + + TK = ic_forms:get_type_code(G, N, X), + + emit(Fd, "%% returns type code\n",[]), + emit(Fd, "tc() -> ~p.\n\n",[TK]), + emit(Fd, "%% returns id\n",[]), + emit(Fd, "id() -> ~p.\n\n",[IR_ID]), + emit(Fd, "%% returns name\n",[]), + emit(Fd, "name() -> ~p.\n\n",[Name]); + + _ -> + + emit(Fd, "%% returns id\n",[]), + emit(Fd, "id() -> ~p.\n\n",[IR_ID]), + emit(Fd, "%% returns name\n",[]), + emit(Fd, "name() -> ~p.\n\n",[Name]) + end. + +get_seq_max(T) when is_record(T, sequence) andalso T#sequence.length == 0 -> + infinity; +get_seq_max(T) when is_record(T, sequence) andalso is_tuple(T#sequence.length) -> + list_to_integer(element(3, T#sequence.length)). + + +align(Cs) -> + ic_util:mk_align(Cs). + diff --git a/lib/ic/src/ictk.erl b/lib/ic/src/ictk.erl new file mode 100644 index 0000000000..63a7705699 --- /dev/null +++ b/lib/ic/src/ictk.erl @@ -0,0 +1,873 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ictk). + + +%% Toplevel generation functions +-export([reg_gen/3, unreg_gen/3]). + + +%% Utilities +-export([get_IR_ID/3, get_IR_VSN/3, register_name/1, unregister_name/1]). + +-import(ic_forms, [get_id2/1, get_body/1, get_idlist/1]). +-import(ic_util, [mk_name/2, mk_oe_name/2, to_atom/1, to_list/1]). +-import(ic_codegen, [emit/2, emit/3, nl/1]). + +-include("icforms.hrl"). +-include("ic.hrl"). + +%%-------------------------------------------------------------------- +%% +%% IFR Registration Generation +%% +%% +%%-------------------------------------------------------------------- + +-define(IFRID(G), mk_name(G, "IFR")). +-define(VARID(G), mk_name(G, "VAR")). +-define(IFRMOD, orber_ifr). + +reg_gen(G, N, X) -> + S = ic_genobj:tktab(G), + Light = ic_options:get_opt(G, light_ifr), + init_var(), + case ic_genobj:is_stubfile_open(G) of + true when Light == false -> + Var = ?IFRID(G), + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), nl(Fd), + emit(Fd, "~p() ->\n", [to_atom(register_name(G))]), + emit(Fd, " ~s = ~p:find_repository(),\n", + [Var, ?IFRMOD]), + nl(Fd), + + %% Write call function that checks if included + %% modules and interfaces are created. + emit(Fd, " register_tests(~s),\n",[?IFRID(G)]), + + reg2(G, S, N, Var, X), + nl(Fd), + emit(Fd, " ok.\n"), + + %% Write general register test function. + register_tests(Fd,G), + + %% Write functopn that registers modules only if + %% they are not registered. + register_if_unregistered(Fd); + true when Light == true -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), nl(Fd), + Regname = to_atom(register_name(G)), + emit(Fd, "~p() ->\n\t~p([]).\n\n", [Regname, Regname]), + emit(Fd, "~p(OE_Options) ->\n\t~p:add_items(?MODULE, OE_Options,\n\t[", + [Regname, ?IFRMOD]), + reg_light(G, N, X), + emit(Fd, "ok]),\n\tok.\n"); + false -> + ok + end. + +reg_light(G, N, X) when is_list(X) -> + reg_light_list(G, N, X); +reg_light(G, N, X) when is_record(X, module) -> + reg_light_list(G, [get_id2(X) | N], get_body(X)); +reg_light(G, N, X) when is_record(X, struct) -> + emit(ic_genobj:stubfiled(G), "{~p, ~p, struct},\n\t", + [get_IR_ID(G, N, X), get_module(X, N)]); +reg_light(G, N, X) when is_record(X, except) -> + emit(ic_genobj:stubfiled(G), "{~p, ~p, except},\n\t", + [get_IR_ID(G, N, X), get_module(X, N)]); +reg_light(G, N, X) when is_record(X, union) -> + emit(ic_genobj:stubfiled(G), "{~p, ~p, union},\n\t", + [get_IR_ID(G, N, X), get_module(X, N)]); +reg_light(G, N, X) when is_record(X, interface) -> + emit(ic_genobj:stubfiled(G), "{~p, ~p, interface},\n\t", + [get_IR_ID(G, N, X), get_module(X, N)]), + reg_light_list(G, [get_id2(X)|N], get_body(X)); +reg_light(_G, _N, _X) -> + ok. + +get_module(X, N) -> + List = [get_id2(X) | N], + list_to_atom(lists:foldl(fun(E, Acc) -> E++"_"++Acc end, + hd(List), tl(List))). + +%% This function filters off all "#include <FileName>.idl" code that +%% come along from preprocessor and scanner. Produces code ONLY for +%% the actuall file. See ticket OTP-2133 +reg_light_list(_G, _N, []) -> []; +reg_light_list(G, N, List ) -> + CurrentFileName = ic_genobj:idlfile(G), + reg_light_list(G, N, {CurrentFileName,true}, List). + +%% The filter function + loop +reg_light_list(_G, _N, {_CFN, _Status}, []) -> []; +reg_light_list(G, N, {CFN,Status}, [X | Xs]) -> + case Status of + true -> + case X of + {preproc,_,{_,_,_FileName},[{_,_,"1"}]} -> + reg_light_list(G, N, {CFN,false}, Xs); + _ -> + reg_light(G, N, X), + reg_light_list(G, N, {CFN,Status}, Xs) + end; + false -> + case X of + {preproc,_,{_,_,CFN},[{_,_,"2"}]} -> + reg_light(G, N, X), + reg_light_list(G, N, {CFN,true}, Xs); + _ -> + reg_light_list(G, N, {CFN,Status}, Xs) + end + end. + + +%% reg2 is top level registration + +reg2(G, S, N, Var, X) -> + reg2(G, S, N, "Repository_create_", Var, X). + +reg2(G, S, N, C, V, X) when is_list(X) -> reg2_list(G, S, N, C, V, X); + +reg2(G, S, N, C, V, X) when is_record(X, module) -> + NewV = r_emit2(G, S, N, C, V, X, "", []), + reg2_list(G, S, [get_id2(X) | N], "ModuleDef_create_", NewV, get_body(X)); + +reg2(G, S, N, C, V, X) when is_record(X, const) -> + r_emit2(G, S, N, C, V, X, ", ~s, ~p", + [get_idltype(G, S, N, X), {X#const.tk, X#const.val}]); + +reg2(G, S, N, C, V, X) when is_record(X, struct) -> + do_struct(G, S, N, C, V, X, ic_forms:get_tk(X)); + +reg2(G, S, N, C, V, X) when is_record(X, except) -> + do_except(G, S, N, C, V, X, ic_forms:get_tk(X)); + +reg2(G, S, N, C, V, X) when is_record(X, union) -> + do_union(G, S, N, C, V, X, ic_forms:get_tk(X)); + +reg2(G, S, N, C, V, X) when is_record(X, enum) -> + r_emit2(G, S, N, C, V, X, ", ~p", + [get_enum_member_list(G, S, N, get_body(X))]); + +reg2(G, S, N, C, V, X) when is_record(X, typedef) -> + do_typedef(G, S, N, C, V, X), + look_for_types(G, S, N, C, V, get_body(X)); + +reg2(G, S, N, C, V, X) when is_record(X, attr) -> + XX = #id_of{type=X}, + lists:foreach(fun(Id) -> r_emit2(G, S, N, C, V, XX#id_of{id=Id}, ", ~s, ~p", + [get_idltype(G, S, N, X), get_mode(G, N, X)]) + end, + get_idlist(X)); + +reg2(G, S, N, C, V, X) when is_record(X, interface) -> + N2 = [get_id2(X) | N], + Body = get_body(X), + BIs = get_base_interfaces(G,X), %% produce code for the interface inheritance + NewV = r_emit2(G, S, N, C, V, X, ", " ++ BIs,[]), + reg2_list(G, S, N2, "InterfaceDef_create_", NewV, Body); + + +reg2(G, S, N, C, V, X) when is_record(X, op) -> + r_emit2(G, S, N, C, V, X, ", ~s, ~p, [~s], [~s], ~p", + [get_idltype(G, S, N, X), get_mode(G, N, X), + get_params(G, S, N, X#op.params), get_exceptions(G, S, N, X), + get_context(G, S, N, X)]); + +reg2(_G, _S, _N, _C, _V, X) when is_record(X, preproc) -> ok; + +reg2(_G, _S, _N, _C, _V, X) when is_record(X, pragma) -> ok; + +reg2(_G, _S, _N, _C, _V, _X) -> ok. + + +%% This function filters off all "#include <FileName>.idl" code that +%% come along from preprocessor and scanner. Produces code ONLY for +%% the actuall file. See ticket OTP-2133 +reg2_list(_G, _S, _N, _C, _V, []) -> []; +reg2_list(G, S, N, C, V, List ) -> + CurrentFileName = ic_genobj:idlfile(G), + reg2_list(G, S, N, C, V, {CurrentFileName,true}, List). + +%% The filter function + loop +reg2_list(_G, _S, _N, _C, _V, {_CFN, _Status}, []) -> []; +reg2_list(G, S, N, C, V, {CFN,Status}, [X | Xs]) -> + case Status of + true -> + case X of + {preproc,_,{_,_,_FileName},[{_,_,"1"}]} -> + reg2_list(G, S, N, C, V, {CFN,false}, Xs); + _ -> + F = reg2(G, S, N, C, V, X), + [F | reg2_list(G, S, N, C, V, {CFN,Status}, Xs)] + end; + false -> + case X of + {preproc,_,{_,_,CFN},[{_,_,"2"}]} -> + F = reg2(G, S, N, C, V, X), + [F | reg2_list(G, S, N, C, V, {CFN,true}, Xs)]; + _ -> + reg2_list(G, S, N, C, V, {CFN,Status}, Xs) + end + end. + + + + + +%% General registration tests +register_tests(Fd,G) -> + IfrId = ?IFRID(G), + emit(Fd,"\n\n%% General IFR registration checks.\n", []), + emit(Fd,"register_tests(~s)->\n",[IfrId]), + emit(Fd," re_register_test(~s),\n",[IfrId]), + emit(Fd," include_reg_test(~s).\n\n",[IfrId]), + + emit(Fd,"\n%% IFR type Re-registration checks.\n", []), + case ic_pragma:fetchRandomLocalType(G) of + {ok,TypeId} -> + emit(Fd,"re_register_test(~s)->\n",[IfrId]), + emit(Fd," case orber_ifr:'Repository_lookup_id'(~s,~p) of\n", [IfrId,TypeId]), + emit(Fd," [] ->\n true;\n",[]), + emit(Fd," _ ->\n exit({allready_registered,~p})\n end.\n\n", [TypeId]); + false -> + emit(Fd,"re_register_test(_)-> true.\n",[]) + end, + + emit(Fd,"~s",[check_include_regs(G)]). + + + + +%% This function produces code for existance check over +%% top level included modules and interfaces +check_include_regs(G) -> + IfrId = ?IFRID(G), + case ic_pragma:get_incl_refs(G) of + none -> + io_lib:format("\n%% No included idl-files detected.\n", []) ++ + io_lib:format("include_reg_test(_~s) -> true.\n",[IfrId]); + IMs -> + io_lib:format("\n%% IFR registration checks for included idl files.\n", []) ++ + io_lib:format("include_reg_test(~s) ->\n",[IfrId]) ++ + check_incl_refs(G,IfrId,IMs) + end. + + + +check_incl_refs(_,_,[]) -> + io_lib:format(" true.\n",[]); +check_incl_refs(G,IfrId,[[First]|Rest]) -> + ModId = ic_pragma:scope2id(G,First), + io_lib:format(" case orber_ifr:'Repository_lookup_id'(~s,~p) of~n", [IfrId,ModId]) ++ + io_lib:format(" [] ->~n exit({unregistered,~p});~n", [ModId]) ++ + io_lib:format(" _ ->~n true~n end,~n",[]) ++ + check_incl_refs(G,IfrId,Rest). + + + +%% This function will return module ref, it will +%% also register module if not registered. +register_if_unregistered(Fd) -> + emit(Fd, "\n\n%% Fetch top module reference, register if unregistered.\n"), + emit(Fd, "oe_get_top_module(OE_IFR, ID, Name, Version) ->\n"), + emit(Fd, " case orber_ifr:'Repository_lookup_id'(OE_IFR, ID) of\n"), + emit(Fd, " [] ->\n"), + emit(Fd, " orber_ifr:'Repository_create_module'(OE_IFR, ID, Name, Version);\n"), + emit(Fd, " Mod ->\n"), + emit(Fd, " Mod\n",[]), + emit(Fd, " end.\n\n"), + emit(Fd, "%% Fetch module reference, register if unregistered.\n"), + emit(Fd, "oe_get_module(OE_IFR, OE_Parent, ID, Name, Version) ->\n"), + emit(Fd, " case orber_ifr:'Repository_lookup_id'(OE_IFR, ID) of\n"), + emit(Fd, " [] ->\n"), + emit(Fd, " orber_ifr:'ModuleDef_create_module'(OE_Parent, ID, Name, Version);\n"), + emit(Fd, " Mod ->\n"), + emit(Fd, " Mod\n",[]), + emit(Fd, " end.\n"). + + + +do_typedef(G, S, N, C, V, X) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + Fd = ic_genobj:stubfiled(G), + Thing = get_thing_name(X), + IR_VSN = get_IR_VSN(G, N, X), + TK = ic_forms:get_tk(X), + + lists:foreach( + fun(Id) -> + r_emit_raw(G, X, Fd, "", C, Thing, V, + get_IR_ID(G, N, Id), get_id2(Id), + IR_VSN, ", ~s", + [get_idltype_tk(G, S, N, + ictype:maybe_array(G, S, N, + Id, TK))]) + end, get_idlist(X)) + end. + + +do_union(G, S, N, C, V, X, {tk_union, _IFRID, _Name, DiscrTK, _DefNr, L}) -> + N2 = [get_id2(X) | N], + r_emit2(G, S, N, C, V, X, ", ~s, [~s]", + [get_idltype_tk(G, S, N, DiscrTK), + get_union_member_def(G, S, N2, L)]), + look_for_types(G, S, N2, C, V, get_body(X)). + +do_struct(G, S, N, C, V, X, {tk_struct, _IFRID, _Name, ElemList}) -> + N2 = [get_id2(X) | N], + r_emit2(G, S, N, C, V, X, ", [~s]", + [get_member_def(G, S, N, ElemList)]), + look_for_types(G, S, N2, C, V, get_body(X)). + +do_except(G, S, N, C, V, X, {tk_except, _IFRID, _Name, ElemList}) -> + N2 = [get_id2(X) | N], + r_emit2(G, S, N, C, V, X, ", [~s]", + [get_member_def(G, S, N, ElemList)]), + look_for_types(G, S, N2, C, V, get_body(X)). + + +%% new_var finds an unused Erlang variable name by increasing a +%% counter. +new_var(_G) -> + lists:flatten(["_OE_", integer_to_list(put(var_count, get(var_count) + 1))]). +init_var() -> + put(var_count, 1). + +%% Public interface. The name of the register function. +register_name(G) -> + mk_oe_name(G, "register"). +unregister_name(G) -> + mk_oe_name(G, "unregister"). + + + +look_for_types(G, S, N, C, V, L) when is_list(L) -> + lists:foreach(fun(X) -> look_for_types(G, S, N, C, V, X) end, L); +look_for_types(G, S, N, C, V, {_Name, TK}) -> % member + look_for_types(G, S, N, C, V, TK); +look_for_types(_G, _S, _N, _C, _V, {tk_union, _IFRID, _Name, _DT, _Def, _L}) -> + ok; +look_for_types(G, S, N, C, V, {_Label, _Name, TK}) -> % case_dcl + look_for_types(G, S, N, C, V, TK); +look_for_types(_G, _S, _N, _C, _V, {tk_struct, _IFRID, _Name, _L}) -> + ok; +look_for_types(_G, _S, _N, _C, _V, _X) -> + ok. + + + + +%% This function produces code for the interface inheritance registration. +%% It produces a string that represents a list of function calls. +%% This list becomes a list of object references when the main function +%% "orber_ifr:ModuleDef_create_interface" is called. + +get_base_interfaces(G,X) -> + case element(3,X) of + [] -> + "[]"; + L -> + "[" ++ + lists:flatten( + lists:foldl( + fun(E, Acc) -> [call_fun_str(G,E), ", " | Acc] end, + call_fun_str(G,hd(L)), + tl(L) + ) + ) ++ "]" + end. + +call_fun_str(G,S) -> + lists:flatten( + io_lib:format("orber_ifr:lookup_id(~s,\"~s\")", + [ ?IFRID(G), + ic_pragma:scope2id(G,S)] )). + + + + + +%%-------------------------------------------------------------------- +%% +%% r_emit emits an IFR register function call. It returns a new +%% variable (if further defs should be added to that one) +%% +%% G is genobj +%% +%% S is symbol table (ets) +%% +%% N is list of ids describing scope +%% +%% C is create stub (eg. "Repository_create_") +%% +%% V is variable name where current def should be added, +%% +%% X is the current def item, +%% +%% F and A is auxillary format and args that will be io_lib +%% formatted and inserted as a string (don't forget to start with +%% ", ") +%% +r_emit2(G, _S, N, C, V, X, F, A) -> + case ic_genobj:is_stubfile_open(G) of + false -> ok; + true -> + {NewV, Str} = get_assign(G, V, X), + r_emit_raw(G, X, ic_genobj:stubfiled(G), Str, + C, get_thing_name(X), V, + get_IR_ID(G, N, X), get_id2(X), get_IR_VSN(G, N, X), + F, A), + NewV + end. + + +%%-------------------------------------------------------------------- +%% +%% An IFR register line registers an entity (Thing) into the IFR. The +%% thing is registered INTO something, an type is registered into a +%% module for instance, and this is reflected in the Var parameter +%% below. The var parameter is the name of the parent IFR object. The +%% Thing parameter is the name of the thing we're trying to register, +%% a typdef is called an alias and an interface is called an +%% interface. Sometimes we need to store the thing we're registering +%% into a variable because we're going to add other things to it +%% later, modules and interfaces are such containers, so we must +%% remember that variable for later use. +%% +%% All parameters shall be strings unless otherwise noted +%% +%% Fd - File descriptor +%% AssignStr - Assign or not, empty except for interfaces and modules +%% Create - Create has diff. names dep. on into what we register +%% Thing - WHAT is registered, interface +%% Var - The name of the variable we register into +%% IR_ID - The IFR identifier (may be "") +%% Id - The identifier (name) of the object +%% IR_VSN - The IFR version as a string +%% AuxStr - An auxillary string +%% +%%r_emit_raw(Fd, AssignStr, Create, Thing, Var, IR_ID, Id, IR_VSN) -> +%% r_emit_raw(Fd, AssignStr, Create, Thing, Var, IR_ID, Id, IR_VSN, "", []). +r_emit_raw(_G, X, Fd, AssignStr, "Repository_create_", Thing, Var, IR_ID, Id, IR_VSN, F, A) + when is_record(X, module) -> + emit(Fd, "~n ~s~p(~s, \"~s\", \"~s\", \"~s\"~s),~n", + [AssignStr, to_atom("oe_get_top_"++Thing), Var, IR_ID, Id, + IR_VSN, io_lib:format(F, A)]); +r_emit_raw(G, X, Fd, AssignStr, "ModuleDef_create_", Thing, Var, IR_ID, Id, IR_VSN, F, A) + when is_record(X, module) -> + emit(Fd, "~n ~s~p(~s, ~s, \"~s\", \"~s\", \"~s\"~s),~n", + [AssignStr, to_atom("oe_get_"++Thing), ?IFRID(G), Var, IR_ID, Id, + IR_VSN, io_lib:format(F, A)]); +r_emit_raw(_G, _X, Fd, AssignStr, Create, Thing, Var, IR_ID, Id, IR_VSN, F, A) -> + emit(Fd, "~n ~s~p:~p(~s, \"~s\", \"~s\", \"~s\"~s),~n", + [AssignStr, ?IFRMOD, to_atom(Create++Thing), Var, IR_ID, Id, + IR_VSN, io_lib:format(F, A)]). + + + + +%% Used by r_emit. Returns tuple {Var, Str} where Var is the resulting +%% output var (if any, otherwise same as input arg) and Str is a +%% string of the assignment if any ("" or "Var = ") +get_assign(G, _V, X) when is_record(X, module) -> + mk_assign(G); +get_assign(G, _V, X) when is_record(X, interface) -> + mk_assign(G); +get_assign(_G, V, _X) -> {V, ""}. +mk_assign(G) -> + V = new_var(G), + {V, io_lib:format("~s = ", [V])}. + +%% Returns a list of strings of all enum members (suitable for ~p) +get_enum_member_list(_G, _S, _N, L) -> + lists:map(fun(M) -> get_id2(M) end, L). + +%% Will output a string of the union members. +get_union_member_def(_G, _S, _N, []) -> []; +get_union_member_def(G, S, N, L) -> + [union_member2str(G, S, N, hd(L)) | + lists:map(fun(M) -> [", ", union_member2str(G, S, N, M)] end, tl(L))]. +%% lists:foldl(fun(M, Acc) -> +%% [union_member2str(G, S, N, M),", " | Acc] end, +%% union_member2str(G, S, N, hd(L)), tl(L)). + +union_member2str(G, S, N, {Label, Name, TK}) -> + io_lib:format("~s{name=~p, label=~p, type=~p, type_def=~s}", + ["#unionmember", Name, Label, TK, + get_idltype_tk(G, S, N, TK)]). + + +%% Will output a string of the struct members. Works for exceptions +%% and structs +%% +get_member_def(_G, _S, _N, []) -> []; +get_member_def(G, S, N, L) -> + [member2str(G, S, N, hd(L)) | + lists:map(fun(M) -> [", ", member2str(G, S, N, M)] end, tl(L))]. + +member2str(G, S, N, {Id, TK}) -> + io_lib:format("~s{name=~p, type=~p, type_def=~s}", + ["#structmember", Id, TK, get_idltype_tk(G, S, N, TK)]). + +%% Translates between record names and create operation names. +get_thing_name(X) when is_record(X, op) -> "operation"; +get_thing_name(X) when is_record(X, const) -> "constant"; +get_thing_name(X) when is_record(X, typedef) -> "alias"; +get_thing_name(X) when is_record(X, attr) -> "attribute"; +get_thing_name(X) when is_record(X, except) -> "exception"; +get_thing_name(X) when is_record(X, id_of) -> get_thing_name(X#id_of.type); +get_thing_name(X) -> to_list(element(1,X)). + + +%% Returns the mode (in, out, oneway etc) of ops and params. Return +%% value is an atom. +get_mode(_G, _N, X) when is_record(X, op) -> + case X#op.oneway of + {oneway, _} -> 'OP_ONEWAY'; + _ -> 'OP_NORMAL' + end; +get_mode(_G, _N, X) when is_record(X, attr) -> + case X#attr.readonly of + {readonly, _} -> 'ATTR_READONLY'; + _ -> 'ATTR_NORMAL' + end; +get_mode(_G, _N, X) when is_record(X, param) -> + case X#param.inout of + {in, _} -> 'PARAM_IN'; + {inout, _} -> 'PARAM_INOUT'; + {out, _} -> 'PARAM_OUT' + end. + + +%% Returns a string form of idltype creation. +%%get_idltype_id(G, S, N, X, Id) -> +%% TK = ictype:tk_lookup(G, S, N, Id), +%% get_idltype_tk(G, S, N, TK). +get_idltype(G, S, N, X) -> + get_idltype_tk(G, S, N, ic_forms:get_tk(X)). +get_idltype_tk(G, _S, _N, TK) -> + io_lib:format("~p:~p(~s, ~p)", [orber_ifr, 'Repository_create_idltype', + ?IFRID(G), TK]). + +%% Returns a string form of typecode creation. This shall be found in +%% the type code symbol table. +%%get_typecode(G, S, N, X) -> typecode. +%%get_typecode(G, S, N, X) -> tk(G, S, N, get_type(X)). + + +%% Returns the string form of a list of parameters. +get_params(_G, _S, _N, []) -> ""; +get_params(G, S, N, L) -> + lists:foldl(fun(X, Acc) -> param2str(G, S, N, X)++", "++Acc end, + param2str(G, S, N, hd(L)), tl(L)). + + +%% Converts a parameter to a string. +param2str(G, S, N, X) -> + io_lib:format("~s{name=~p, type=~p, type_def=~s, mode=~p}~n", + ["#parameterdescription", get_id2(X), + ic_forms:get_tk(X), + %%tk_lookup(G, S, N, get_type(X)), + get_idltype(G, S, N, X), + get_mode(G, N, X)]). + + + + +%% Public interface. Returns the IFR ID of an object. This +%% is updated to comply with CORBA 2.0 pragma directives. +get_IR_ID(G, N, X) -> + ScopedId = [get_id2(X) | N], + case ic_pragma:get_alias(G,ScopedId) of + none -> + case ic_pragma:pragma_id(G, N, X) of + none -> + case ic_pragma:pragma_prefix(G, N, X) of + none -> + IR_ID = lists:flatten( + io_lib:format("IDL:~s:~s", + [slashify(ScopedId), + get_IR_VSN(G, N, X)])), + ic_pragma:mk_alias(G,IR_ID,ScopedId), + IR_ID; + PF -> + IR_ID = lists:flatten( + io_lib:format("IDL:~s:~s", + [ PF ++ "/" ++ + get_id2(X), + get_IR_VSN(G, N, X)])), + ic_pragma:mk_alias(G,IR_ID,ScopedId), + IR_ID + end; + PI -> + ic_pragma:mk_alias(G,PI,ScopedId), + PI + end; + Alias -> + Alias + end. + + +%% Public interface. Returns the IFR Version of an object. This +%% is updated to comply with CORBA 2.0 pragma directives. +get_IR_VSN(G, N, X) -> + ic_pragma:pragma_version(G,N,X). + + + + + +%% Returns a slashified name, [I1, M1] becomes "M1/I1" +%slashify(List) -> lists:foldl(fun(X, Acc) -> get_id2(X)++"/"++Acc end, +% hd(List), tl(List)). + +%% Returns a slashified name, [I1, M1] becomes "M1/I1" +slashify(List) -> lists:foldl(fun(X, Acc) -> X++"/"++Acc end, + hd(List), tl(List)). + + +%% Returns the context literals of an op +get_context(_G, _S, _N, X) -> + lists:map(fun(C) -> element(3, C) end, X#op.ctx). + + + +%% Returns the list of the exceptions of an operation +get_exceptions(G, S, N, X) -> + case X#op.raises of + [] -> + ""; + L -> + lists:flatten( + lists:foldl( + fun(E, Acc) -> [excdef(G, S, N, X, E), ", " | Acc] end, + excdef(G, S, N, X, hd(L)), + tl(L) + ) + ) + end. + + +%% Returns the definition of an exception of an operation +excdef(G, S, N, X, L) -> + io_lib:format("orber_ifr:lookup_id(~s,\"~s\")", + [ ?IFRID(G), + get_EXC_ID(G, S, N, X, L) ] ). + + + + + + +%% This function produces code for the exception registration. +%% It produces a string that represents a list of function calls. +%% This list becomes a list of object references when the main function +%% "orber_ifr:InterfaceDef_create_operation" is called. + +get_EXC_ID(G, _S, N, X, ScopedId) -> + case ic_pragma:get_alias(G,ScopedId) of + none -> + case ic_pragma:pragma_id(G, N, X) of + none -> + case ic_pragma:pragma_prefix(G, N, X) of + none -> + EXC_ID = lists:flatten( + io_lib:format("IDL:~s:~s", [slashify(ScopedId), + get_IR_VSN(G, N, X)])), + ic_pragma:mk_alias(G,EXC_ID,ScopedId), + EXC_ID; + PF -> + EXC_ID = lists:flatten( + io_lib:format("IDL:~s:~s", [ PF ++ "/" ++ + hd(ScopedId), + get_IR_VSN(G, N, X)])), + ic_pragma:mk_alias(G,EXC_ID,ScopedId), + EXC_ID + end; + PI -> + ic_pragma:mk_alias(G,PI,ScopedId), + PI + end; + Alias -> + Alias + end. + + + + + +%% unreg_gen/1 uses the information stored in pragma table +%% to decide which modules are to be unregistered +unreg_gen(G, N, X) -> + Light = ic_options:get_opt(G, light_ifr), + case ic_genobj:is_stubfile_open(G) of + true when Light == false -> + Var = ?IFRID(G), + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), nl(Fd), + emit(Fd, "~p() ->\n", [to_atom(unregister_name(G))]), + emit(Fd, " ~s = ~p:find_repository(),\n", + [Var, ?IFRMOD]), + nl(Fd), + + unreg2(G, N, X), + emit(Fd, " ok.\n\n"), + destroy(Fd); + true -> + Fd = ic_genobj:stubfiled(G), + nl(Fd), nl(Fd), + Unregname = to_atom(unregister_name(G)), + emit(Fd, "~p() ->\n\t~p([]).\n\n~p(OE_Options) ->\n", + [Unregname, Unregname, Unregname]), + emit(Fd, "\t~p:remove(?MODULE, OE_Options),\n\tok.\n\n", [?IFRMOD]); + false -> ok + end. + + +destroy(Fd) -> +emit(Fd," +oe_destroy_if_empty(OE_IFR,IFR_ID) -> + case orber_ifr:'Repository_lookup_id'(OE_IFR, IFR_ID) of + [] -> + ok; + Ref -> + case orber_ifr:contents(Ref, \'dk_All\', \'true\') of + [] -> + orber_ifr:destroy(Ref), + ok; + _ -> + ok + end + end. + +oe_destroy(OE_IFR,IFR_ID) -> + case orber_ifr:'Repository_lookup_id'(OE_IFR, IFR_ID) of + [] -> + ok; + Ref -> + orber_ifr:destroy(Ref), + ok + end. + +",[]). + + + + + + + + + + +%% unreg2 is top level registration + +unreg2(G, N, X) -> + emit(ic_genobj:stubfiled(G),"~s",[lists:flatten(unreg3(G, N, X))]). + +unreg3(G, N, X) when is_list(X) -> + unreg3_list(G, N, X, []); + +unreg3(G, N, X) when is_record(X, module) -> + unreg3_list(G, [get_id2(X) | N], get_body(X), [unreg_collect(G, N, X)]); + +unreg3(G, N, X) when is_record(X, const) -> + unreg_collect(G, N, X); + +unreg3(G, N, X) when is_record(X, struct) -> + unreg_collect(G, N, X); + +unreg3(G, N, X) when is_record(X, except) -> + unreg_collect(G, N, X); + +unreg3(G, N, X) when is_record(X, union) -> + unreg_collect(G, N, X); + +unreg3(G, N, X) when is_record(X, enum) -> + unreg_collect(G, N, X); + +unreg3(G, N, X) when is_record(X, typedef) -> + unreg_collect(G, N, X); + +unreg3(G, N, X) when is_record(X, interface) -> + unreg_collect(G, N, X); + +unreg3(_G, _N, X) when is_record(X, op) -> []; + +unreg3(_G, _N, X) when is_record(X, attr) -> []; + +unreg3(_G, _N, X) when is_record(X, preproc) -> []; + +unreg3(_G, _N, X) when is_record(X, pragma) -> []; + +unreg3(_G, _N, _X) -> []. + + +unreg3_list(_G, _N, [], Found) -> + Found; +unreg3_list(G, N, List, Found) -> + CurrentFileName = ic_genobj:idlfile(G), + unreg3_list(G, N, {CurrentFileName,true}, List, Found). + +%% The filter function + loop +unreg3_list(_G, _N, {_CFN, _Status}, [], Found) -> + Found; +unreg3_list(G, N, {CFN,Status}, [X | Xs], Found) -> + case Status of + true -> + case X of + {preproc,_,{_,_,_FileName},[{_,_,"1"}]} -> + unreg3_list(G, N, {CFN,false}, Xs, Found); + _ -> + unreg3_list(G, N, {CFN,Status}, Xs, [unreg3(G, N, X) | Found]) + end; + false -> + case X of + {preproc,_,{_,_,CFN},[{_,_,"2"}]} -> + unreg3_list(G, N, {CFN,true}, Xs,[unreg3(G, N, X) | Found]); + _ -> + unreg3_list(G, N, {CFN,Status}, Xs, Found) + end + end. + + + +unreg_collect(G, N, X) when is_record(X, module) -> + io_lib:format(" oe_destroy_if_empty(OE_IFR, ~p),\n", + [get_IR_ID(G, N, X)]); +unreg_collect(G, N, X) when is_record(X, typedef) -> + lists:map(fun(Id) -> + io_lib:format(" oe_destroy(OE_IFR, ~p),\n", + [get_IR_ID(G, N, Id)]) + end, + ic_forms:get_idlist(X)); +unreg_collect(G, N, X) -> + io_lib:format(" oe_destroy(OE_IFR, ~p),\n", + [get_IR_ID(G, N, X)]). + + + diff --git a/lib/ic/src/ictype.erl b/lib/ic/src/ictype.erl new file mode 100644 index 0000000000..4704191bee --- /dev/null +++ b/lib/ic/src/ictype.erl @@ -0,0 +1,1413 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +-module(ictype). + + +-include("ic.hrl"). +-include("icforms.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([type_check/2, scoped_lookup/4, maybe_array/5, to_uppercase/1]). + +-export([name2type/2, member2type/3, isBasicTypeOrEterm/3, isEterm/3]). +-export([isBasicType/1, isBasicType/2, isBasicType/3, isString/3, isWString/3, + isArray/3, isStruct/3, isUnion/3, isEnum/3, isSequence/3, isBoolean/3 ]). +-export([fetchTk/3, fetchType/1, tk/4]). +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +%%----------------------------------------------------------------- +%% Macros +%%----------------------------------------------------------------- +%%-define(DBG(F,A), io:format(F,A)). +-define(DBG(F,A), true). +-define(STDDBG, ?DBG(" dbg: ~p: ~p~n", [element(1,X), ic_forms:get_id2(X)])). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +type_check(G, Forms) -> + S = ic_genobj:tktab(G), + check_list(G, S, [], Forms). + +scoped_lookup(G, S, N, X) -> + Id = ic_symtab:scoped_id_strip(X), + case ic_symtab:scoped_id_is_global(X) of + true -> + lookup(G, S, [], X, Id); + false -> + lookup(G, S, N, X, Id) + end. + + +%%-------------------------------------------------------------------- +%% maybe_array +%% +%% Array declarators are indicated on the declarator and not on +%% the type, therefore the declarator decides if the array type +%% kind is added or not. +%% +maybe_array(G, S, N, X, TK) when is_record(X, array) -> + mk_array(G, S, N, X#array.size, TK); +maybe_array(_G, _S, _N, _, TK) -> TK. + + + +name2type(G, Name) -> + S = ic_genobj:tktab(G), + ScopedName = lists:reverse(string:tokens(Name, "_")), + InfoList = ets:lookup(S, ScopedName ), + filter( InfoList ). + + +%% This is en overloaded function, +%% differs in input on unions +member2type(_G, X, I) when is_record(X, union)-> + Name = ic_forms:get_id2(I), + case lists:keysearch(Name,2,element(6,X#union.tk)) of + false -> + error; + {value,Rec} -> + fetchType(element(3,Rec)) + end; +member2type( G, SName, MName ) -> + + S = ic_genobj:tktab( G ), + SNList = lists:reverse(string:tokens(SName,"_")), + ScopedName = [MName | SNList], + InfoList = ets:lookup( S, ScopedName ), + + case filter( InfoList ) of + error -> + %% Try a little harder, seeking inside tktab + case lookup_member_type_in_tktab(S, ScopedName, MName) of + error -> + %% Check if this is the "return to return1" case + case MName of + "return1" -> + %% Do it all over again ! + ScopedName2 = ["return" | SNList], + InfoList2 = ets:lookup( S, ScopedName2 ), + case filter( InfoList2 ) of + error -> + %% Last resort: seek in pragma table + lookup_type_in_pragmatab(G, SName); + + Other -> + Other + end; + _ -> + %% Last resort: seek in pragma table + lookup_type_in_pragmatab(G, SName) + end; + Other -> + Other + end; + Other -> + Other + end. + + +lookup_member_type_in_tktab(S, ScopedName, MName) -> + case ets:match_object(S, {'_',member,{MName,'_'},nil}) of + [] -> + error; + [{_FullScopedName,member,{MName,TKInfo},nil}]-> + fetchType( TKInfo ); + List -> + lookup_member_type_in_tktab(List,ScopedName) + end. + +lookup_member_type_in_tktab([], _ScopedName) -> + error; +lookup_member_type_in_tktab([{FullScopedName,_,{_,TKInfo},_}|Rest],ScopedName) -> + case lists:reverse(string:tokens(ic_util:to_undersc(FullScopedName),"_")) of + ScopedName -> + fetchType(TKInfo); + _ -> + lookup_member_type_in_tktab(Rest,ScopedName) + end. + + +lookup_type_in_pragmatab(G, SName) -> + S = ic_genobj:pragmatab(G), + + %% Look locally first + case ets:match(S,{file_data_local,'_','_','$2','_','_',SName,'_','_'}) of + [] -> + %% No match, seek included + case ets:match(S,{file_data_included,'_','_','$2','_','_',SName,'_','_'}) of + + [] -> + error; + [[Type]] -> + io:format("1 Found(~p) : ~p~n",[SName,Type]), + Type + end; + + [[Type]] -> + io:format("2 Found(~p) : ~p~n",[SName,Type]), + Type + end. + + + + +isString(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_string',_}, _} -> + true; + _ -> + false + end; +isString(_G, _N, T) when is_record(T, string) -> + true; +isString(_G, _N, _Other) -> + false. + + +isWString(G, N, T) when element(1, T) == scoped_id -> %% WSTRING + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_wstring',_}, _} -> + true; + _ -> + false + end; +isWString(_G, _N, T) when is_record(T, wstring) -> + true; +isWString(_G, _N, _Other) -> + false. + + +isArray(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_array', _, _}, _} -> + true; + _ -> + false + end; +isArray(_G, _N, T) when is_record(T, array) -> + true; +isArray(_G, _N, _Other) -> + false. + + +isSequence(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_sequence', _, _}, _} -> + true; + _ -> + false + end; +isSequence(_G, _N, T) when is_record(T, sequence) -> + true; +isSequence(_G, _N, _Other) -> + false. + + +isStruct(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_struct', _, _, _}, _} -> + true; + _ -> + false + end; +isStruct(_G, _N, T) when is_record(T, struct) -> + true; +isStruct(_G, _N, _Other) -> + false. + + +isUnion(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_union', _, _, _,_,_}, _} -> + true; + _Other -> + false + end; +isUnion(_G, _N, T) when is_record(T, union) -> + true; +isUnion(_G, _N, _Other) -> + false. + + + +isEnum(G, N, T) when element(1, T) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, T) of + {_FullScopedName, _, {'tk_enum',_,_,_}, _} -> + true; + _Other -> + false + end; +isEnum(_G, _N, T) when is_record(T, enum) -> + true; +isEnum(_G, _N, _Other) -> + false. + + + +isBoolean(G, N, T) when element(1, T) == scoped_id -> + {_, _, TK, _} = + ic_symtab:get_full_scoped_name(G, N, T), + case fetchType(TK) of + 'boolean' -> + true; + _ -> + false + end; +isBoolean(_, _, {'tk_boolean',_}) -> + true; +isBoolean(_, _, {'boolean',_}) -> + true; +isBoolean(_, _, _) -> + false. + + +%%% Just used for C + +isBasicTypeOrEterm(G, N, S) -> + case isBasicType(G, N, S) of + true -> + true; + false -> + isEterm(G, N, S) + end. + +isEterm(G, N, S) when element(1, S) == scoped_id -> + {FullScopedName, _, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + case ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)) of + "erlang_term" -> + true; + "ETERM*" -> + true; + _X -> + false + end; +isEterm(_G, _Ni, _X) -> + false. + +isBasicType(_G, _N, {scoped_id,_,_,["term","erlang"]}) -> + false; +isBasicType(G, N, S) when element(1, S) == scoped_id -> + {_, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, S), + isBasicType(fetchType(TK)); +isBasicType(_G, _N, {string, _} ) -> + false; +isBasicType(_G, _N, {wstring, _} ) -> %% WSTRING + false; +isBasicType(_G, _N, {unsigned, {long, _}} ) -> + true; +isBasicType(_G, _N, {unsigned, {short, _}} ) -> + true; +isBasicType(_G, _N, {Type, _} ) -> + isBasicType(Type); +isBasicType(_G, _N, _X) -> + false. + + +isBasicType( G, Name ) -> + isBasicType( name2type( G, Name ) ). + + +isBasicType( Type ) -> + lists:member(Type, + [tk_short,short, + tk_long,long, + tk_longlong,longlong, %% LLONG + tk_ushort,ushort, + tk_ulong,ulong, + tk_ulonglong,ulonglong, %% ULLONG + tk_float,float, + tk_double,double, + tk_boolean,boolean, + tk_char,char, + tk_wchar,wchar, %% WCHAR + tk_octet,octet, + tk_any,any]). %% Fix for any + + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- +check(G, _S, N, X) when is_record(X, preproc) -> + handle_preproc(G, N, X#preproc.cat, X), + X; + +check(G, S, N, X) when is_record(X, op) -> + ?STDDBG, + TK = tk_base(G, S, N, ic_forms:get_type(X)), + tktab_add(G, S, N, X), + N2 = [ic_forms:get_id2(X) | N], + Ps = lists:map(fun(P) -> + tktab_add(G, S, N2, P), + P#param{tk=tk_base(G, S, N, ic_forms:get_type(P))} end, + X#op.params), + %% Check for exception defs. + Raises = lists:map(fun(E) -> name_lookup(G, S, N, E) end, + X#op.raises), + case ic_forms:is_oneway(X) of + true -> + if TK /= tk_void -> + ic_error:error(G, {bad_oneway_type, X, TK}); + true -> ok + end, + case ic:filter_params([inout, out], X#op.params) of + [] -> ok; % No out parameters! + _ -> + ic_error:error(G, {oneway_outparams, X}) + end, + case X#op.raises of + [] -> ok; + _ -> + ic_error:error(G, {oneway_raises, X}) + end; + false -> + ok + end, + X#op{params=Ps, tk=TK, raises=Raises}; + +check(G, S, N, X) when is_record(X, interface) -> + ?STDDBG, + N2 = [ic_forms:get_id2(X) | N], + TK = {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}, + Inherit = inherit_resolve(G, S, N, X#interface.inherit, []), + tktab_add(G, S, N, X, TK, Inherit), + CheckedBody = check_list(G, S, N2, ic_forms:get_body(X)), + InhBody = calc_inherit_body(G, N2, CheckedBody, Inherit, []), + X2 = X#interface{inherit=Inherit, tk=TK, body=CheckedBody, + inherit_body=InhBody}, + ic_symtab:store(G, N, X2), + X2; + +check(G, S, N, X) when is_record(X, forward) -> + ?STDDBG, + tktab_add(G, S, N, X, {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}), + X; + + +check(G, S, N, X) when is_record(X, const) -> + ?STDDBG, + case tk_base(G, S, N, ic_forms:get_type(X)) of + Err when element(1, Err) == error -> X; + TK -> + check_const_tk(G, S, N, X, TK), + case iceval:eval_const(G, S, N, TK, X#const.val) of + Err when element(1, Err) == error -> X; + {ok, NewTK, Val} -> + V = iceval:get_val(Val), + tktab_add(G, S, N, X, NewTK, V), + X#const{val=V, tk=NewTK}; + Val -> + V = iceval:get_val(Val), + tktab_add(G, S, N, X, TK, V), + X#const{val=V, tk=TK} + end + end; + +check(G, S, N, X) when is_record(X, const) -> + ?STDDBG, + case tk_base(G, S, N, ic_forms:get_type(X)) of + Err when element(1, Err) == error -> X; + TK -> + check_const_tk(G, S, N, X, TK), + case iceval:eval_const(G, S, N, TK, X#const.val) of + Err when element(1, Err) == error -> X; + Val -> + V = iceval:get_val(Val), + tktab_add(G, S, N, X, TK, V), + X#const{val=V, tk=TK} + end + end; + +check(G, S, N, X) when is_record(X, except) -> + ?STDDBG, + TK = tk(G, S, N, X), + X#except{tk=TK}; + +check(G, S, N, X) when is_record(X, struct) -> + ?STDDBG, + TK = tk(G, S, N, X), + X#struct{tk=TK}; + +check(G, S, N, X) when is_record(X, enum) -> + ?STDDBG, + TK = tk(G, S, N, X), + X#enum{tk=TK}; + +check(G, S, N, X) when is_record(X, union) -> + ?STDDBG, + TK = tk(G, S, N, X), + X#union{tk=TK}; + +check(G, S, N, X) when is_record(X, attr) -> + ?STDDBG, + TK = tk_base(G, S, N, ic_forms:get_type(X)), + XX = #id_of{type=X}, + lists:foreach(fun(Id) -> tktab_add(G, S, N, XX#id_of{id=Id}) end, + ic_forms:get_idlist(X)), + X#attr{tk=TK}; + +check(G, S, N, X) when is_record(X, module) -> + ?STDDBG, + tktab_add(G, S, N, X), + X#module{body=check_list(G, S, [ic_forms:get_id2(X) | N], ic_forms:get_body(X))}; + +check(G, S, N, X) when is_record(X, typedef) -> + ?STDDBG, + TKbase = tk(G, S, N, X), + X#typedef{tk=TKbase}; + +check(_G, _S, _N, X) -> + ?DBG(" dbg: ~p~n", [element(1,X)]), + X. + +handle_preproc(G, _N, line_nr, X) -> ic_genobj:set_idlfile(G, ic_forms:get_id2(X)); +handle_preproc(_G, _N, _C, _X) -> ok. + + +%%-------------------------------------------------------------------- +%% +%% TK calculation +%% +%%-------------------------------------------------------------------- + +tk(G, S, N, X) when is_record(X, union) -> + N2 = [ic_forms:get_id2(X) | N], + DisrcTK = tk(G, S, N, ic_forms:get_type(X)), + case check_switch_tk(G, S, N, X, DisrcTK) of + true -> + do_special_enum(G, S, N2, ic_forms:get_type(X)), + BodyTK = lists:reverse( + tk_caselist(G, S, N2, DisrcTK, ic_forms:get_body(X))), + tktab_add(G, S, N, X, + {tk_union, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), + DisrcTK, default_count(ic_forms:get_body(X)), BodyTK}); + false -> + tk_void + end; + +tk(G, S, N, X) when is_record(X, enum) -> + N2 = [ic_forms:get_id2(X) | N], + tktab_add(G, S, N, X, + {tk_enum, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), + enum_body(G, S, N2, ic_forms:get_body(X))}); + + +%% Note that the TK returned from this function is the base TK. It +%% must be modified for each of the identifiers in the idlist (for +%% array reasons). +tk(G, S, N, X) when is_record(X, typedef) -> + case X of + %% Special case only for term and java backend ! + {typedef,{any,_},[{'<identifier>',_,"term"}],undefined} -> + case ic_options:get_opt(G, be) of + java -> + tktab_add(G, S, N, X, tk_term), + tk_term; + _ -> + TK = tk(G, S, N, ic_forms:get_body(X)), + lists:foreach(fun(Id) -> + tktab_add(G, S, N, #id_of{id=Id, type=X}, + maybe_array(G, S, N, Id, TK)) + end, + X#typedef.id), + TK + end; + _ -> + TK = tk(G, S, N, ic_forms:get_body(X)), + lists:foreach(fun(Id) -> + tktab_add(G, S, N, #id_of{id=Id, type=X}, + maybe_array(G, S, N, Id, TK)) + end, + X#typedef.id), + TK + end; + +tk(G, S, N, X) when is_record(X, struct) -> + N2 = [ic_forms:get_id2(X) | N], + tktab_add(G, S, N, X, {tk_struct, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), + tk_memberlist(G, S, N2, ic_forms:get_body(X))}); + +tk(G, S, N, X) when is_record(X, except) -> + N2 = [ic_forms:get_id2(X) | N], + tktab_add(G, S, N, X, {tk_except, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), + tk_memberlist(G, S, N2, ic_forms:get_body(X))}); + +tk(G, S, N, X) -> tk_base(G, S, N, X). + + +tk_base(G, S, N, X) when is_record(X, sequence) -> + {tk_sequence, tk(G, S, N, X#sequence.type), + len_eval(G, S, N, X#sequence.length)}; + +tk_base(G, S, N, X) when is_record(X, string) -> + {tk_string, len_eval(G, S, N, X#string.length)}; + +tk_base(G, S, N, X) when is_record(X, wstring) -> %% WSTRING + {tk_wstring, len_eval(G, S, N, X#wstring.length)}; + +%% Fixed constants can be declared as: +%% (1) const fixed pi = 3.14D; or +%% (2) typedef fixed<3,2> f32; +%% const f32 pi = 3.14D; +tk_base(G, S, N, X) when is_record(X, fixed) -> + %% Case 2 + {tk_fixed, len_eval(G, S, N, X#fixed.digits), len_eval(G, S, N, X#fixed.scale)}; +tk_base(_G, _S, _N, {fixed, _}) -> + %% Case 1 + tk_fixed; + + +%% Special case, here CORBA::TypeCode is built in +%% ONLY when erl_corba is the backend of choice +tk_base(G, S, N, {scoped_id,V1,V2,["TypeCode","CORBA"]}) -> + case ic_options:get_opt(G, be) of + false -> + tk_TypeCode; + erl_corba -> + tk_TypeCode; + erl_template -> + tk_TypeCode; + _ -> + case scoped_lookup(G, S, N, {scoped_id,V1,V2,["TypeCode","CORBA"]}) of + T when element(1, T) == error -> T; + T when is_tuple(T) -> element(3, T) + end + end; + +tk_base(G, S, N, X) when element(1, X) == scoped_id -> + case scoped_lookup(G, S, N, X) of + T when element(1, T) == error -> T; + T when is_tuple(T) -> element(3, T) + end; +tk_base(_G, _S, _N, {long, _}) -> tk_long; +tk_base(_G, _S, _N, {'long long', _}) -> tk_longlong; %% LLONG +tk_base(_G, _S, _N, {short, _}) -> tk_short; +tk_base(_G, _S, _N, {'unsigned', {short, _}}) -> tk_ushort; +tk_base(_G, _S, _N, {'unsigned', {long, _}}) -> tk_ulong; +tk_base(_G, _S, _N, {'unsigned', {'long long', _}})-> tk_ulonglong; %% ULLONG +tk_base(_G, _S, _N, {float, _}) -> tk_float; +tk_base(_G, _S, _N, {double, _}) -> tk_double; +tk_base(_G, _S, _N, {boolean, _}) -> tk_boolean; +tk_base(_G, _S, _N, {char, _}) -> tk_char; +tk_base(_G, _S, _N, {wchar, _}) -> tk_wchar; %% WCHAR +tk_base(_G, _S, _N, {octet, _}) -> tk_octet; +tk_base(_G, _S, _N, {null, _}) -> tk_null; +tk_base(_G, _S, _N, {void, _}) -> tk_void; +tk_base(_G, _S, _N, {any, _}) -> tk_any; +tk_base(_G, _S, _N, {'Object', _}) -> {tk_objref, "", "Object"}. + + +%%-------------------------------------------------------------------- +%% +%% Special handling of idlists. Note that the recursion case is given +%% as accumulator to foldr. Idlists are those lists of identifiers +%% that share the same definition, i.e. multiple cases, multiple type +%% declarations, multiple member names. +%% +tk_memberlist(G, S, N, [X | Xs]) -> + BaseTK = tk(G, S, N, ic_forms:get_type(X)), + + XX = #id_of{type=X}, + lists:foldr(fun(Id, Acc) -> + [tk_member(G, S, N, XX#id_of{id=Id}, BaseTK) | Acc] end, + tk_memberlist(G, S, N, Xs), + ic_forms:get_idlist(X)); +tk_memberlist(_G, _S, _N, []) -> []. + +%% same as above but for case dcls +tk_caselist(G, S, N, DiscrTK, Xs) -> + lists:foldl(fun(Case, Acc) -> + BaseTK = tk(G, S, N, ic_forms:get_type(Case)), + %% tktab_add for the uniqueness check of the declarator + tktab_add(G, S, N, Case), + lists:foldl(fun(Id, Acc2) -> + case tk_case(G, S, N, Case, BaseTK, + DiscrTK, Id) of + Err when element(1, Err)==error -> + Acc2; + TK -> + unique_add_case_label(G, S, N, Id, + TK, Acc2) + end + end, + Acc, + ic_forms:get_idlist(Case)) + end, + [], + Xs). + + +%% Handling of the things that can be in an idlist or caselist +tk_member(G, S, N, X, BaseTK) -> + tktab_add(G, S, N, X, + {ic_forms:get_id2(X), maybe_array(G, S, N, X#id_of.id, BaseTK)}). + + +get_case_id_and_check(G, _S, _N, _X, ScopedId) -> + case ic_symtab:scoped_id_is_global(ScopedId) of + true -> ic_error:error(G, {bad_scope_enum_case, ScopedId}); + false -> ok + end, + case ic_symtab:scoped_id_strip(ScopedId) of + [Id] -> Id; + _List -> + ic_error:error(G, {bad_scope_enum_case, ScopedId}), + "" + end. + + +tk_case(G, S, N, X, BaseTK, DiscrTK, Id) -> + case case_eval(G, S, N, DiscrTK, Id) of + Err when element(1, Err) == error -> Err; + Val -> + case iceval:check_tk(G, DiscrTK, Val) of + true -> + {iceval:get_val(Val), ic_forms:get_id2(X), + maybe_array(G, S, N, X#case_dcl.id, BaseTK)}; + false -> + ic_error:error(G, {bad_case_type, DiscrTK, X, + iceval:get_val(Val)}) + end + end. + +tktab_add(G, S, N, X) -> + tktab_add_id(G, S, N, X, ic_forms:get_id2(X), nil, nil). +tktab_add(G, S, N, X, TK) -> + tktab_add_id(G, S, N, X, ic_forms:get_id2(X), TK, nil). +tktab_add(G, S, N, X, TK, Aux) -> + tktab_add_id(G, S, N, X, ic_forms:get_id2(X), TK, Aux). + + +tktab_add_id(G, S, N, X, Id, TK, Aux) when is_record(X,enumerator) -> + + %% Check if the "scl" flag is set to true + %% if so, allow old semantics ( errornous ) + %% Warning, this is for compatibility reasons only. + Name = case ic_options:get_opt(G, scl) of + true -> + [Id | N]; + false -> + [Id | tl(N)] + end, + + UName = mk_uppercase(Name), + case ets:lookup(S, Name) of + [_] -> ic_error:error(G, {multiply_defined, X}); + [] -> + case ets:lookup(S, UName) of + [] -> ok; + [_] -> ic_error:error(G, {illegal_spelling, X}) + end + end, + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; +%% +%% Fixes the multiple file module definition check +%% but ONLY for Corba backend +%% +tktab_add_id(G, S, N, X, Id, TK, Aux) when is_record(X,module) -> + case ic_options:get_opt(G, be) of + erl_template -> + Name = [Id | N], + UName = mk_uppercase(Name), + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; + erl_corba -> + Name = [Id | N], + UName = mk_uppercase(Name), + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; + false -> %% default == erl_corba + Name = [Id | N], + UName = mk_uppercase(Name), + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; + java -> + Name = [Id | N], + UName = mk_uppercase(Name), + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; + erl_genserv -> + Name = [Id | N], + UName = mk_uppercase(Name), + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; + erl_plain -> + Name = [Id | N], + UName = mk_uppercase(Name), + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK; + _Be -> + Name = [Id | N], + UName = mk_uppercase(Name), + case ets:lookup(S, Name) of + [_] -> ic_error:error(G, {multiply_defined, X}); + [] -> + case ets:lookup(S, UName) of + [] -> ok; + [_] -> ic_error:error(G, {illegal_spelling, X}) + end + end, + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK + end; +tktab_add_id(G, S, N, X, Id, TK, Aux) -> + Name = [Id | N], + UName = mk_uppercase(Name), + case ets:lookup(S, Name) of + [{_, forward, _, _}] when is_record(X, interface) -> ok; + [XX] when is_record(X, forward) andalso element(2, XX)==interface -> ok; + [_] -> ic_error:error(G, {multiply_defined, X}); + [] -> + case ets:lookup(S, UName) of + [] -> ok; + [_] -> ic_error:error(G, {illegal_spelling, X}) + end + end, + ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), + if UName =/= Name -> ets:insert(S, {UName, spellcheck}); + true -> true end, + TK. + + + + +%%-------------------------------------------------------------------- +%% enum_body +%% +%% Special because ids are treated different than usual. +%% +enum_body(G, S, N, [Enum | EnumList]) -> + tktab_add(G, S, N, Enum), %%%, enum_val, Enum), + %% tktab_add(G, S, N, X, TK, V), + [ic_forms:get_id2(Enum) | enum_body(G, S, N, EnumList)]; +enum_body(_G, _S, _N, []) -> []. + + +%%-------------------------------------------------------------------- +%% mk_array +%% +%% Multi dimensional arrays are written as nested tk_array +%% +mk_array(G, S, N, [Sz | Szs], TK) -> + case iceval:eval_const(G, S, N, positive_int, Sz) of + Err when element(1, Err) == error -> TK; + Val -> + {tk_array, mk_array(G, S, N, Szs, TK), iceval:get_val(Val)} + end; +mk_array(_G, _S, _N, [], TK) -> TK. + + +%%-------------------------------------------------------------------- +%% len_eval +%% +%% Evaluates the length, which in case it has been left out is a +%% plain 0 (zero) +%% +len_eval(_G, _S, _N, 0) -> 0; +len_eval(G, S, N, X) -> %%iceval:eval_const(G, S, N, positive_int, X). + case iceval:eval_const(G, S, N, positive_int, X) of + Err when element(1, Err) == error -> 0; + Val -> iceval:get_val(Val) + end. + + +%%-------------------------------------------------------------------- +%% case_eval +%% +%% Evaluates the case label. +%% + +case_eval(G, S, N, DiscrTK, X) when element(1, DiscrTK) == tk_enum, + element(1, X) == scoped_id -> + {tk_enum, _, _, Cases} = DiscrTK, + Id = get_case_id_and_check(G, S, N, X, X), + %%io:format("Matching: ~p to ~p~n", [Id, Cases]), + case lists:member(Id, Cases) of + true -> + {enum_id, Id}; + false -> + iceval:mk_val(scoped_lookup(G, S, N, X)) % Will generate error + end; + +case_eval(G, S, N, DiscrTK, X) -> + iceval:eval_e(G, S, N, DiscrTK, X). + + +%% The enum declarator is in the union scope. +do_special_enum(G, S, N, X) when is_record(X, enum) -> + tktab_add(G, S, N, #id_of{id=X#enum.id, type=X}); +do_special_enum(_G, _S, _N, _X) -> + ok. + + +unique_add_case_label(G, _S, _N, Id, TK, TKList) -> +%%%io:format("check_case_labels: TK:~p TKLIST:~p ~n", [TK, TKList]), + if element(1, TK) == error -> + TKList; + true -> + case lists:keysearch(element(1, TK), 1, TKList) of + {value, _} -> + ic_error:error(G, {multiple_cases, Id}), + TKList; + false -> + [TK | TKList] + end + end. + + +%%-------------------------------------------------------------------- +%% default_count +%% +%% Returns the position of the default case. +%% +%% Modified for OTP-2007 +%% +default_count(Xs) -> + default_count2(Xs, 0). + +default_count2([X | Xs], N) -> default_count3(X#case_dcl.label, Xs, N); +default_count2([], _) -> -1. + +default_count3([{default, _} | _Ys], _Xs, N) -> N; +default_count3([_ | Ys], Xs, N) -> default_count3(Ys, Xs, N+1); +default_count3([], Xs, N) -> default_count2(Xs, N). + + + + +%% +%% Type checks. +%% +%% Check constant type references (only for the scoped id case, others +%% are caught by the BNF) +%% +check_const_tk(_G, _S, _N, _X, tk_long) -> true; +check_const_tk(_G, _S, _N, _X, tk_longlong) -> true; %% LLONG +check_const_tk(_G, _S, _N, _X, tk_short) -> true; +check_const_tk(_G, _S, _N, _X, tk_ushort) -> true; +check_const_tk(_G, _S, _N, _X, tk_ulong) -> true; +check_const_tk(_G, _S, _N, _X, tk_ulonglong) -> true; %% ULLONG +check_const_tk(_G, _S, _N, _X, tk_float) -> true; +check_const_tk(_G, _S, _N, _X, tk_double) -> true; +check_const_tk(_G, _S, _N, _X, tk_boolean) -> true; +check_const_tk(_G, _S, _N, _X, tk_char) -> true; +check_const_tk(_G, _S, _N, _X, tk_wchar) -> true; %% WCHAR +check_const_tk(_G, _S, _N, _X, tk_octet) -> true; +check_const_tk(_G, _S, _N, _X, {tk_string, _Len}) -> true; +check_const_tk(_G, _S, _N, _X, {tk_wstring, _Len}) -> true; %% WSTRING +check_const_tk(_G, _S, _N, _X, tk_fixed) -> true; +check_const_tk(_G, _S, _N, _X, {tk_fixed, _Digits, _Scale}) -> true; +check_const_tk(G, _S, _N, X, TK) -> ic_error:error(G, {illegal_const_t, X, TK}). + + +check_switch_tk(_G, _S, _N, _X, tk_long) -> true; +check_switch_tk(_G, _S, _N, _X, tk_longlong) -> true; %% LLONG +check_switch_tk(_G, _S, _N, _X, tk_short) -> true; +check_switch_tk(_G, _S, _N, _X, tk_ushort) -> true; +check_switch_tk(_G, _S, _N, _X, tk_ulong) -> true; +check_switch_tk(_G, _S, _N, _X, tk_ulonglong) -> true; %% ULLONG +check_switch_tk(_G, _S, _N, _X, tk_boolean) -> true; +check_switch_tk(_G, _S, _N, _X, tk_char) -> true; +check_switch_tk(_G, _S, _N, _X, tk_wchar) -> true; %% WCHAR +check_switch_tk(_G, _S, _N, _X, TK) when element(1, TK) == tk_enum -> true; +check_switch_tk(G, _S, _N, X, TK) -> ic_error:error(G, {illegal_switch_t, X, TK}), + false. + + + +%% Lookup a name +name_lookup(G, S, N, X) -> + case scoped_lookup(G, S, N, X) of + T when is_tuple(T) -> element(1, T) + end. + + +lookup(G, S, N, X, Id) -> + N2 = Id ++ N, + ?DBG(" Trying ~p ...~n", [N2]), + case ets:lookup(S, N2) of + [] -> + case look_for_interface(G, S, [hd(N2)], tl(N2)) of + + %% First attempt: filtering inherited members ! + [{_, member, _, _}] -> + case look_for_interface(G, S, [hd(N)], tl(N2)) of + [T] -> + ?DBG(" -- found ~p~n", [T]), + T; + _ -> + lookup(G, S, tl(N), X, Id) + end; + %% + + [T] -> + ?DBG(" -- found ~p~n", [T]), + T; + + _ -> + if N == [] -> + ic_error:error(G, {tk_not_found, X}); + true -> + lookup(G, S, tl(N), X, Id) + end + + end; + + %% Second attempt: filtering members ! + [{_, member, _, _}] -> + case look_for_interface(G, S, [hd(N2)], tl(N2)) of + [T] -> + ?DBG(" -- found ~p~n", [T]), + T; + _ -> + if N == [] -> + ic_error:error(G, {tk_not_found, X}); + true -> + lookup(G, S, tl(N), X, Id) + end + end; + %% + [T] -> + ?DBG(" -- found ~p~n", [T]), + T + end. + + +look_for_interface(_G, _S, _Hd, []) -> + false; +look_for_interface(G, S, Hd, Tl) -> + case ets:lookup(S, Tl) of + [{_, interface, _TK, Inh}] -> + case look_in_inherit(G, S, Hd, Inh) of + %% gather_inherit(G, S, Inh, [])) of + [X] when is_tuple(X) -> + [X]; + _ -> + look_for_interface(G, S, Hd ++ [hd(Tl)], tl(Tl)) + end; + _ -> + look_for_interface(G, S, Hd ++ [hd(Tl)], tl(Tl)) + end. + +look_in_inherit(G, S, Id, [I | Is]) -> + case ets:lookup(S, Id ++ I) of + [X] when is_tuple(X) -> + [X]; + [] -> + look_in_inherit(G, S, Id, Is) + end; +look_in_inherit(_G, _S, _Id, []) -> + false. + + +%% L is a list of names +mk_uppercase(L) -> + lists:map(fun(Z) -> lists:map(fun(X) when X>=$a, X=<$z -> X-$a+$A; + (X) -> X end, Z) end, L). + + +%%-------------------------------------------------------------------- +%% +%% Inheritance stuff +%% +%% +%%-------------------------------------------------------------------- + +%% InhBody is an accumulating parameter + +calc_inherit_body(G, N, OrigBody, [X|Xs], InhBody) -> + case ic_symtab:retrieve(G, X) of + Intf when is_record(Intf, interface) -> + Body = filter_body(G, X, ic_forms:get_body(Intf), N, OrigBody, InhBody), + calc_inherit_body(G, N, OrigBody, Xs, [{X, Body} | InhBody]); + XXX -> + io:format("Oops, not found ~p~n", [XXX]), + calc_inherit_body(G, N, OrigBody, Xs, InhBody) + end; +calc_inherit_body(_G, _N, _OrigBody, [], InhBody) -> lists:reverse(InhBody). + + +filter_body(G, XPath, [X | Xs], OrigPath, OrigBody, InhBody) -> + case complex_body_member(G, XPath, X, OrigPath, OrigBody, InhBody) of + true -> + %%io:format("NOT adding ~p~n", [ic_forms:get_id2(X)]), + filter_body(G, XPath, Xs, OrigPath, OrigBody, InhBody); + {false, NewX} -> % For those with idlist + %%io:format("Adding from idlist~n", []), + [NewX | filter_body(G, XPath, Xs, OrigPath, OrigBody, InhBody)]; + false -> + %%io:format("Adding: ~p~n", [ic_forms:get_id2(X)]), + [X | filter_body(G, XPath, Xs, OrigPath, OrigBody, InhBody)] + end; +filter_body(_G, _XPath, [], _OrigPath, _OrigBody, _InhBody) -> []. + + +complex_body_member(G, XPath, X, OrigPath, OrigBody, InhBody) -> + case has_idlist(X) of + true -> + idlist_member(G, XPath, X, OrigPath, OrigBody, InhBody); + false -> + straight_member(G, XPath, X, OrigPath, OrigBody, InhBody) + end. + + +idlist_member(G, XPath, X, OrigPath, OrigBody, InhBody) -> + XX = #id_of{type=X}, + F = fun(Id) -> + not(straight_member(G, XPath, XX#id_of{id=Id}, OrigPath, + OrigBody, InhBody)) + end, + case lists:filter(F, ic_forms:get_idlist(X)) of + [] -> + true; + IdList -> +%%% io:format("Idlist added: ~p~n",[IdList]), + {false, replace_idlist(X, IdList)} + end. + + +straight_member(G, XPath, X, OrigPath, OrigBody, InhBody) -> + %%io:format("straight member: ~p~n", [ic_forms:get_id2(X)]), + case body_member(G, XPath, X, OrigPath, OrigBody) of + true -> + true; + false -> + inh_body_member(G, XPath, X, InhBody) + end. + + +inh_body_member(G, XPath, X, [{Name, Body} | InhBody]) -> + case body_member(G, XPath, X, Name, Body) of + true -> + true; + false -> + inh_body_member(G, XPath, X, InhBody) + end; +inh_body_member(_G, _XPath, _X, []) -> false. + + +body_member(G, XPath, X, YPath, [Y|Ys]) -> + case has_idlist(Y) of + true -> + YY = #id_of{type=Y}, + case list_and(fun(Y2) -> + not(is_equal(G, XPath, X, YPath, + YY#id_of{id=Y2})) end, + ic_forms:get_idlist(Y)) of + true -> + body_member(G, XPath, X, YPath, Ys); + false -> + true + end; + false -> + case is_equal(G, XPath, X, YPath, Y) of + false -> + body_member(G, XPath, X, YPath, Ys); + true -> + true + end + end; +body_member(_G, _XPath, _X, _YPath, []) -> false. + + +is_equal(G, XPath, X, YPath, Y) -> + case {ic_forms:get_id2(X), ic_forms:get_id2(Y)} of + {ID, ID} -> + collision(G, XPath, X, YPath, Y), + true; + _ -> + false + end. + + +%% X is the new item, Y is the old one. So it is X that collides with +%% Y and Y shadows X. +collision(G, XPath, X, YPath, Y) -> + I1 = get_beef(X), + % I2 = get_beef(Y), + if is_record(I1, op) -> %%, record(I2, op) -> + ic_error:error(G, {inherit_name_collision, + {YPath, Y}, {XPath, X}}); + is_record(I1, attr) -> %%, record(I2, attr) -> + ic_error:error(G, {inherit_name_collision, + {YPath, Y}, {XPath, X}}); + true -> + ?ifopt(G, warn_name_shadow, + ic_error:warn(G, {inherit_name_shadow, + {YPath, Y}, {XPath, X}})) + end. + +has_idlist(X) when is_record(X, typedef) -> true; +has_idlist(X) when is_record(X, member) -> true; +has_idlist(X) when is_record(X, case_dcl) -> true; +has_idlist(X) when is_record(X, attr) -> true; +has_idlist(_) -> false. + +replace_idlist(X, IdList) when is_record(X, typedef) -> X#typedef{id=IdList}; +replace_idlist(X, IdList) when is_record(X, attr) -> X#attr{id=IdList}. + +get_beef(X) when is_record(X, id_of) -> X#id_of.type; +get_beef(X) -> X. + + +%% And among all elements in list +list_and(F, [X|Xs]) -> + case F(X) of + true -> list_and(F, Xs); + false -> false + end; +list_and(_F, []) -> true. + + + + + +%%-------------------------------------------------------------------- +%% +%% resolve_inherit shall return a list of resolved inheritances, +%% that is all names replaced with their global names. +%% + +inherit_resolve(G, S, N, [X|Rest], Out) -> + case scoped_lookup(G, S, N, X) of + {Name, _T, _TK, Inh} -> + case lists:member(Name, Out) of + true -> + inherit_resolve(G, S, N, Rest, Out); + false -> + case unique_append(Inh, [Name|Out]) of + error -> + ic_error:error(G, {inherit_resolve, X, Name}), + inherit_resolve(G, S, N, Rest, []); + UA -> + inherit_resolve(G, S, N, Rest, UA) + end + end; + _ -> inherit_resolve(G, S, N, Rest, Out) + end; +inherit_resolve(_G, _S, _N, [], Out) -> lists:reverse(Out). + +unique_append([X|Xs], L) -> + case lists:member(X, L) of + true -> unique_append(Xs, L); + false -> unique_append(Xs, [X|L]) + end; +unique_append([], L) -> L; +%% Error +unique_append(_, _L) -> error. + + + + +%%-------------------------------------------------------------------- +%% +%% Utilities +%% + +%% Must preserve order, therefore had to write my own (instead of lists:map) +check_list(G, S, N, [X|Xs]) -> + X1 = check(G, S, N, X), + [X1 | check_list(G, S, N, Xs)]; +check_list(_G, _S, _N, []) -> []. + + + +filter( [] ) -> + error; +filter( [I | Is ] ) -> + case I of + { _, member, { _, TKINFO }, _ } -> + fetchType( TKINFO ); + + { _, struct, _, _ } -> + struct; + + { _, typedef, TKINFO, _ } -> + fetchType( TKINFO ); + + { _, module, _, _ } -> + module; + + { _, interface, _, _ } -> + interface; + + { _, op, _, _ } -> + op; + + { _,enum, _, _ } -> + enum; + + { _, spellcheck } -> + filter( Is ); + + _ -> + error + end. + + +fetchType( { tk_sequence, _, _ } ) -> + sequence; +fetchType( { tk_array, _, _ } ) -> + array; +fetchType( { tk_struct, _, _, _} ) -> + struct; +fetchType( { tk_string, _} ) -> + string; +fetchType( { tk_wstring, _} ) -> %% WSTRING + wstring; +fetchType( { tk_fixed, _, _} ) -> + fixed; +fetchType( tk_short ) -> + short; +fetchType( tk_long ) -> + long; +fetchType( tk_longlong ) -> %% LLONG + longlong; +fetchType( tk_ushort ) -> + ushort; +fetchType( tk_ulong ) -> + ulong; +fetchType( tk_ulonglong ) -> %% ULLONG + ulonglong; +fetchType( tk_float ) -> + float; +fetchType( tk_double ) -> + double; +fetchType( tk_boolean ) -> + boolean; +fetchType( tk_char ) -> + char; +fetchType( tk_wchar ) -> %% WCHAR + wchar; +fetchType( tk_octet ) -> + octet; +fetchType( { tk_enum, _, _, _ } ) -> + enum; +fetchType( { tk_union, _, _, _, _, _ } ) -> + union; +fetchType( tk_any ) -> + any; +fetchType( _ ) -> + error. + +%% Z is a single name +to_uppercase(Z) -> + lists:map(fun(X) when X>=$a, X=<$z -> X-$a+$A; + (X) -> X end, Z). + + +%%------------------------------------------------------------ +%% +%% Always fetchs TK of a record. +%% +%%------------------------------------------------------------ +fetchTk(G,N,X) -> + case ic_forms:get_tk(X) of + undefined -> + searchTk(G,ictk:get_IR_ID(G, N, X)); + TK -> + TK + end. + + +%%------------------------------------------------------------ +%% +%% seek type code when not accessible by get_tk/1 +%% +%%------------------------------------------------------------ +searchTk(G,IR_ID) -> + S = ic_genobj:tktab(G), + case catch searchTk(S,IR_ID,typedef) of + {value,TK} -> + TK; + _ -> %% false / exit + case catch searchTk(S,IR_ID,struct) of + {value,TK} -> + TK; + _ -> %% false / exit + case catch searchTk(S,IR_ID,union) of + {value,TK} -> + TK; + _ -> + undefined + end + end + end. + + +searchTk(S,IR_ID,Type) -> + L = lists:flatten(ets:match(S,{'_',Type,'$1','_'})), + case lists:keysearch(IR_ID,2,L) of + {value,TK} -> + {value,TK}; + false -> + searchInsideTks(L,IR_ID) + end. + + +searchInsideTks([],_IR_ID) -> + false; +searchInsideTks([{tk_array,TK,_}|Xs],IR_ID) -> + case searchIncludedTk(TK,IR_ID) of + {value,TK} -> + {value,TK}; + false -> + searchInsideTks(Xs,IR_ID) + end. + + +searchIncludedTk({tk_array,TK,_},IR_ID) -> + searchIncludedTk(TK,IR_ID); +searchIncludedTk({tk_sequence,TK,_},IR_ID) -> + searchIncludedTk(TK,IR_ID); +searchIncludedTk(TK, _IR_ID) when is_atom(TK) -> + false; +searchIncludedTk(TK,IR_ID) -> + case element(2,TK) == IR_ID of + true -> + {value,TK}; + false -> + false + end. + diff --git a/lib/ic/src/icunion.erl b/lib/ic/src/icunion.erl new file mode 100644 index 0000000000..38a2d14913 --- /dev/null +++ b/lib/ic/src/icunion.erl @@ -0,0 +1,1490 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(icunion). + +-import(ic_codegen, [emit/2, emit/3, emit/4, emit_c_enc_rpt/4, emit_c_dec_rpt/4]). +-import(ic_cbe, [mk_c_type/3, mk_c_type/4]). + +-include("icforms.hrl"). +-include("ic.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([union_gen/4]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([]). + +union_gen(G, N, X, c) when is_record(X, union) -> + emit_c_union(G, N, X); +union_gen(_G, _N, _X, _L) -> + ok. + + +%% Emits the union +emit_c_union(G, N, X) -> + %%io:format("Rec = ~p\n",[X]), + case ic_genobj:is_hrlfile_open(G) of + true -> + + %% Sort Union Default = put it last in case list + NewX = #union{ id = X#union.id, + type = X#union.type, + body = mvDefaultToTail(X#union.body), + tk = X#union.tk }, + + UnionScope = [ic_forms:get_id2(NewX) | N], + + case ic_pragma:is_local(G,UnionScope) of + + true -> + + HFd = ic_genobj:hrlfiled(G), + emit_c_union_values(G, N, NewX, HFd), + UnionName = ic_util:to_undersc(UnionScope), + + emit(HFd, "\n#ifndef __~s__\n",[ictype:to_uppercase(UnionName)]), + emit(HFd, "#define __~s__\n",[ictype:to_uppercase(UnionName)]), + ic_codegen:mcomment_light(HFd, + [io_lib:format("Union definition: ~s", + [UnionName])], + c), + emit(HFd, "typedef struct {\n"), + emit(HFd, " ~s _d;\n", [get_c_union_discriminator(G, N, NewX)]), + emit(HFd, " union {\n"), + emit_c_union_values_decl(G, N, NewX, HFd), + emit(HFd, " } _u;\n"), + emit(HFd, "} ~s;\n\n", [UnionName]), + + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, int*, int*);\n", + [ic_util:mk_oe_name(G, "sizecalc_"), UnionName]), + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, ~s*);\n", + [ic_util:mk_oe_name(G, "encode_"), UnionName, UnionName]), + emit(HFd, "int ~s~s(CORBA_Environment *oe_env, char *, int*, ~s*);\n", + [ic_util:mk_oe_name(G, "decode_"), UnionName, UnionName]), + emit(HFd, "\n#endif\n\n"), + create_c_union_file(G, N, NewX, UnionName); + + false -> %% Do not generate included types att all. + ok + end; + false -> + ok + end. + + + +%% Loops over union members and creates members typedefs +emit_c_union_values(G, N, X, Fd) -> + emit_c_union_values_loop(G, N, X, Fd, X#union.body). + +emit_c_union_values_loop(G, N, X, Fd, [CU]) -> + case CU of + {case_dcl,_,Id,Type} -> + case Id of + {array, _AID, _SZ} -> % Check for arrays + mk_array_file(G,N,X,Id,Type,Fd); + _ -> % Elementary types or seq/struct + ok + end; + _ -> + error + end; +emit_c_union_values_loop(G, N, X, Fd, [CU |CUs]) -> + case CU of + {case_dcl,_,Id,Type} -> + case Id of + {array, _AID, _SZ} -> % Check for arrays + mk_array_file(G,N,X,Id,Type,Fd); + _ -> % Elementary types or seq/struct + emit_c_union_values_loop(G, N, X, Fd, CUs) + end; + _ -> + error + end. + + +%% Loops over union members and declares members inside union structure +emit_c_union_values_decl(G, N, X, Fd) -> + emit_c_union_values_decl_loop(G, N, X, Fd, X#union.body). + +emit_c_union_values_decl_loop(G, N, X, Fd, [CU]) -> + case CU of + {case_dcl,_,Id,Type} -> + case Id of + {array, _AID, _SZ} -> % Check for arrays + mk_array_decl(G,N,X,Id,Type,Fd); + _ -> % Elementary types or seq/struct + mk_union_member_decl(G,N,X,Id,Type,Fd), + ok + end; + _ -> + error + end; +emit_c_union_values_decl_loop(G, N, X, Fd, [CU |CUs]) -> + case CU of + {case_dcl,_,Id,Type} -> + case Id of + {array, _AID, _SZ} -> % Check for arrays + mk_array_decl(G,N,X,Id,Type,Fd), + emit_c_union_values_decl_loop(G, N, X, Fd, CUs); + _ -> % Elementary types or seq/struct + mk_union_member_decl(G,N,X,Id,Type,Fd), + emit_c_union_values_decl_loop(G, N, X, Fd, CUs) + end; + _ -> + error + end. + + +%% Makes the declaration for the array in union +mk_array_decl(G,N,X,Id,Type,Fd) -> + emit(Fd, " ~s ~s;\n", + [getCaseTypeStr(G,N,X,Id,Type), + mk_array_name(Id)]). + +mk_array_name({array,Id,D}) -> + ic_forms:get_id2(Id) ++ mk_array_dim(D). + +mk_array_dim([]) -> + ""; +mk_array_dim([{_,_,Dim}|Dims]) -> + "[" ++ Dim ++ "]" ++ mk_array_dim(Dims). + + +%% Creates the array file +mk_array_file(G,N,X,{array,AID,SZ},Type,HFd) -> + ArrayName = ic_util:to_undersc([ic_forms:get_id2(AID),ic_forms:get_id2(X) | N]), + ArrayDim = extract_array_dim(SZ), + emit(HFd, "\n#ifndef __~s__\n",[ictype:to_uppercase(ArrayName)]), + emit(HFd, "#define __~s__\n\n",[ictype:to_uppercase(ArrayName)]), + icstruct:create_c_array_coding_file(G, + N, + {ArrayName,ArrayDim}, + Type, + no_typedef), + emit(HFd, "\n#endif\n\n"). + +extract_array_dim([{_,_,Dim}]) -> + [Dim]; +extract_array_dim([{_,_,Dim}|Dims]) -> + [Dim | extract_array_dim(Dims)]. + + +%% Makes the declaration for the member in union +mk_union_member_decl(G,N,X,Id,Type,Fd) -> + emit(Fd, " ~s ~s;\n", + [getCaseTypeStr(G,N,X,Id,Type), + ic_forms:get_id2(Id)]). + + + + +%% File utilities +create_c_union_file(G, N, X, UnionName) -> + + {Fd , SName} = open_c_coding_file(G, UnionName), + _HFd = ic_genobj:hrlfiled(G), %% Write on stubfile header + HrlFName = filename:basename(ic_genobj:include_file(G)), + ic_codegen:emit_stub_head(G, Fd, SName, c), + emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% Fd = ic_genobj:stubfiled(G), %% Write on stubfile + %% HFd = ic_genobj:hrlfiled(G), %% Write on stubfile header + %% HrlFName = filename:basename(ic_genobj:include_file(G)), + %% emit(Fd, "#include \"~s\"\n\n",[HrlFName]), + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + put(op_variable_count, 0), + put(tmp_declarations, []), + + %% Write generated code on file + emit_union_sizecount(G, N, X, Fd, UnionName), + emit_union_encode(G, N, X, Fd, UnionName), + emit_union_decode(G, N, X, Fd, UnionName), + file:close(Fd). + +open_c_coding_file(G, Name) -> + SName = string:concat(ic_util:mk_oe_name(G, "code_"), Name), + FName = + ic_file:join(ic_options:get_opt(G, stubdir),ic_file:add_dot_c(SName)), + case file:open(FName, [write]) of + {ok, Fd} -> + {Fd, SName}; + Other -> + exit(Other) + end. + + + + +get_c_union_discriminator(G, N, X) -> + case getDiscrStr(G, N, X#union.type) of + error -> + ic_error:fatal_error(G, {illegal_typecode_for_c, X#union.type, N}); + DiscrStr -> + case ic_code:get_basetype(G, DiscrStr) of + {short, _} -> + "CORBA_short"; + {unsigned,{short, _}} -> + "CORBA_unsigned_short"; + {long, _} -> + "CORBA_long"; + {unsigned,{long, _}} -> + "CORBA_unsigned_long"; + {boolean,_} -> + "CORBA_boolean"; + {char,_} -> + "CORBA_char"; + {enum, EnumType} -> + EnumType; + _ -> + DiscrStr + end + end. + +getDiscrStr(G, N, S) when element(1, S) == scoped_id -> + case ic_symtab:get_full_scoped_name(G, N, S) of + {FSN, _, tk_short, _} -> + ic_util:to_undersc(FSN); + {FSN, _, tk_ushort, _} -> + ic_util:to_undersc(FSN); + {FSN, _, tk_long, _} -> + ic_util:to_undersc(FSN); + {FSN, _, tk_ulong, _} -> + ic_util:to_undersc(FSN); + {FSN, _, tk_boolean, _} -> + ic_util:to_undersc(FSN); + {FSN, _, tk_char, _} -> + ic_util:to_undersc(FSN); + {FSN, _, {tk_enum,_,_,_}, _} -> + ic_util:to_undersc(FSN); + _ -> + error + end; +getDiscrStr(_G, N, X) -> + case X of + {short,_} -> + "CORBA_short"; + {unsigned,{short,_}} -> + "CORBA_unsigned_short"; + {long, _} -> + "CORBA_long"; + {unsigned,{long,_}} -> + "CORBA_unsigned_long"; + {boolean,_} -> + "CORBA_boolean"; + {char,_} -> + "CORBA_char"; + {enum,TID,_,_} -> + ic_util:to_undersc([ic_forms:get_id2(TID) | N]); + _ -> + error + end. + + + + +getCaseTypeStr(G, N, X, I, T) when element(1, T) == scoped_id -> + case catch ic_symtab:get_full_scoped_name(G, N, T) of + {FSN, _, _, _} -> + BT = ic_code:get_basetype(G, ic_util:to_undersc(FSN)), + case isList(BT) of + true -> + BT; + false -> + case BT of + {short,_} -> + "CORBA_short"; + {unsigned,{short,_}} -> + "CORBA_unsigned_short"; + {long, _} -> + "CORBA_long"; + {unsigned,{long,_}} -> + "CORBA_unsigned_long"; + {float,_} -> + "CORBA_float"; + {double,_} -> + "CORBA_double"; + {boolean,_} -> + "CORBA_boolean"; + {char,_} -> + "CORBA_char"; + {wchar,_} -> + "CORBA_wchar"; + {octet,_} -> + "CORBA_octet"; + {string,_} -> + "CORBA_char*"; + {wstring,_} -> + "CORBA_wchar*"; + {sequence,_,_} -> + ic_util:to_undersc([ic_forms:get_id2(I), ic_forms:get_id2(X) | N]); + {struct,SID,_,_} -> + ic_util:to_undersc([ic_forms:get_id2(SID), ic_forms:get_id2(X) | N]); + {enum,EID} -> + EID; + {any, _} -> %% Fix for any type + "CORBA_long"; + _ -> + %%io:format("BT = ~p~n",[BT]), + error + end + end + end; +getCaseTypeStr(_G, N, X, I, T) -> + case T of + {short,_} -> + "CORBA_short"; + {unsigned,{short,_}} -> + "CORBA_unsigned_short"; + {long, _} -> + "CORBA_long"; + {unsigned,{long,_}} -> + "CORBA_unsigned_long"; + {float,_} -> + "CORBA_float"; + {double,_} -> + "CORBA_double"; + {boolean,_} -> + "CORBA_boolean"; + {char,_} -> + "CORBA_char"; + {wchar,_} -> + "CORBA_wchar"; + {octet,_} -> + "CORBA_octet"; + {string,_} -> + "CORBA_char*"; + {wstring,_} -> + "CORBA_wchar*"; + {sequence,_,_} -> + ic_util:to_undersc([ic_forms:get_id2(I), ic_forms:get_id2(X) | N]); + {struct,SID,_,_} -> + ic_util:to_undersc([ic_forms:get_id2(SID), ic_forms:get_id2(X) | N]); + {union,UID,_,_,_} -> + ic_util:to_undersc([ic_forms:get_id2(UID), ic_forms:get_id2(X) | N]); + {any, _} -> %% Fix for any type + "CORBA_long"; + _ -> + error + end. + +isList(L) when is_list(L) -> + true; +isList(_) -> + false. + +%% +%% Sizecount facilities +%% +emit_union_sizecount(G, N, X, Fd, UnionName) -> + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, int* oe_size_count_index, int* oe_size) {\n\n", + [ic_util:mk_oe_name(G, "sizecalc_"), UnionName]), + + emit(Fd, " int oe_malloc_size = 0;\n"), + emit(Fd, " int oe_error_code = 0;\n"), + emit(Fd, " int oe_type = 0;\n"), + emit(Fd, " int oe_tmp = 0;\n"), + emit_union_discr_var_decl(G, N, X, Fd), + + ic_codegen:nl(Fd), + emit(Fd, " if(*oe_size == 0)\n",[]), + AlignName = lists:concat(["*oe_size + sizeof(",UnionName,")"]), + emit(Fd, " *oe_size = ~s;\n\n", [ic_util:mk_align(AlignName)]), + + emit(Fd, " if ((oe_error_code = ei_get_type(oe_env->_inbuf, oe_size_count_index, &oe_type, &oe_tmp)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + + %%emit(Fd, " if (oe_tmp != 3)\n"), + %%emit(Fd, " return -1;\n\n"), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n", []), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit_c_union_discr_sizecount(G, N, X, Fd), + emit(Fd, " /* Calculate union size */\n"), + emit(Fd, " switch(oe_discr) {\n"), + + emit_c_union_loop(G, N, X, Fd, X#union.body, sizecalc), + emit(Fd, " }\n\n"), + + emit(Fd, " *oe_size = ~s;\n",[ic_util:mk_align("*oe_size+oe_malloc_size")]), + emit(Fd, " return 0;\n"), + emit(Fd, "}\n\n\n"). + + +emit_union_discr_var_decl(G, N, X, Fd) -> + UD = get_c_union_discriminator(G, N, X), + case UD of + "CORBA_short" -> + emit(Fd, " long oe_discr = 0;\n"); + "CORBA_unsigned_short" -> + emit(Fd, " unsigned long oe_discr = 0;\n"); + "CORBA_long" -> + emit(Fd, " long oe_discr = 0;\n"); + "CORBA_unsigned_long" -> + emit(Fd, " unsigned long oe_discr = 0;\n"); + "CORBA_boolean" -> + emit(Fd, " int oe_discr = 0;\n"), + emit(Fd, " char oe_bool[256];\n"); + "CORBA_char" -> + emit(Fd, " char oe_discr = 0;\n"); + _T -> + emit(Fd, " int oe_dummy = 0;\n"), + emit(Fd, " ~s oe_discr = 0;\n",[UD]) + end. + + +emit_c_union_discr_sizecount(G, N, X, Fd) -> + emit(Fd, " /* Calculate discriminator size */\n"), + UD = get_c_union_discriminator(G, N, X), + case UD of + "CORBA_short" -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, &oe_discr)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_unsigned_short" -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, oe_size_count_index, &oe_discr)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_long" -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, &oe_discr)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_unsigned_long" -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, oe_size_count_index, &oe_discr)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_boolean" -> + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, oe_size_count_index, oe_bool)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if (strcmp(oe_bool, \"false\") == 0) {\n"), + emit(Fd, " oe_discr = 0;\n"), + emit(Fd, " }\n"), + emit(Fd, " else if (strcmp(oe_bool, \"true\") == 0) {\n"), + emit(Fd, " oe_discr = 1;\n"), + emit(Fd, " }\n"), + emit(Fd, " else {\n"), + emit_c_dec_rpt(Fd, " ", "not boolean", []), + emit(Fd, " return -1;\n }\n"); + + "CORBA_char" -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, oe_size_count_index, &oe_discr)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + T -> + emit(Fd, " oe_tmp = *oe_size_count_index;\n"), + emit(Fd, " if ((oe_error_code = oe_sizecalc_~s(oe_env, oe_size_count_index, &oe_malloc_size)) < 0) {\n", [T]), + ?emit_c_dec_rpt(Fd, " ", "oe_size_calc_~s", [T]), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " *oe_size_count_index = oe_tmp;\n"), + emit(Fd, " oe_tmp = oe_env->_iin;\n"), + emit(Fd, " oe_env->_iin = *oe_size_count_index;\n"), + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, NULL, &oe_dummy, &oe_discr)) < 0) {\n", [T]), + ?emit_c_dec_rpt(Fd, " ", "oe_decode_~s", [T]), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " *oe_size_count_index = oe_env->_iin;\n"), + emit(Fd, " oe_env->_iin = oe_tmp;\n\n") + end. + + + +emit_c_union_loop(G, N, X, Fd, CaseList, Case) -> + emit_c_union_loop(G, N, X, Fd, CaseList, false, Case). + +emit_c_union_loop(G, N, X, Fd, [], GotDefaultCase, Case) -> + case GotDefaultCase of + false -> + emit_c_union_valueless_discriminator(G, N, X, Fd, Case) + end; +emit_c_union_loop(G, N, X, Fd, [CU|CUs], GotDefaultCase, Case) -> + case CU of + {case_dcl,CaseList,I,T} -> + GotDefaultCase = emit_c_union_case(G, N, X, Fd, I, T, CaseList, Case), + emit_c_union_loop(G, N, X, Fd, CUs, GotDefaultCase, Case); + _ -> + error + end. + +emit_c_union_valueless_discriminator(_G, _N, _X, Fd, Case) -> + emit(Fd, " default:\n"), + case Case of + sizecalc -> + emit(Fd, " {\n"), + emit(Fd, " char oe_undefined[15];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, " + "oe_size_count_index, oe_undefined)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " }\n"); + encode -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"undefined\")) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"); + decode -> + emit(Fd, " {\n"), + emit(Fd, " char oe_undefined[15];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, " + "oe_undefined)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if (strcmp(oe_undefined, \"undefined\") != 0) {\n"), + emit_c_dec_rpt(Fd, " ", "undefined", []), + emit(Fd, " return -1;\n }\n"), + emit(Fd, " }\n") + end. + + +emit_c_union_case(G, N, X, Fd, I, T, [{default,_}], Case) -> + emit(Fd, " default:\n"), + case Case of + sizecalc -> + getCaseTypeSizecalc(G, N, X, Fd, I, T); + encode -> + getCaseTypeEncode(G, N, X, Fd, I, T); + decode -> + getCaseTypeDecode(G, N, X, Fd, I, T) + end, + true; +emit_c_union_case(G, N, X, Fd, I, T, [{Bool,_}], Case) -> %% Boolean discriminator + case Bool of + 'TRUE' -> + emit(Fd, " case 1:\n"); + 'FALSE' -> + emit(Fd, " case 0:\n") + end, + case Case of + sizecalc -> + getCaseTypeSizecalc(G, N, X, Fd, I, T); + encode -> + getCaseTypeEncode(G, N, X, Fd, I, T); + decode -> + getCaseTypeDecode(G, N, X, Fd, I, T) + end, + emit(Fd, " break;\n\n"), + false; +emit_c_union_case(G, N, X, Fd, I, T, [{Bool,_}|Rest], Case) -> %% Boolean discriminator + case Bool of + 'TRUE' -> + emit(Fd, " case 1:\n"); + 'FALSE' -> + emit(Fd, " case 0:\n") + end, + emit_c_union_case(G, N, X, Fd, I, T, Rest, Case), + false; +emit_c_union_case(G, N, X, Fd, I, T, [{_,_,NrStr}], Case) -> %% Integer type discriminator + case get_c_union_discriminator(G, N, X) of + "CORBA_char" -> + emit(Fd, " case \'~s\':\n",[NrStr]); + _ -> + emit(Fd, " case ~s:\n",[NrStr]) + end, + case Case of + sizecalc -> + getCaseTypeSizecalc(G, N, X, Fd, I, T); + encode -> + getCaseTypeEncode(G, N, X, Fd, I, T); + decode -> + getCaseTypeDecode(G, N, X, Fd, I, T) + end, + emit(Fd, " break;\n\n"), + false; +emit_c_union_case(G, N, X, Fd, I, T, [{_,_,NrStr}|Rest], Case) -> %% Integer type discriminator + emit(Fd, " case ~s:\n",[NrStr]), + emit_c_union_case(G, N, X, Fd, I, T, Rest, Case), + false; +emit_c_union_case(G, N, X, Fd, I, T, [{scoped_id,_,_,[EID]}], Case) -> %% Enumerant type discriminator + SID = ic_util:to_undersc([EID|get_c_union_discriminator_scope(G, N, X)]), + %%io:format("SID = ~p~n",[SID]), + emit(Fd, " case ~s:\n",[SID]), + case Case of + sizecalc -> + getCaseTypeSizecalc(G, N, X, Fd, I, T); + encode -> + getCaseTypeEncode(G, N, X, Fd, I, T); + decode -> + getCaseTypeDecode(G, N, X, Fd, I, T) + end, + emit(Fd, " break;\n\n"), + false; +emit_c_union_case(G, N, X, Fd, I, T, [{scoped_id,_,_,[EID]}|Rest], Case) -> %% Enumerant type discriminator + SID = ic_util:to_undersc([EID|get_c_union_discriminator_scope(G, N, X)]), + %%io:format("SID = ~p~n",[SID]), + emit(Fd, " case ~s:\n",[SID]), + emit_c_union_case(G, N, X, Fd, I, T, Rest, Case), + false. + + +%% +%% Returns the enumerant discriminator scope +%% +get_c_union_discriminator_scope(G, N, X) -> + {FullScopedName, _, _TK, _} = ic_symtab:get_full_scoped_name(G, N, X#union.type), + BT = case ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)) of + {enum,ST} -> + ST; + Other -> + Other + end, + tl(lists:reverse(string:tokens(BT,"_"))). %% Ugly work arround + + + + + +getCaseTypeSizecalc(G, N, X, Fd, I, T) when element(1, T) == scoped_id -> + case ic_fetch:member2type(G,X,I) of + ushort -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ushort:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + ulong -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ulong:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + short -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "short:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + long -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "long:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + float -> + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "float:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + double -> + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "double:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + boolean -> + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "boolean:ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"); + char -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "char:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + octet -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "octet:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + string -> + emit(Fd, " if ((oe_error_code = ei_get_type(oe_env->_inbuf, oe_size_count_index, &oe_type, &oe_tmp)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if ((oe_error_code = ei_decode_string(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_string", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_malloc_size = ~s;\n",[ic_util:mk_align("oe_malloc_size+oe_tmp+1")]); + any -> %% Fix for any type + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + + _ -> + case getCaseTypeStr(G, N, X, I, T) of + "erlang_pid" -> + emit(Fd, " if ((oe_error_code = ei_decode_pid(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n", + []), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_pid", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + emit(Fd, " if ((oe_error_code = ei_decode_port(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n", + []), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_port", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_ref" -> + emit(Fd, " if ((oe_error_code = ei_decode_ref(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n", + []), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_ref", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_term" -> + emit(Fd, " if ((oe_error_code = ei_decode_term(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n", + []), + ?emit_c_dec_rpt(Fd, " ", "ei_deoce_term", []), + emit(Fd, " return oe_error_code;\n }\n"); + + Other -> + + emit(Fd, " if ((oe_error_code = oe_sizecalc_~s(oe_env, oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [Other]), + ?emit_c_dec_rpt(Fd, " ", "oe_sizecalc_~s", [Other]), + emit(Fd, " return oe_error_code;\n }\n") + end + end; +getCaseTypeSizecalc(G, N, X, Fd, I, T) -> + case I of + {array,_,_} -> + ArrayName = ic_util:to_undersc([ic_forms:get_id2(I),ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_sizecalc_~s(oe_env, oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [ArrayName]), + ?emit_c_dec_rpt(Fd, " ", "oe_sizecalc_~s", [ArrayName]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + case T of + {short,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "short:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + {unsigned,{short,_}} -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ushort:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + {long, _} -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "long:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + {unsigned,{long,_}} -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ulong:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + {float,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "float:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }"); + {double,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "double:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + {boolean,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "boolean:ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"); + {char,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "char:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + {octet,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "octet:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + {string,_} -> + emit(Fd, " if ((oe_error_code = ei_get_type(oe_env->_inbuf, oe_size_count_index, &oe_type, &oe_tmp)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ei_get_type", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if ((oe_error_code = ei_decode_string(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_string", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_malloc_size = ~s;\n",[ic_util:mk_align("oe_malloc_size+oe_tmp+1")]); + {sequence,_,_} -> + SeqName = ic_util:to_undersc([ic_forms:get_id2(I), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_sizecalc_~s(oe_env, oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [SeqName]), + ?emit_c_dec_rpt(Fd, " ", "sequence:oe_sizecalc_~s", [SeqName]), + emit(Fd, " return oe_error_code;\n }\n"); + {struct,SID,_,_} -> + StructName = ic_util:to_undersc([ic_forms:get_id2(SID), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_sizecalc_~s(oe_env, oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [StructName]), + ?emit_c_dec_rpt(Fd, " ", "struct:oe_sizecalc_~s", [StructName]), + emit(Fd, " return oe_error_code;\n }\n"); + {union,UID,_,_,_} -> + UnionName = ic_util:to_undersc([ic_forms:get_id2(UID), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_sizecalc_~s(oe_env, oe_size_count_index, &oe_malloc_size)) < 0) {\n", + [UnionName]), + ?emit_c_dec_rpt(Fd, " ", "union:oe_sizecalce_~s", [UnionName]), + emit(Fd, " return oe_error_code;\n }\n"); + {any, _} -> %% Fix for any type + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, oe_size_count_index, 0)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "any:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end + end. + + + + + +%% +%% Encode facilities +%% +emit_union_encode(G, N, X, Fd, UnionName) -> + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, ~s* oe_rec) {\n\n", + [ic_util:mk_oe_name(G, "encode_"), UnionName, UnionName]), + + emit(Fd, " int oe_error_code = 0;\n\n"), + + emit(Fd, " if ((oe_error_code = oe_ei_encode_tuple_header(oe_env, 3)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"~s\")) < 0) {\n", + [UnionName]), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit_c_union_discr_encode(G, N, X, Fd), + emit(Fd, " /* Encode union */\n"), + emit(Fd, " switch(oe_rec->_d) {\n"), + emit_c_union_loop(G, N, X, Fd, X#union.body, encode), + emit(Fd, " }\n\n"), + emit(Fd, " return 0;\n"), + emit(Fd, "}\n\n\n"). + + +emit_c_union_discr_encode(G, N, X, Fd) -> + emit(Fd, " /* Encode descriminator */\n"), + UD = get_c_union_discriminator(G, N, X), + case UD of + "CORBA_short" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_d)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_unsigned_short" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ulong(oe_env, oe_rec->_d)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_long" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_d)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_unsigned_long" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ulong(oe_env, oe_rec->_d)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_boolean" -> + emit(Fd, " switch(oe_rec->_d) {\n"), + emit(Fd, " case 0:\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"false\")) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " case 1:\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"true\")) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " default:\n"), + emit_c_enc_rpt(Fd, " ", "boolean failure", []), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n\n"); + "CORBA_char" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_char(oe_env, oe_rec->_d)) < 0) {\n"), + emit_c_enc_rpt(Fd, " ", "oe_ei_encode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + T -> + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, oe_rec->_d)) < 0) {\n", [T]), + ?emit_c_enc_rpt(Fd, " ", "oe_encode_~s", [T]), + emit(Fd, " return oe_error_code;\n }\n") + end. + + +getCaseTypeEncode(G, N, X, Fd, I, T) when element(1, T) == scoped_id -> + case ic_fetch:member2type(G,X,I) of + ushort -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ulong(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "ushort:oe_ei_encode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + ulong -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ulong(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "ulong:oe_ei_encode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + short -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "short:oe_ei_encode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + long -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "long:oe_ei_encode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + float -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_double(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "float:oe_ei_encode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + double -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_double(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "double:oe_ei_encode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + boolean -> + emit(Fd, " switch(oe_rec->_u.~s) {\n",[ic_forms:get_id2(I)]), + emit(Fd, " case 0:\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"false\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean:oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " case 1:\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"true\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean:oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " default:\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean failure", []), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n"); + char -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_char(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "char:oe_ei_encode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + octet -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_char(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "octet:oe_ei_encode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + string -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_string(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_ei_encode_string", []), + emit(Fd, " return oe_error_code;\n }\n"); + struct -> + case ic_cbe:mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_pid(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_ei_encode_pid", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_port(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_ei_encode_port", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_ref" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ref(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_ei_encode_ref", []), + emit(Fd, " return oe_error_code;\n }\n"); + "ETERM*" -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_term(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_ei_encode_term", []), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T), ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_encode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n") + end; + sequence -> + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "sequence:oe_encode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + array -> + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "array:oe_encode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + union -> + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "union:oe_encode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + enum -> + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "enum:oe_encode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + any -> %% Fix for any type + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "enum:oe_ei_encodelong", []), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end; +getCaseTypeEncode(G, N, X, Fd, I, T) -> + case I of + {array,AID,_} -> + ArrayName = ic_util:to_undersc([ic_forms:get_id2(AID),ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ArrayName,ic_forms:get_id2(AID)]), + ?emit_c_enc_rpt(Fd, " ", "array:oe_encode_~s", [ArrayName]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + case T of + {short,_} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "short:oe_ei_encode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + {unsigned,{short,_}} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ulong(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "ushort:oe_ei_encode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + {long, _} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_long(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "long:oe_ei_encode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + {unsigned,{long,_}} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_ulong(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "ulong:oe_ei_encode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + {float,_} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_double(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "float:oe_ei_encode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + {double,_} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_double(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "double:oe_ei_encode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + {boolean,_} -> + emit(Fd, " switch(oe_rec->_u.~s) {\n",[ic_forms:get_id2(I)]), + emit(Fd, " case 0:\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"false\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean:oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " case 1:\n"), + emit(Fd, " if ((oe_error_code = oe_ei_encode_atom(oe_env, \"true\")) < 0) {\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean:oe_ei_encode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " break;\n"), + emit(Fd, " default:\n"), + ?emit_c_enc_rpt(Fd, " ", "boolean failure", []), + emit(Fd, " return -1;\n"), + emit(Fd, " }\n"); + {char,_} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_char(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "char:oe_ei_encode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + {octet,_} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_char(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "octet:oe_ei_encode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + {string,_} -> + emit(Fd, " if ((oe_error_code = oe_ei_encode_string(oe_env, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "oe_ei_encode_string", []), + emit(Fd, " return oe_error_code;\n }\n"); + {sequence,_,_} -> + SeqName = ic_util:to_undersc([ic_forms:get_id2(I), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [SeqName,ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "sequence:oe_encode_~s", [SeqName]), + emit(Fd, " return oe_error_code;\n }\n"); + {struct,SID,_,_} -> + StructName = ic_util:to_undersc([ic_forms:get_id2(SID), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [StructName,ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "struct:oe_encode_~s", [StructName]), + emit(Fd, " return oe_error_code;\n }\n"); + {union,UID,_,_,_} -> + UnionName = ic_util:to_undersc([ic_forms:get_id2(UID), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_encode_~s(oe_env, &oe_rec->_u.~s)) < 0) {\n", + [UnionName,ic_forms:get_id2(I)]), + ?emit_c_enc_rpt(Fd, " ", "union:oe_encode_~s", [UnionName]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end + end. + + + + +%% +%% Decode facilities +%% +emit_union_decode(G, N, X, Fd, UnionName) -> + emit(Fd, "int ~s~s(CORBA_Environment *oe_env, char *oe_first, int* oe_index, ~s* oe_rec) {\n\n", + [ic_util:mk_oe_name(G, "decode_"), UnionName, UnionName]), + + emit(Fd, " int oe_error_code = 0;\n"), + emit(Fd, " int oe_tmp = 0;\n"), + emit(Fd, " char oe_union_name[256];\n\n"), + + emit(Fd, " if((char*) oe_rec == oe_first)\n",[]), + AlignName = lists:concat(["*oe_index + sizeof(",UnionName,")"]), + emit(Fd, " *oe_index = ~s;\n\n", [ic_util:mk_align(AlignName)]), + + emit(Fd, " if ((oe_error_code = ei_decode_tuple_header(oe_env->_inbuf, &oe_env->_iin, &oe_tmp)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_tuple_header", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_union_name)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit_c_union_discr_decode(G, N, X, Fd), + emit(Fd, " /* Decode union */\n"), + emit(Fd, " switch(oe_rec->_d) {\n"), + emit_c_union_loop(G, N, X, Fd, X#union.body, decode), + emit(Fd, " }\n\n"), + + emit(Fd, " *oe_index = ~s;\n", [ic_util:mk_align("*oe_index")]), + emit(Fd, " return 0;\n"), + emit(Fd, "}\n\n\n"). + + +emit_c_union_discr_decode(G, N, X, Fd) -> + emit(Fd, " /* Decode descriminator */\n"), + UD = get_c_union_discriminator(G, N, X), + case UD of + "CORBA_short" -> + emit(Fd, " {\n"), + emit(Fd, " long oe_long;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_long)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "short:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_d = (short) oe_long;\n\n"), + emit(Fd, " if (oe_rec->_d != oe_long)\n return -1;\n"), + emit(Fd, " }\n\n"); + "CORBA_unsigned_short" -> + emit(Fd, " {\n"), + emit(Fd, " unsigned long oe_ulong;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, &oe_env->_iin, &oe_ulong)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "unshort:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_d = (unsigned short) oe_ulong;\n\n"), + emit(Fd, " if (oe_rec->_d != oe_ulong)\n return -1;\n"), + emit(Fd, " }\n\n"); + "CORBA_long" -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_d)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "long:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_unsigned_long" -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_d)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "ulong:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + "CORBA_boolean" -> + emit(Fd, " {\n"), + emit(Fd, " char oe_bool[25];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_bool)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "boolean:ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if (strcmp(oe_bool, \"false\") == 0) {\n"), + emit(Fd, " oe_rec->_d = 0;\n"), + emit(Fd, " }else if (strcmp(oe_bool, \"true\") == 0) {\n"), + emit(Fd, " oe_rec->_d = 1;\n"), + emit(Fd, " } else {\n"), + emit_c_dec_rpt(Fd, " ", "boolean failure", []), + emit(Fd, " return -1;\n }\n"), + emit(Fd, " }\n\n"); + "CORBA_char" -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_d)) < 0) {\n"), + emit_c_dec_rpt(Fd, " ", "char:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + T -> + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_d)) < 0) {\n", + [T]), + ?emit_c_dec_rpt(Fd, " ", "oe_decode_~s", [T]), + emit(Fd, " return oe_error_code;\n }\n") + end. + + + +getCaseTypeDecode(G, N, X, Fd, I, T) when element(1, T) == scoped_id -> + case ic_fetch:member2type(G,X,I) of + ushort -> + emit(Fd, " {\n"), + emit(Fd, " unsigned long oe_ulong;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, &oe_env->_iin, &oe_ulong)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ushort:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_u.~s = (unsigned short) oe_ulong;\n\n",[ic_forms:get_id2(I)]), + emit(Fd, " if (oe_rec->_u.~s != oe_ulong)\n return -1;\n",[ic_forms:get_id2(I)]), + emit(Fd, " }\n"); + ulong -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ulong:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + short -> + emit(Fd, " {\n"), + emit(Fd, " long oe_long;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_long)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "short:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_u.~s = (short) oe_long;\n\n",[ic_forms:get_id2(I)]), + emit(Fd, " if (oe_rec->_u.~s != oe_long)\n return -1;\n",[ic_forms:get_id2(I)]), + emit(Fd, " }\n"); + long -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "long:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + float -> + emit(Fd, " {\n"), + emit(Fd, " double oe_double;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, &oe_env->_iin, &oe_double)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "float:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_u.~s = (float) oe_double;\n",[ic_forms:get_id2(I)]), + emit(Fd, " }\n"); + double -> + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "double:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + boolean -> + emit(Fd, " {\n"), + emit(Fd, " char oe_bool[25];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_bool)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "boolean:ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if (strcmp(oe_bool, \"false\") == 0) {\n"), + emit(Fd, " oe_rec->_u.~s = 0;\n",[ic_forms:get_id2(I)]), + emit(Fd, " } else if (strcmp(oe_bool, \"true\") == 0) {\n"), + emit(Fd, " oe_rec->_u.~s = 1;\n",[ic_forms:get_id2(I)]), + emit(Fd, " } else {\n"), + ?emit_c_dec_rpt(Fd, " ", "boolean failure", []), + emit(Fd, " return -1;\n }\n"), + emit(Fd, " }\n"); + char -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "char:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + octet -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "octet:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + string -> + emit(Fd, " {\n"), + emit(Fd, " int oe_type = 0;\n"), + emit(Fd, " int oe_string_ctr = 0;\n\n"), + + emit(Fd, " (int) ei_get_type(oe_env->_inbuf, &oe_env->_iin, &oe_type, &oe_string_ctr);\n\n"), + + emit(Fd, " oe_rec->_u.~s = (void *) (oe_first + *oe_index);\n\n",[ic_forms:get_id2(I)]), + + emit(Fd, " if ((oe_error_code = ei_decode_string(oe_env->_inbuf, &oe_env->_iin, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_string", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " *oe_index = ~s;\n",[ic_util:mk_align("*oe_index+oe_string_ctr+1")]), + emit(Fd, " }\n"); + struct -> + case ic_cbe:mk_c_type(G, N, T, evaluate_not) of + "erlang_pid" -> + emit(Fd, " if ((oe_error_code = ei_decode_pid(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_pid", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_port" -> + emit(Fd, " if ((oe_error_code = ei_decode_port(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_port", []), + emit(Fd, " return oe_error_code;\n }\n"); + "erlang_ref" -> + emit(Fd, " if ((oe_error_code = ei_decode_ref(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_ref", []), + emit(Fd, " return oe_error_code;\n }\n"); + "ETERM*" -> + emit(Fd, " if ((oe_error_code = ei_decode_term(oe_env->_inbuf, &oe_env->_iin, (void **)&oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_term", []), + emit(Fd, " return oe_error_code;\n }\n"); + + _ -> + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "oe_decode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n") + end; + sequence -> + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "sequence:oe_decode_~s", + [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + array -> + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "array:oe_decode_~s", [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + union -> + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "union:oe_decode_~s", [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + enum -> + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [getCaseTypeStr(G, N, X, I, T),ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "enum:oe_decode_~s", [getCaseTypeStr(G, N, X, I, T)]), + emit(Fd, " return oe_error_code;\n }\n"); + any -> %% Fix for any type + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "any:ei_decodelong", []), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end; +getCaseTypeDecode(G, N, X, Fd, I, T) -> + case I of + {array,AID,_} -> + ArrayName = ic_util:to_undersc([ic_forms:get_id2(AID),ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, oe_rec->_u.~s)) < 0) {\n", + [ArrayName,ic_forms:get_id2(AID)]), + ?emit_c_dec_rpt(Fd, " ", "array:oe_decode_~s", [ArrayName]), + emit(Fd, " return oe_error_code;\n }\n"); + _ -> + case T of + {short,_} -> + emit(Fd, " {\n"), + emit(Fd, " long oe_long;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_long)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "short:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_u.~s = (short) oe_long;\n\n",[ic_forms:get_id2(I)]), + emit(Fd, " if (oe_rec->_u.~s != oe_long)\n return -1;\n",[ic_forms:get_id2(I)]), + emit(Fd, " }\n"); + {unsigned,{short,_}} -> + emit(Fd, " {\n"), + emit(Fd, " unsigned long oe_ulong;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, &oe_env->_iin, &oe_ulong)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "ushort:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_u.~s = (unsigned short) oe_ulong;\n\n",[ic_forms:get_id2(I)]), + emit(Fd, " if (oe_rec->_u.~s != oe_ulong)\n return -1;\n",[ic_forms:get_id2(I)]), + emit(Fd, " }\n"); + {long, _} -> + emit(Fd, " if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "long:ei_decode_long", []), + emit(Fd, " return oe_error_code;\n }\n"); + {unsigned,{long,_}} -> + emit(Fd, " if ((oe_error_code = ei_decode_ulong(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ulong:ei_decode_ulong", []), + emit(Fd, " return oe_error_code;\n }\n"); + {float,_} -> + emit(Fd, " {\n"), + emit(Fd, " double oe_double;\n"), + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, &oe_env->_iin, &oe_double)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "float:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " oe_rec->_u.~s = (float) oe_double;\n",[ic_forms:get_id2(I)]), + emit(Fd, " }\n"); + {double,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_double(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "dobule:ei_decode_double", []), + emit(Fd, " return oe_error_code;\n }\n"); + {boolean,_} -> + emit(Fd, " {\n"), + emit(Fd, " char oe_bool[25];\n\n"), + emit(Fd, " if ((oe_error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_bool)) < 0) {\n"), + ?emit_c_dec_rpt(Fd, " ", "boolean:ei_decode_atom", []), + emit(Fd, " return oe_error_code;\n }\n"), + emit(Fd, " if (strcmp(oe_bool, \"false\") == 0) {\n"), + emit(Fd, " oe_rec->_u.~s = 0;\n",[ic_forms:get_id2(I)]), + emit(Fd, " } else if (strcmp(oe_bool, \"true\") == 0) {\n"), + emit(Fd, " oe_rec->_u.~s = 1;\n",[ic_forms:get_id2(I)]), + emit(Fd, " } else {\n"), + ?emit_c_dec_rpt(Fd, " ", "boolean failure", []), + emit(Fd, " return -1;\n }\n"), + emit(Fd, " }\n"); + {char,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "char:ei_decode_char", []), + emit(Fd, " return oe_error_code;\n }\n"); + {octet,_} -> + emit(Fd, " if ((oe_error_code = ei_decode_char(oe_env->_inbuf, &oe_env->_iin, &oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + emit(Fd, " return oe_error_code;\n }\n"); + {string,_} -> + emit(Fd, " {\n"), + emit(Fd, " int oe_type = 0;\n"), + emit(Fd, " int oe_string_ctr = 0;\n\n"), + + emit(Fd, " (int) ei_get_type(oe_env->_inbuf, &oe_env->_iin, &oe_type, &oe_string_ctr);\n\n"), + + emit(Fd, " oe_rec->_u.~s = (void *) (oe_first + *oe_index);\n\n",[ic_forms:get_id2(I)]), + + emit(Fd, " if ((oe_error_code = ei_decode_string(oe_env->_inbuf, &oe_env->_iin, oe_rec->_u.~s)) < 0) {\n", + [ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "ei_decode_string", []), + emit(Fd, " return oe_error_code;\n }\n"), + + emit(Fd, " *oe_index = ~s;\n",[ic_util:mk_align("*oe_index+oe_string_ctr+1")]), + emit(Fd, " }\n"); + {sequence,_,_} -> + SeqName = ic_util:to_undersc([ic_forms:get_id2(I), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [SeqName,ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "sequence:oe_decode_~s", [SeqName]), + emit(Fd, " return oe_error_code;\n }\n"); + {struct,SID,_,_} -> + StructName = ic_util:to_undersc([ic_forms:get_id2(SID), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [StructName,ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "struct:oe_decode_~s", [StructName]), + emit(Fd, " return oe_error_code;\n }\n"); + {union,UID,_,_,_} -> + UnionName = ic_util:to_undersc([ic_forms:get_id2(UID), ic_forms:get_id2(X) | N]), + emit(Fd, " if ((oe_error_code = oe_decode_~s(oe_env, oe_first, oe_index, &oe_rec->_u.~s)) < 0) {\n", + [UnionName,ic_forms:get_id2(I)]), + ?emit_c_dec_rpt(Fd, " ", "union:oe_decode_~s", [UnionName]), + emit(Fd, " return oe_error_code;\n }"); + _ -> + ic_error:fatal_error(G, {illegal_typecode_for_c, T, N}) + end + end. + +mvDefaultToTail(CDclL) -> + mvDefaultToTail(CDclL,[],[]). + + +mvDefaultToTail([], F, FD) -> + lists:reverse(F) ++ FD; +mvDefaultToTail([{case_dcl,CaseList,I,T}|Rest], Found, FoundDefault) -> + case lists:keysearch(default, 1, CaseList) of + {value,Default} -> + NewCaseList = lists:delete(Default, CaseList) ++ [Default], + mvDefaultToTail(Rest, Found, [{case_dcl,NewCaseList,I,T}|FoundDefault]); + false -> + mvDefaultToTail(Rest, [{case_dcl,CaseList,I,T}|Found], FoundDefault) + end. + + diff --git a/lib/ic/src/icyeccpre.hrl b/lib/ic/src/icyeccpre.hrl new file mode 100644 index 0000000000..71b02b784b --- /dev/null +++ b/lib/ic/src/icyeccpre.hrl @@ -0,0 +1,124 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + + +-export([parse/1, parse_and_scan/1, format_error/1]). + +-import(lists, [reverse/1]). + +-ifdef(JAM). +-compile([{parse_transform,jam_yecc_pj},pj]). +-endif. + + +-include("icforms.hrl"). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% The parser generator will insert appropriate declarations before this line.% + +parse(Tokens) -> + case catch yeccpars1(Tokens, false, 0, [], []) of + error -> + Errorline = + if Tokens == [] -> 0; true -> element(2, hd(Tokens)) end, + {error, + {Errorline, ?MODULE, "syntax error at or after this line."}}; + Other -> + Other + end. + +parse_and_scan({Mod, Fun, Args}) -> + case apply(Mod, Fun, Args) of + {eof, _} -> + {ok, eof}; + {error, Descriptor, _} -> + {error, Descriptor}; + {ok, Tokens, _} -> + yeccpars1(Tokens, {Mod, Fun, Args}, 0, [], []) + end. + +format_error(Message) -> + case io_lib:deep_char_list(Message) of + true -> + Message; + _ -> + io_lib:write(Message) + end. + +% To be used in grammar files to throw an error message to the parser toplevel. +% Doesn't have to be exported! +return_error(Line, Message) -> + throw({error, {Line, ?MODULE, Message}}). + + +% Don't change yeccpars1/6 too much, it is called recursively by yeccpars2/8! +yeccpars1([Token | Tokens], Tokenizer, State, States, Vstack) -> + yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens, + Tokenizer); +yeccpars1([], {M, F, A}, State, States, Vstack) -> + case catch apply(M, F, A) of + {eof, Endline} -> + {error, {Endline, ?MODULE, "end_of_file"}}; + {error, Descriptor, _Endline} -> + {error, Descriptor}; + {'EXIT', Reason} -> + {error, {0, ?MODULE, Reason}}; + {ok, Tokens, _Endline} -> + case catch yeccpars1(Tokens, {M, F, A}, State, States, Vstack) of + error -> + Errorline = element(2, hd(Tokens)), + {error, {Errorline, ?MODULE, + "syntax error at or after this line."}}; + Other -> + Other + end + end; +yeccpars1([], false, State, States, Vstack) -> + yeccpars2(State, '$end', States, Vstack, {'$end', 999999}, [], false). + +% For internal use only. +yeccerror(Token) -> + {error, + {element(2, Token), ?MODULE, + ["syntax error before: ", yecctoken2string(Token)]}}. + +yecctoken2string({atom, _, A}) -> io_lib:write(A); +yecctoken2string({integer,_,N}) -> io_lib:write(N); +yecctoken2string({float,_,F}) -> io_lib:write(F); +yecctoken2string({char,_,C}) -> io_lib:write_char(C); +yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]); +yecctoken2string({string,_,S}) -> io_lib:write_string(S); +yecctoken2string({reserved_symbol, _, A}) -> io_lib:format("~w", [A]); +yecctoken2string({'dot', _}) -> "'.'"; +yecctoken2string({'$end', _}) -> + []; +yecctoken2string({Other, _}) when is_atom(Other) -> + io_lib:format("~w", [Other]); +yecctoken2string({_, _, Other}) when is_list(Other) andalso is_number(hd(Other)) -> + Other; +yecctoken2string({_, _, Other}) -> + io_lib:format("~p", [Other]); +yecctoken2string(Other) -> + io_lib:write(Other). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + |