aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile21
-rw-r--r--lib/asn1/Makefile2
-rw-r--r--lib/asn1/c_src/Makefile7
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c1
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml50
-rw-r--r--lib/asn1/doc/src/asn1ct.xml6
-rw-r--r--lib/asn1/doc/src/asn1rt.xml30
-rw-r--r--lib/asn1/src/asn1.app.src3
-rw-r--r--lib/asn1/src/asn1.appup.src30
-rw-r--r--lib/asn1/src/asn1ct.erl18
-rw-r--r--lib/asn1/src/asn1ct_check.erl286
-rw-r--r--lib/asn1/src/asn1ct_gen.erl25
-rw-r--r--lib/asn1/src/asn1ct_table.erl34
-rw-r--r--lib/asn1/src/asn1ct_tok.erl2
-rw-r--r--lib/asn1/src/asn1ct_value.erl3
-rw-r--r--lib/asn1/src/asn1rt.erl3
-rw-r--r--lib/asn1/test/asn1_SUITE.erl10
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EnumExt.asn12
-rw-r--r--lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn14
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn114
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn17
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn14
-rw-r--r--lib/asn1/test/asn1_appup_test.erl359
-rw-r--r--lib/asn1/test/error_SUITE.erl108
-rw-r--r--lib/asn1/test/testEnumExt.erl4
-rw-r--r--lib/asn1/test/testPrimStrings.erl2
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml4
-rw-r--r--lib/common_test/src/common_test.app.src7
-rw-r--r--lib/common_test/src/common_test.appup.src22
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl9
-rw-r--r--lib/common_test/src/ct_framework.erl61
-rw-r--r--lib/common_test/src/ct_gen_conn.erl7
-rw-r--r--lib/common_test/src/ct_hooks.erl38
-rw-r--r--lib/common_test/src/ct_logs.erl2
-rw-r--r--lib/common_test/src/ct_netconfc.erl17
-rw-r--r--lib/common_test/src/ct_run.erl65
-rw-r--r--lib/common_test/src/ct_telnet.erl195
-rw-r--r--lib/common_test/src/ct_telnet_client.erl137
-rw-r--r--lib/common_test/src/ct_testspec.erl7
-rw-r--r--lib/common_test/src/ct_util.erl11
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/cth_conn_log.erl1
-rw-r--r--lib/common_test/src/cth_surefire.erl4
-rw-r--r--lib/common_test/src/unix_telnet.erl6
-rw-r--r--lib/common_test/test/ct_config_info_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl113
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl138
-rw-r--r--lib/common_test/test/ct_group_info_SUITE.erl30
-rw-r--r--lib/common_test/test/ct_groups_spec_SUITE.erl75
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl14
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_repeat_1_SUITE.erl35
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_sequence_1_SUITE.erl19
-rw-r--r--lib/common_test/test/ct_skip_SUITE.erl118
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_telnet_SUITE.erl63
-rw-r--r--lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl65
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE.erl8
-rw-r--r--lib/common_test/test/ct_test_support.erl52
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl92
-rw-r--r--lib/common_test/test/telnet_server.erl100
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/compile.xml15
-rw-r--r--lib/compiler/src/beam_a.erl2
-rw-r--r--lib/compiler/src/beam_asm.erl2
-rw-r--r--lib/compiler/src/beam_block.erl12
-rw-r--r--lib/compiler/src/beam_bool.erl29
-rw-r--r--lib/compiler/src/beam_bsm.erl1
-rw-r--r--lib/compiler/src/beam_clean.erl4
-rw-r--r--lib/compiler/src/beam_dict.erl20
-rw-r--r--lib/compiler/src/beam_disasm.erl59
-rw-r--r--lib/compiler/src/beam_flatten.erl4
-rw-r--r--lib/compiler/src/beam_jump.erl4
-rw-r--r--lib/compiler/src/beam_split.erl4
-rw-r--r--lib/compiler/src/beam_utils.erl3
-rw-r--r--lib/compiler/src/beam_validator.erl92
-rw-r--r--lib/compiler/src/beam_z.erl12
-rw-r--r--lib/compiler/src/cerl.erl99
-rw-r--r--lib/compiler/src/cerl_clauses.erl23
-rw-r--r--lib/compiler/src/cerl_inline.erl43
-rw-r--r--lib/compiler/src/cerl_trees.erl20
-rw-r--r--lib/compiler/src/compile.erl75
-rw-r--r--lib/compiler/src/compiler.app.src4
-rw-r--r--lib/compiler/src/compiler.appup.src22
-rw-r--r--lib/compiler/src/core_lib.erl4
-rw-r--r--lib/compiler/src/core_lint.erl27
-rw-r--r--lib/compiler/src/core_parse.hrl20
-rw-r--r--lib/compiler/src/core_parse.yrl8
-rw-r--r--lib/compiler/src/core_pp.erl14
-rw-r--r--lib/compiler/src/erl_bifs.erl1
-rwxr-xr-xlib/compiler/src/genop.tab6
-rw-r--r--lib/compiler/src/rec_env.erl5
-rw-r--r--lib/compiler/src/sys_core_fold.erl229
-rw-r--r--lib/compiler/src/sys_pre_expand.erl7
-rw-r--r--lib/compiler/src/v3_codegen.erl175
-rw-r--r--lib/compiler/src/v3_core.erl764
-rw-r--r--lib/compiler/src/v3_kernel.erl164
-rw-r--r--lib/compiler/src/v3_kernel.hrl4
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl29
-rw-r--r--lib/compiler/src/v3_life.erl17
-rw-r--r--lib/compiler/test/Makefile3
-rw-r--r--lib/compiler/test/andor_SUITE.erl17
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl15
-rw-r--r--lib/compiler/test/compile_SUITE.erl104
-rw-r--r--lib/compiler/test/compile_SUITE_data/small.erl48
-rw-r--r--lib/compiler/test/compile_SUITE_data/small_maps.erl16
-rw-r--r--lib/compiler/test/core_SUITE.erl6
-rw-r--r--lib/compiler/test/core_SUITE_data/bad_boolean_guard.core32
-rw-r--r--lib/compiler/test/core_SUITE_data/eval_case.core34
-rw-r--r--lib/compiler/test/core_SUITE_data/map_core_test.core4
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl55
-rw-r--r--lib/compiler/test/error_SUITE.erl43
-rw-r--r--lib/compiler/test/fun_SUITE.erl8
-rw-r--r--lib/compiler/test/guard_SUITE.erl12
-rw-r--r--lib/compiler/test/lc_SUITE.erl8
-rw-r--r--lib/compiler/test/map_SUITE.erl600
-rw-r--r--lib/compiler/test/receive_SUITE.erl1
-rw-r--r--lib/compiler/test/record_SUITE.erl8
-rw-r--r--lib/compiler/test/warnings_SUITE.erl125
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/src/cosEvent.app.src3
-rw-r--r--lib/cosEvent/vsn.mk2
-rw-r--r--lib/cosEventDomain/src/cosEventDomain.app.src4
-rw-r--r--lib/cosEventDomain/vsn.mk2
-rw-r--r--lib/cosFileTransfer/src/cosFileTransfer.app.src4
-rw-r--r--lib/cosFileTransfer/vsn.mk2
-rw-r--r--lib/cosNotification/src/cosNotification.app.src4
-rw-r--r--lib/cosNotification/vsn.mk2
-rw-r--r--lib/cosProperty/src/cosProperty.app.src4
-rw-r--r--lib/cosProperty/vsn.mk2
-rw-r--r--lib/cosTime/src/cosTime.app.src4
-rw-r--r--lib/cosTime/vsn.mk3
-rw-r--r--lib/cosTransactions/src/cosTransactions.app.src3
-rw-r--r--lib/cosTransactions/vsn.mk2
-rw-r--r--lib/crypto/Makefile4
-rw-r--r--lib/crypto/c_src/Makefile.in7
-rw-r--r--lib/crypto/c_src/crypto.c305
-rw-r--r--lib/crypto/c_src/crypto_callback.c22
-rw-r--r--lib/crypto/doc/src/crypto.xml23
-rw-r--r--lib/crypto/doc/src/crypto_app.xml4
-rw-r--r--lib/crypto/src/crypto.app.src3
-rw-r--r--lib/crypto/src/crypto.appup.src14
-rw-r--r--lib/crypto/src/crypto.erl7
-rw-r--r--lib/crypto/test/crypto_SUITE.erl22
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/src/dbg_ieval.erl88
-rw-r--r--lib/debugger/src/dbg_iload.erl83
-rw-r--r--lib/debugger/src/dbg_wx_filedialog_win.erl19
-rw-r--r--lib/debugger/src/dbg_wx_mon.erl4
-rw-r--r--lib/debugger/src/dbg_wx_mon_win.erl4
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl4
-rw-r--r--lib/debugger/src/dbg_wx_view.erl4
-rw-r--r--lib/debugger/src/debugger.app.src4
-rw-r--r--lib/debugger/src/debugger.appup.src10
-rw-r--r--lib/debugger/src/debugger.erl6
-rw-r--r--lib/debugger/src/int.erl5
-rw-r--r--lib/debugger/test/Makefile1
-rw-r--r--lib/debugger/test/bs_construct_SUITE.erl40
-rw-r--r--lib/debugger/test/debugger_SUITE.erl7
-rw-r--r--lib/debugger/test/erl_eval_SUITE.erl1
-rw-r--r--lib/debugger/test/int_eval_SUITE.erl9
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl3
-rw-r--r--lib/debugger/test/map_SUITE.erl1003
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml2
-rw-r--r--lib/dialyzer/src/dialyzer.app.src13
-rw-r--r--lib/dialyzer/src/dialyzer.appup.src11
-rw-r--r--lib/dialyzer/src/dialyzer.erl16
-rw-r--r--lib/dialyzer/src/dialyzer.hrl15
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl27
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl8
-rw-r--r--lib/dialyzer/src/dialyzer_callgraph.erl35
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl50
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl41
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl106
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl117
-rw-r--r--lib/dialyzer/src/dialyzer_dep.erl43
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl36
-rw-r--r--lib/dialyzer/src/dialyzer_races.erl27
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl27
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl23
-rw-r--r--lib/dialyzer/test/Makefile1
-rw-r--r--lib/dialyzer/test/dialyzer_SUITE.erl77
-rw-r--r--lib/dialyzer/test/file_utils.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/array4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/dict26
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/ets6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/ewgi6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/inf_loop12
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/inf_loop22
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/para21
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/queue20
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/simple6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/wings14
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl93
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl36
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl123
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl64
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl77
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl27
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl2
-rw-r--r--lib/dialyzer/test/options1_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/race_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow34
-rw-r--r--lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow44
-rw-r--r--lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow58
-rw-r--r--lib/dialyzer/test/race_SUITE_data/results/ets_insert_param8
-rw-r--r--lib/dialyzer/test/small_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes39
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes23
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/funs_from_outside7
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/invalid_spec_22
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/predef8
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/record_construct2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl47
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl40
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl83
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl8
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl12
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/on_load.erl7
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/predef.erl67
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/predef2.erl56
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/record_construct.erl2
-rw-r--r--lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings5
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl95
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl9
-rw-r--r--lib/dialyzer/test/user_SUITE_data/dialyzer_options2
-rw-r--r--lib/diameter/doc/src/diameter_make.xml2
-rw-r--r--lib/diameter/doc/src/notes.xml8
-rw-r--r--lib/diameter/include/diameter_gen.hrl123
-rw-r--r--lib/diameter/src/Makefile19
-rw-r--r--lib/diameter/src/base/diameter_service.erl2
-rw-r--r--lib/diameter/src/diameter.app.src19
-rw-r--r--lib/diameter/src/info/diameter_dbg.erl (renamed from lib/diameter/src/base/diameter_dbg.erl)15
-rw-r--r--lib/diameter/src/info/diameter_info.erl (renamed from lib/diameter/src/base/diameter_info.erl)14
-rw-r--r--lib/diameter/src/modules.mk12
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl23
-rw-r--r--lib/diameter/test/diameter_codec_test.erl3
-rw-r--r--lib/edoc/src/edoc.app.src4
-rw-r--r--lib/edoc/src/edoc.appup.src22
-rw-r--r--lib/edoc/src/edoc_specs.erl6
-rw-r--r--lib/edoc/src/edoc_tags.erl2
-rw-r--r--lib/edoc/test/edoc_SUITE.erl11
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/eldap.xml3
-rw-r--r--lib/eldap/src/Makefile2
-rw-r--r--lib/eldap/src/eldap.app.src4
-rw-r--r--lib/eldap/src/eldap.appup.src21
-rw-r--r--lib/eldap/src/eldap.erl25
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl7
-rw-r--r--lib/eldap/test/eldap_misc_SUITE.erl51
-rw-r--r--lib/eldap/vsn.mk3
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl17
-rw-r--r--lib/erl_docgen/src/docgen_otp_specs.erl2
-rw-r--r--lib/erl_docgen/src/erl_docgen.app.src4
-rw-r--r--lib/erl_docgen/src/erl_docgen.appup.src22
-rw-r--r--lib/erl_docgen/test/Makefile65
-rw-r--r--lib/erl_docgen/test/erl_docgen.spec1
-rw-r--r--lib/erl_docgen/test/erl_docgen_SUITE.erl50
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/aclocal.m450
-rw-r--r--lib/erl_interface/doc/src/ei.xml34
-rw-r--r--lib/erl_interface/include/ei.h6
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c9
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h3
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c2
-rw-r--r--lib/erl_interface/src/decode/decode_skip.c10
-rw-r--r--lib/erl_interface/src/decode/decode_tuple_header.c23
-rw-r--r--lib/erl_interface/src/encode/encode_tuple_header.c19
-rw-r--r--lib/erl_interface/src/misc/ei_decode_term.c3
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c12
-rw-r--r--lib/erl_interface/test/all_SUITE_data/ei_runner.c6
-rw-r--r--lib/erl_interface/test/all_SUITE_data/ei_runner.h3
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c6
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE.erl11
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c490
-rw-r--r--lib/erl_interface/vsn.mk3
-rw-r--r--lib/et/src/et.app.src4
-rw-r--r--lib/et/src/et.appup.src10
-rw-r--r--lib/et/test/Makefile1
-rw-r--r--lib/et/test/et_SUITE.erl50
-rw-r--r--lib/et/vsn.mk2
-rw-r--r--lib/eunit/src/eunit.app.src4
-rw-r--r--lib/eunit/src/eunit.appup.src22
-rw-r--r--lib/eunit/src/eunit_serial.erl6
-rw-r--r--lib/eunit/test/eunit_SUITE.erl10
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/doc/src/gs.xml4
-rw-r--r--lib/gs/src/Makefile2
-rw-r--r--lib/gs/src/gs.app.src3
-rw-r--r--lib/gs/src/gs.appup.src22
-rw-r--r--lib/gs/src/gstk_generic.erl10
-rw-r--r--lib/gs/test/Makefile65
-rw-r--r--lib/gs/test/gs.spec1
-rw-r--r--lib/gs/test/gs_SUITE.erl50
-rw-r--r--lib/gs/vsn.mk2
-rw-r--r--lib/hipe/Makefile2
-rw-r--r--lib/hipe/arm/hipe_arm_assemble.erl48
-rw-r--r--lib/hipe/cerl/Makefile11
-rw-r--r--lib/hipe/cerl/cerl_closurean.erl8
-rw-r--r--lib/hipe/cerl/cerl_messagean.erl4
-rw-r--r--lib/hipe/cerl/cerl_to_icode.erl38
-rw-r--r--lib/hipe/cerl/cerl_typean.erl4
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl16
-rw-r--r--lib/hipe/cerl/erl_types.erl430
-rw-r--r--lib/hipe/flow/cfg.hrl6
-rw-r--r--lib/hipe/flow/cfg.inc4
-rw-r--r--lib/hipe/flow/hipe_dominators.erl6
-rw-r--r--lib/hipe/flow/liveness.inc6
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl4
-rw-r--r--lib/hipe/icode/hipe_icode.erl13
-rw-r--r--lib/hipe/icode/hipe_icode.hrl8
-rw-r--r--lib/hipe/icode/hipe_icode_callgraph.erl4
-rw-r--r--lib/hipe/icode/hipe_icode_cfg.erl6
-rw-r--r--lib/hipe/icode/hipe_icode_coordinator.erl8
-rw-r--r--lib/hipe/icode/hipe_icode_exceptions.erl8
-rw-r--r--lib/hipe/icode/hipe_icode_fp.erl8
-rw-r--r--lib/hipe/icode/hipe_icode_instruction_counter.erl5
-rw-r--r--lib/hipe/icode/hipe_icode_mulret.erl16
-rw-r--r--lib/hipe/icode/hipe_icode_range.erl10
-rw-r--r--lib/hipe/icode/hipe_icode_ssa.erl4
-rw-r--r--lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl16
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl53
-rw-r--r--lib/hipe/llvm/Makefile109
-rw-r--r--lib/hipe/llvm/elf32_format.hrl59
-rw-r--r--lib/hipe/llvm/elf64_format.hrl58
-rw-r--r--lib/hipe/llvm/elf_format.erl790
-rw-r--r--lib/hipe/llvm/elf_format.hrl488
-rw-r--r--lib/hipe/llvm/hipe_llvm.erl1131
-rw-r--r--lib/hipe/llvm/hipe_llvm_arch.hrl11
-rw-r--r--lib/hipe/llvm/hipe_llvm_liveness.erl112
-rw-r--r--lib/hipe/llvm/hipe_llvm_main.erl514
-rw-r--r--lib/hipe/llvm/hipe_llvm_merge.erl114
-rw-r--r--lib/hipe/llvm/hipe_rtl_to_llvm.erl1612
-rw-r--r--lib/hipe/main/hipe.app.src11
-rw-r--r--lib/hipe/main/hipe.appup.src7
-rw-r--r--lib/hipe/main/hipe.erl53
-rw-r--r--lib/hipe/main/hipe_main.erl42
-rw-r--r--lib/hipe/misc/hipe_consttab.erl8
-rw-r--r--lib/hipe/misc/hipe_consttab.hrl4
-rw-r--r--lib/hipe/misc/hipe_gensym.erl2
-rw-r--r--lib/hipe/misc/hipe_pack_constants.erl102
-rw-r--r--lib/hipe/misc/hipe_sdi.erl15
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl48
-rw-r--r--lib/hipe/regalloc/hipe_ig_moves.erl4
-rw-r--r--lib/hipe/regalloc/hipe_ls_regalloc.erl4
-rw-r--r--lib/hipe/regalloc/hipe_optimistic_regalloc.erl4
-rw-r--r--lib/hipe/rtl/hipe_icode2rtl.erl4
-rw-r--r--lib/hipe/rtl/hipe_rtl.erl174
-rw-r--r--lib/hipe/rtl/hipe_rtl.hrl3
-rw-r--r--lib/hipe/rtl/hipe_rtl_liveness.erl3
-rw-r--r--lib/hipe/rtl/hipe_tagscheme.erl37
-rw-r--r--lib/hipe/sparc/hipe_sparc_assemble.erl48
-rw-r--r--lib/hipe/ssa/hipe_ssa_const_prop.inc10
-rw-r--r--lib/hipe/test/Makefile73
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_add.erl18
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_bincomp.erl79
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_bits.erl150
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_bitsize.erl23
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_bugs_R08.erl32
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_bugs_R09.erl35
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_bugs_R12.erl133
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_build.erl41
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_catch_bug.erl25
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_checksum.erl35
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_construct.erl128
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_decode.erl980
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_decode_extract.hrl91
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_des.erl734
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_extract.erl94
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_flatb.erl29
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_id3.erl75
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_match.erl175
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_native_float.erl22
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_orber.erl26
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_pmatch.erl269
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl67
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_pmatch_in_guards.erl23
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_potpurri.erl200
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_remove3.erl104
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_save.erl21
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_shell_native.erl275
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_split.erl105
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_system_limit_32.erl26
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_utf.erl18
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_var_segs.erl76
-rw-r--r--lib/hipe/test/hipe.spec6
-rw-r--r--lib/hipe/test/hipe_SUITE.erl55
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl182
-rw-r--r--lib/hipe/util/hipe_digraph.erl8
-rw-r--r--lib/hipe/util/hipe_dot.erl6
-rw-r--r--lib/hipe/util/hipe_vectors.hrl4
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_x86_assemble.erl51
-rw-r--r--lib/ic/src/ic.app.src3
-rw-r--r--lib/ic/src/ic.erl2
-rw-r--r--lib/ic/src/ic_codegen.erl3
-rw-r--r--lib/ic/src/ic_pp.erl8
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE.erl2
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/src/ftp/ftp.erl142
-rw-r--r--lib/inets/src/http_client/httpc_cookie.erl3
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl72
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl6
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl46
-rw-r--r--lib/inets/src/http_server/httpd_example.erl4
-rw-r--r--lib/inets/src/inets_app/inets.app.src4
-rw-r--r--lib/inets/src/inets_app/inets.appup.src16
-rw-r--r--lib/inets/test/Makefile2
-rw-r--r--lib/inets/test/ftp_suite_lib.erl20
-rw-r--r--lib/inets/test/httpc_SUITE.erl42
-rw-r--r--lib/inets/test/httpd_1_0.erl9
-rw-r--r--lib/inets/test/httpd_1_1.erl111
-rw-r--r--lib/inets/test/httpd_SUITE.erl1482
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl5
-rw-r--r--lib/inets/test/httpd_test_lib.erl119
-rw-r--r--lib/inets/test/inets_appup_test.erl298
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl15
-rw-r--r--lib/inets/test/old_httpd_SUITE.erl140
-rw-r--r--lib/inets/vsn.mk3
-rw-r--r--lib/jinterface/doc/src/jinterface_users_guide.xml4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java289
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java3
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java20
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/java_files1
-rw-r--r--lib/jinterface/test/jinterface_SUITE.erl18
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Makefile.src3
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Maps.java108
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/Makefile4
-rw-r--r--lib/kernel/doc/src/app.xml36
-rw-r--r--lib/kernel/doc/src/application.xml22
-rw-r--r--lib/kernel/doc/src/file.xml18
-rw-r--r--lib/kernel/doc/src/ref_man.xml5
-rw-r--r--lib/kernel/doc/src/ref_man.xml.src67
-rw-r--r--lib/kernel/include/dist.hrl3
-rw-r--r--lib/kernel/src/application.erl29
-rw-r--r--lib/kernel/src/application_controller.erl48
-rw-r--r--lib/kernel/src/application_master.erl38
-rw-r--r--lib/kernel/src/disk_log_server.erl12
-rw-r--r--lib/kernel/src/dist_util.erl5
-rw-r--r--lib/kernel/src/file.erl3
-rw-r--r--lib/kernel/src/global.erl22
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl35
-rw-r--r--lib/kernel/src/inet.erl5
-rw-r--r--lib/kernel/src/inet_config.erl3
-rw-r--r--lib/kernel/src/kernel.app.src3
-rw-r--r--lib/kernel/src/kernel.appup.src12
-rw-r--r--lib/kernel/src/os.erl13
-rw-r--r--lib/kernel/src/rpc.erl4
-rw-r--r--lib/kernel/test/application_SUITE.erl83
-rw-r--r--lib/kernel/test/application_SUITE_data/deadlock/deadlock.app2
-rw-r--r--lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl12
-rw-r--r--lib/kernel/test/file_SUITE.erl161
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl33
-rw-r--r--lib/kernel/test/heart_SUITE.erl8
-rw-r--r--lib/kernel/test/kernel_SUITE.erl72
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl175
-rw-r--r--lib/megaco/aclocal.m450
-rw-r--r--lib/megaco/src/app/megaco.app.src5
-rw-r--r--lib/megaco/src/binary/megaco_binary_encoder_lib.erl8
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/mnesia.xml26
-rw-r--r--lib/mnesia/src/mnesia.app.src3
-rw-r--r--lib/mnesia/src/mnesia.erl10
-rw-r--r--lib/mnesia/src/mnesia.hrl4
-rw-r--r--lib/mnesia/src/mnesia_controller.erl18
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl29
-rw-r--r--lib/mnesia/src/mnesia_event.erl4
-rw-r--r--lib/mnesia/src/mnesia_index.erl14
-rw-r--r--lib/mnesia/src/mnesia_lib.erl72
-rw-r--r--lib/mnesia/src/mnesia_locker.erl8
-rw-r--r--lib/mnesia/src/mnesia_log.erl14
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl15
-rw-r--r--lib/mnesia/src/mnesia_recover.erl8
-rw-r--r--lib/mnesia/src/mnesia_snmp_hook.erl8
-rw-r--r--lib/mnesia/src/mnesia_tm.erl5
-rw-r--r--lib/mnesia/test/mnesia_SUITE.erl15
-rw-r--r--lib/mnesia/test/mnesia_config_test.erl7
-rw-r--r--lib/mnesia/test/mnesia_durability_test.erl30
-rw-r--r--lib/mnesia/test/mnesia_evil_coverage_test.erl11
-rw-r--r--lib/mnesia/test/mnesia_frag_test.erl4
-rw-r--r--lib/mnesia/test/mnesia_nice_coverage_test.erl3
-rw-r--r--lib/mnesia/test/mnesia_schema_recovery_test.erl143
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/src/cdv_bin_cb.erl24
-rw-r--r--lib/observer/src/cdv_dist_cb.erl12
-rw-r--r--lib/observer/src/cdv_html_wx.erl26
-rw-r--r--lib/observer/src/cdv_term_cb.erl21
-rw-r--r--lib/observer/src/crashdump_viewer.erl14
-rw-r--r--lib/observer/src/observer.app.src5
-rw-r--r--lib/observer/src/observer.appup.src10
-rw-r--r--lib/observer/src/observer.erl7
-rw-r--r--lib/observer/src/observer_html_lib.erl77
-rw-r--r--lib/observer/src/observer_lib.erl14
-rw-r--r--lib/observer/src/observer_procinfo.erl13
-rw-r--r--lib/observer/src/observer_tv_wx.erl6
-rw-r--r--lib/observer/src/observer_wx.erl17
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl82
-rw-r--r--lib/observer/test/observer_SUITE.erl236
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/aclocal.m450
-rw-r--r--lib/odbc/src/odbc.app.src3
-rw-r--r--lib/odbc/src/odbc.appup.src20
-rw-r--r--lib/odbc/test/odbc_start_SUITE.erl12
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/src/Makefile3
-rw-r--r--lib/orber/src/orber.app.src4
-rw-r--r--lib/orber/src/orber_interceptors.erl11
-rw-r--r--lib/orber/vsn.mk2
-rw-r--r--lib/os_mon/c_src/memsup.c8
-rw-r--r--lib/os_mon/src/os_mon.app.src5
-rw-r--r--lib/os_mon/src/os_mon.appup.src27
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl6
-rw-r--r--lib/os_mon/test/disksup_SUITE.erl17
-rw-r--r--lib/os_mon/test/os_mon_SUITE.erl9
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/ose/Makefile36
-rw-r--r--lib/ose/doc/html/.gitignore0
-rw-r--r--lib/ose/doc/man3/.gitignore0
-rw-r--r--lib/ose/doc/man6/.gitignore0
-rw-r--r--lib/ose/doc/pdf/.gitignore0
-rw-r--r--lib/ose/doc/src/.gitignore1
-rw-r--r--lib/ose/doc/src/Makefile132
-rw-r--r--lib/ose/doc/src/book.xml48
-rw-r--r--lib/ose/doc/src/notes.xml33
-rw-r--r--lib/ose/doc/src/ose_app.xml37
-rw-r--r--lib/ose/doc/src/ose_erl_driver.xml110
-rw-r--r--lib/ose/doc/src/ose_intro.xml153
-rw-r--r--lib/ose/doc/src/ose_signals_chapter.xml239
-rw-r--r--lib/ose/doc/src/part.xml38
-rw-r--r--lib/ose/doc/src/ref_man.xml39
-rw-r--r--lib/ose/ebin/.gitignore0
-rw-r--r--lib/ose/include/.gitignore0
-rw-r--r--lib/ose/info2
-rw-r--r--lib/ose/src/Makefile106
-rw-r--r--lib/ose/src/ose.app.src27
-rw-r--r--lib/ose/src/ose.appup.src22
-rw-r--r--lib/ose/src/ose.erl452
-rw-r--r--lib/ose/test/Makefile67
-rw-r--r--lib/ose/test/ose.cover2
-rw-r--r--lib/ose/test/ose.spec1
-rw-r--r--lib/ose/test/ose_SUITE.erl765
-rw-r--r--lib/ose/vsn.mk1
-rw-r--r--lib/otp_mibs/src/otp_mibs.app.src4
-rw-r--r--lib/otp_mibs/src/otp_mibs.appup.src11
-rw-r--r--lib/otp_mibs/test/otp_mibs_SUITE.erl15
-rw-r--r--lib/otp_mibs/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/yecc.xml8
-rw-r--r--lib/parsetools/src/parsetools.app.src3
-rw-r--r--lib/parsetools/src/parsetools.appup.src22
-rw-r--r--lib/parsetools/src/yecc.erl4
-rw-r--r--lib/parsetools/test/Makefile1
-rw-r--r--lib/parsetools/test/app_SUITE.erl50
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/percept/src/percept.app.src4
-rw-r--r--lib/percept/src/percept.appup.src12
-rw-r--r--lib/percept/test/percept_SUITE.erl12
-rw-r--r--lib/percept/vsn.mk2
-rw-r--r--lib/public_key/doc/src/public_key.xml2
-rw-r--r--lib/public_key/include/public_key.hrl22
-rw-r--r--lib/public_key/src/pubkey_cert.erl17
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl4
-rw-r--r--lib/public_key/src/pubkey_crl.erl36
-rw-r--r--lib/public_key/src/pubkey_pem.erl8
-rw-r--r--lib/public_key/src/pubkey_ssh.erl7
-rw-r--r--lib/public_key/src/public_key.app.src4
-rw-r--r--lib/public_key/src/public_key.appup.src25
-rw-r--r--lib/public_key/src/public_key.erl23
-rw-r--r--lib/public_key/test/public_key_SUITE.erl9
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/src/reltool.app.src4
-rw-r--r--lib/reltool/src/reltool.appup.src9
-rw-r--r--lib/reltool/src/reltool.hrl3
-rw-r--r--lib/reltool/src/reltool_server.erl3
-rw-r--r--lib/reltool/test/reltool_app_SUITE.erl9
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/c_src/Makefile.in7
-rw-r--r--lib/runtime_tools/doc/specs/.gitignore1
-rw-r--r--lib/runtime_tools/doc/src/Makefile10
-rw-r--r--lib/runtime_tools/doc/src/ref_man.xml1
-rw-r--r--lib/runtime_tools/doc/src/specs.xml4
-rw-r--r--lib/runtime_tools/doc/src/system_information.xml98
-rw-r--r--lib/runtime_tools/src/erts_alloc_config.erl169
-rw-r--r--lib/runtime_tools/src/observer_backend.erl28
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src4
-rw-r--r--lib/runtime_tools/src/runtime_tools.appup.src10
-rw-r--r--lib/runtime_tools/src/system_information.erl282
-rw-r--r--lib/runtime_tools/test/runtime_tools_SUITE.erl6
-rw-r--r--lib/runtime_tools/test/system_information_SUITE.erl10
-rw-r--r--lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat3
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/src/sasl.app.src4
-rw-r--r--lib/sasl/src/sasl.appup.src12
-rw-r--r--lib/sasl/src/systools_make.erl2
-rw-r--r--lib/sasl/src/systools_rc.erl14
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl35
-rw-r--r--lib/sasl/test/sasl_SUITE.erl73
-rw-r--r--lib/sasl/test/systools_SUITE.erl34
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl2
-rw-r--r--lib/sasl/test/systools_rc_SUITE.erl168
-rw-r--r--lib/sasl/test/test_lib.hrl4
-rw-r--r--lib/snmp/doc/src/snmpa_mib_data.xml4
-rw-r--r--lib/snmp/doc/src/snmpa_mib_storage.xml4
-rw-r--r--lib/snmp/src/app/snmp.app.src4
-rw-r--r--lib/snmp/test/Makefile4
-rw-r--r--lib/snmp/test/modules.mk3
-rw-r--r--lib/snmp/test/snmp_agent_test.erl300
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl22
-rw-r--r--lib/snmp/test/snmp_appup_test.erl491
-rw-r--r--lib/snmp/test/snmp_manager_test.erl337
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl3
-rw-r--r--lib/snmp/test/snmp_test_mgr_counter_server.erl152
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/ssh.xml22
-rw-r--r--lib/ssh/src/ssh.app.src4
-rw-r--r--lib/ssh/src/ssh.appup.src12
-rw-r--r--lib/ssh/src/ssh.erl18
-rw-r--r--lib/ssh/src/ssh_acceptor.erl5
-rw-r--r--lib/ssh/src/ssh_auth.erl4
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl16
-rw-r--r--lib/ssh/src/ssh_file.erl26
-rw-r--r--lib/ssh/src/ssh_message.erl30
-rw-r--r--lib/ssh/src/ssh_sftpd.erl2
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl6
-rw-r--r--lib/ssh/test/ssh_unicode_SUITE.erl5
-rw-r--r--lib/ssl/doc/src/ssl.xml28
-rw-r--r--lib/ssl/src/dtls.erl46
-rw-r--r--lib/ssl/src/dtls_connection.erl724
-rw-r--r--lib/ssl/src/dtls_connection.hrl21
-rw-r--r--lib/ssl/src/dtls_connection_sup.erl12
-rw-r--r--lib/ssl/src/dtls_handshake.erl402
-rw-r--r--lib/ssl/src/dtls_handshake.hrl4
-rw-r--r--lib/ssl/src/dtls_record.erl57
-rw-r--r--lib/ssl/src/dtls_record.hrl13
-rw-r--r--lib/ssl/src/dtls_v1.erl4
-rw-r--r--lib/ssl/src/ssl.app.src4
-rw-r--r--lib/ssl/src/ssl.appup.src51
-rw-r--r--lib/ssl/src/ssl.erl102
-rw-r--r--lib/ssl/src/ssl_alert.erl18
-rw-r--r--lib/ssl/src/ssl_alert.hrl16
-rw-r--r--lib/ssl/src/ssl_api.hrl33
-rw-r--r--lib/ssl/src/ssl_cipher.erl27
-rw-r--r--lib/ssl/src/ssl_cipher.hrl12
-rw-r--r--lib/ssl/src/ssl_connection.erl7
-rw-r--r--lib/ssl/src/ssl_connection.hrl12
-rw-r--r--lib/ssl/src/ssl_handshake.erl64
-rw-r--r--lib/ssl/src/ssl_handshake.hrl16
-rw-r--r--lib/ssl/src/ssl_internal.hrl14
-rw-r--r--lib/ssl/src/ssl_manager.erl8
-rw-r--r--lib/ssl/src/ssl_pkix_db.erl4
-rw-r--r--lib/ssl/src/ssl_record.erl19
-rw-r--r--lib/ssl/src/ssl_record.hrl4
-rw-r--r--lib/ssl/src/ssl_sup.erl18
-rw-r--r--lib/ssl/src/ssl_v3.erl4
-rw-r--r--lib/ssl/src/tls.erl45
-rw-r--r--lib/ssl/src/tls_handshake.erl16
-rw-r--r--lib/ssl/src/tls_handshake.hrl4
-rw-r--r--lib/ssl/src/tls_record.erl11
-rw-r--r--lib/ssl/src/tls_v1.erl10
-rw-r--r--lib/ssl/test/Makefile1
-rw-r--r--lib/ssl/test/make_certs.erl312
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl68
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl536
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl12
-rw-r--r--lib/ssl/test/ssl_test_lib.erl154
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile1
-rw-r--r--lib/stdlib/doc/src/array.xml16
-rw-r--r--lib/stdlib/doc/src/dict.xml10
-rw-r--r--lib/stdlib/doc/src/digraph.xml4
-rw-r--r--lib/stdlib/doc/src/epp.xml64
-rw-r--r--lib/stdlib/doc/src/erl_parse.xml10
-rw-r--r--lib/stdlib/doc/src/ets.xml6
-rw-r--r--lib/stdlib/doc/src/gb_sets.xml90
-rw-r--r--lib/stdlib/doc/src/gb_trees.xml46
-rw-r--r--lib/stdlib/doc/src/lists.xml8
-rw-r--r--lib/stdlib/doc/src/maps.xml318
-rw-r--r--lib/stdlib/doc/src/queue.xml10
-rw-r--r--lib/stdlib/doc/src/ref_man.xml1
-rw-r--r--lib/stdlib/doc/src/sets.xml10
-rw-r--r--lib/stdlib/doc/src/specs.xml1
-rw-r--r--lib/stdlib/doc/src/supervisor.xml6
-rw-r--r--lib/stdlib/doc/src/sys.xml73
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml48
-rw-r--r--lib/stdlib/doc/src/zip.xml13
-rw-r--r--lib/stdlib/src/array.erl102
-rw-r--r--lib/stdlib/src/dets.erl12
-rw-r--r--lib/stdlib/src/dict.erl123
-rw-r--r--lib/stdlib/src/digraph.erl98
-rw-r--r--lib/stdlib/src/digraph_utils.erl44
-rw-r--r--lib/stdlib/src/edlin_expand.erl8
-rw-r--r--lib/stdlib/src/epp.erl181
-rw-r--r--lib/stdlib/src/erl_compile.erl18
-rw-r--r--lib/stdlib/src/erl_eval.erl65
-rw-r--r--lib/stdlib/src/erl_expand_records.erl14
-rw-r--r--lib/stdlib/src/erl_lint.erl393
-rw-r--r--lib/stdlib/src/erl_parse.yrl64
-rw-r--r--lib/stdlib/src/erl_pp.erl32
-rw-r--r--lib/stdlib/src/escript.erl15
-rw-r--r--lib/stdlib/src/ets.erl4
-rw-r--r--lib/stdlib/src/filename.erl6
-rw-r--r--lib/stdlib/src/gb_sets.erl167
-rw-r--r--lib/stdlib/src/gb_trees.erl139
-rw-r--r--lib/stdlib/src/gen.erl6
-rw-r--r--lib/stdlib/src/gen_event.erl37
-rw-r--r--lib/stdlib/src/gen_fsm.erl20
-rw-r--r--lib/stdlib/src/gen_server.erl16
-rw-r--r--lib/stdlib/src/io.erl10
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl21
-rw-r--r--lib/stdlib/src/lists.erl15
-rw-r--r--lib/stdlib/src/maps.erl15
-rw-r--r--lib/stdlib/src/otp_internal.erl43
-rw-r--r--lib/stdlib/src/qlc_pt.erl11
-rw-r--r--lib/stdlib/src/queue.erl75
-rw-r--r--lib/stdlib/src/re.erl3
-rw-r--r--lib/stdlib/src/sets.erl115
-rw-r--r--lib/stdlib/src/slave.erl23
-rw-r--r--lib/stdlib/src/sofs.erl10
-rw-r--r--lib/stdlib/src/stdlib.app.src5
-rw-r--r--lib/stdlib/src/stdlib.appup.src12
-rw-r--r--lib/stdlib/src/supervisor.erl52
-rw-r--r--lib/stdlib/src/supervisor_bridge.erl11
-rw-r--r--lib/stdlib/src/sys.erl65
-rw-r--r--lib/stdlib/src/zip.erl37
-rw-r--r--lib/stdlib/test/Makefile2
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl4
-rw-r--r--lib/stdlib/test/dets_SUITE.erl116
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl165
-rw-r--r--lib/stdlib/test/epp_SUITE.erl106
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl39
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl21
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl257
-rw-r--r--lib/stdlib/test/erl_lint_SUITE_data/predef.erl67
-rw-r--r--lib/stdlib/test/erl_lint_SUITE_data/predef2.erl56
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl33
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl26
-rw-r--r--lib/stdlib/test/ets_SUITE.erl18
-rw-r--r--lib/stdlib/test/expand_test.erl6
-rw-r--r--lib/stdlib/test/expand_test1.erl4
-rw-r--r--lib/stdlib/test/filename_SUITE.erl53
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl11
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl20
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl15
-rw-r--r--lib/stdlib/test/lists_SUITE.erl12
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl19
-rw-r--r--lib/stdlib/test/shell_SUITE.erl2
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl73
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl57
-rw-r--r--lib/stdlib/test/sys_SUITE.erl84
-rw-r--r--lib/stdlib/test/sys_sp1.erl114
-rw-r--r--lib/stdlib/test/sys_sp2.erl107
-rw-r--r--lib/stdlib/test/unicode_SUITE.erl12
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl34
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl275
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl10
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl14
-rw-r--r--lib/syntax_tools/src/igor.erl20
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src3
-rw-r--r--lib/syntax_tools/src/syntax_tools.appup.src22
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl9
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/test_server/doc/src/test_server.xml25
-rw-r--r--lib/test_server/src/configure.in27
-rw-r--r--lib/test_server/src/test_server.app.src5
-rw-r--r--lib/test_server/src/test_server.appup.src22
-rw-r--r--lib/test_server/src/test_server.erl56
-rw-r--r--lib/test_server/src/test_server_ctrl.erl331
-rw-r--r--lib/test_server/src/test_server_sup.erl313
-rw-r--r--lib/test_server/src/ts.erl104
-rw-r--r--lib/test_server/src/ts.unix.config2
-rw-r--r--lib/test_server/src/ts_run.erl5
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl16
-rw-r--r--lib/test_server/vsn.mk2
-rw-r--r--lib/tools/c_src/Makefile.in8
-rw-r--r--lib/tools/emacs/erlang.el24
-rw-r--r--lib/tools/emacs/test.erl.indented11
-rw-r--r--lib/tools/emacs/test.erl.orig11
-rw-r--r--lib/tools/src/cover.erl186
-rw-r--r--lib/tools/src/tools.app.src22
-rw-r--r--lib/tools/src/tools.appup.src40
-rw-r--r--lib/tools/src/xref_compiler.erl4
-rw-r--r--lib/tools/src/xref_utils.erl4
-rw-r--r--lib/tools/test/cover_SUITE.erl125
-rw-r--r--lib/tools/test/cover_SUITE_data/f.erl7
-rw-r--r--lib/tools/test/eprof_SUITE.erl8
-rw-r--r--lib/tools/test/tools_SUITE.erl8
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/typer/src/typer.app.src4
-rw-r--r--lib/typer/src/typer.appup.src22
-rw-r--r--lib/typer/src/typer.erl4
-rw-r--r--lib/typer/test/Makefile65
-rw-r--r--lib/typer/test/typer.spec1
-rw-r--r--lib/typer/test/typer_SUITE.erl56
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/webtool/src/Makefile2
-rw-r--r--lib/webtool/src/webtool.app.src4
-rw-r--r--lib/webtool/src/webtool.appup.src10
-rw-r--r--lib/webtool/test/Makefile65
-rw-r--r--lib/webtool/test/webtool.spec1
-rw-r--r--lib/webtool/test/webtool_SUITE.erl50
-rw-r--r--lib/webtool/vsn.mk2
-rw-r--r--lib/wx/aclocal.m450
-rw-r--r--lib/wx/api_gen/Makefile4
-rw-r--r--lib/wx/api_gen/wx_doxygen.conf3
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.c_src36
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.erl46
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.c_src4
-rw-r--r--lib/wx/api_gen/wx_gen.erl22
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl30
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl54
-rw-r--r--lib/wx/api_gen/wxapi.conf19
-rw-r--r--lib/wx/c_src/Makefile.in10
-rw-r--r--lib/wx/c_src/gen/wxe_derived_dest.h8
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp154
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp286
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h1217
-rw-r--r--lib/wx/c_src/wxe_callback_impl.cpp29
-rw-r--r--lib/wx/c_src/wxe_callback_impl.h27
-rw-r--r--lib/wx/c_src/wxe_events.h31
-rw-r--r--lib/wx/c_src/wxe_impl.cpp32
-rw-r--r--lib/wx/c_src/wxe_impl.h17
-rwxr-xr-xlib/wx/configure.in14
-rw-r--r--lib/wx/include/wx.hrl245
-rw-r--r--lib/wx/src/gen/wxBufferedDC.erl4
-rw-r--r--lib/wx/src/gen/wxBufferedPaintDC.erl4
-rw-r--r--lib/wx/src/gen/wxClientDC.erl4
-rw-r--r--lib/wx/src/gen/wxEvtHandler.erl48
-rw-r--r--lib/wx/src/gen/wxGridCellBoolEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellChoiceEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellFloatEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellNumberEditor.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellTextEditor.erl4
-rw-r--r--lib/wx/src/gen/wxLocale.erl278
-rw-r--r--lib/wx/src/gen/wxMemoryDC.erl4
-rw-r--r--lib/wx/src/gen/wxMirrorDC.erl4
-rw-r--r--lib/wx/src/gen/wxPaintDC.erl4
-rw-r--r--lib/wx/src/gen/wxPostScriptDC.erl4
-rw-r--r--lib/wx/src/gen/wxScreenDC.erl4
-rw-r--r--lib/wx/src/gen/wxWindowDC.erl4
-rw-r--r--lib/wx/src/gen/wx_misc.erl18
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl1217
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl1217
-rw-r--r--lib/wx/src/wx.app.src3
-rw-r--r--lib/wx/src/wx.appup.src10
-rw-r--r--lib/wx/src/wxe.hrl5
-rw-r--r--lib/wx/src/wxe_master.erl18
-rw-r--r--lib/wx/src/wxe_server.erl205
-rw-r--r--lib/wx/src/wxe_util.erl33
-rw-r--r--lib/wx/test/wx_app_SUITE.erl9
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl34
-rw-r--r--lib/wx/test/wx_class_SUITE.erl17
-rw-r--r--lib/wx/test/wx_event_SUITE.erl12
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/src/xmerl.app.src3
-rw-r--r--lib/xmerl/src/xmerl.appup.src31
-rw-r--r--lib/xmerl/test/xmerl_appup_test.erl339
-rw-r--r--lib/xmerl/vsn.mk2
885 files changed, 36022 insertions, 11119 deletions
diff --git a/lib/Makefile b/lib/Makefile
index 5128241563..95170d8a56 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -25,16 +25,17 @@ ERTS_APPLICATIONS = stdlib sasl kernel compiler
# Then these have to be build
ERLANG_APPLICATIONS = tools test_server common_test runtime_tools \
- inets xmerl edoc erl_docgen
+ inets parsetools
# These are only build if -a is given to otp_build or make is used directly
-ALL_ERLANG_APPLICATIONS = snmp otp_mibs erl_interface asn1 jinterface \
+ALL_ERLANG_APPLICATIONS = xmerl edoc erl_docgen snmp otp_mibs erl_interface \
+ asn1 jinterface \
wx debugger reltool gs \
- ic mnesia crypto orber os_mon parsetools syntax_tools \
+ ic mnesia crypto orber os_mon syntax_tools \
public_key ssl observer odbc diameter \
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco webtool \
- eunit ssh typer percept eldap dialyzer hipe
+ eunit ssh typer percept eldap dialyzer hipe ose
ifdef BUILD_ALL
ERLANG_APPLICATIONS += $(ALL_ERLANG_APPLICATIONS)
@@ -59,10 +60,14 @@ else
else
ifdef TERTIARY_BOOTSTRAP
SUB_DIRECTORIES = snmp sasl jinterface ic syntax_tools wx
- else # Not bootstrap build
- SUB_DIRECTORIES = $(ERTS_APPLICATIONS) \
- $(ERLANG_APPLICATIONS) \
- $(EXTRA_APPLICATIONS)
+ else
+ ifdef DOC_BOOTSTRAP
+ SUB_DIRECTORIES = xmerl edoc erl_docgen
+ else # Not bootstrap build
+ SUB_DIRECTORIES = $(ERTS_APPLICATIONS) \
+ $(ERLANG_APPLICATIONS) \
+ $(EXTRA_APPLICATIONS)
+ endif
endif
endif
endif
diff --git a/lib/asn1/Makefile b/lib/asn1/Makefile
index 1bc303b73c..18e95a2471 100644
--- a/lib/asn1/Makefile
+++ b/lib/asn1/Makefile
@@ -25,6 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
#
SUB_DIRECTORIES = src doc/src c_src
+
static_lib: SUB_DIRECTORIES = c_src
@@ -62,7 +63,6 @@ info:
version:
@echo "$(VSN)"
-
# ----------------------------------------------------
# Application (source) release targets
# ----------------------------------------------------
diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile
index ded4b73d1b..a7cd03f516 100644
--- a/lib/asn1/c_src/Makefile
+++ b/lib/asn1/c_src/Makefile
@@ -96,7 +96,12 @@ endif
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
+ifneq ($(findstring ose,$(TARGET)),ose)
opt: $(NIF_SHARED_OBJ_FILE)
+else
+# Do not build dynamic files on OSE
+opt:
+endif
debug: opt
@@ -134,7 +139,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/lib"
+ifneq ($(findstring ose,$(TARGET)),ose)
$(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) "$(RELSYSDIR)/priv/lib"
+endif
$(INSTALL_DIR) "$(RELSYSDIR)/c_src"
$(INSTALL_DATA) *.c "$(RELSYSDIR)/c_src"
diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index 0930010fda..8a0e4b1cf0 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -1320,6 +1320,7 @@ static void unload(ErlNifEnv* env, void* priv_data) {
}
+
static ErlNifFunc nif_funcs[] = {
{ "encode_per_complete", 1, encode_per_complete },
{ "decode_ber_tlv_raw", 1, decode_ber_tlv_raw },
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index eb9f000e75..ee54fdffd7 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -205,16 +205,13 @@ ok
is saved in the <c>People.asn1db</c> file, the
generated Erlang code is compiled using the Erlang compiler and
loaded into the Erlang runtime system. Now there is a user interface
- of encode/2 and decode/2 in the module People, which is invoked by:
+ for <c>encode/2</c> and <c>decode/2</c> in the module People,
+ which is invoked by:
<br></br>
<c><![CDATA['People':encode(<Type name>,<Value>),]]></c> <br></br>
or <br></br>
-<c><![CDATA['People':decode(<Type name>,<Value>),]]></c> <br></br>
-
- Alternatively one can use the <c><![CDATA[asn1rt:encode(<Module name> ,<Type name>,<Value>)]]></c> and <c><![CDATA[asn1rt:decode(< Module name>,<Type name>,<Value>)]]></c> calls.
- However, they are not as efficient as the previous methods since they
- result in an additional <c>apply/3</c> call.</p>
+<c><![CDATA['People':decode(<Type name>,<Value>),]]></c></p>
<p>Assume there is a network
application which receives instances of the ASN.1 defined
type Person, modifies and sends them back again:</p>
@@ -241,16 +238,14 @@ receive
encoding-rules.
<br></br>
The encoder and the decoder can also be run from
- the shell. The following dialogue with the shell illustrates
- how the functions
- <c>asn1rt:encode/3</c> and <c>asn1rt:decode/3</c> are used.</p>
+ the shell.</p>
<pre>
2> <input>Rockstar = {'Person',"Some Name",roving,50}.</input>
{'Person',"Some Name",roving,50}
-3> <input>{ok,Bin} = asn1rt:encode('People','Person',Rockstar).</input>
+3> <input>{ok,Bin} = 'People':encode('Person',Rockstar).</input>
{ok,&lt;&lt;243,17,19,9,83,111,109,101,32,78,97,109,101,2,1,2,
2,1,50&gt;&gt;}
-4> <input>{ok,Person} = asn1rt:decode('People','Person',Bin).</input>
+4> <input>{ok,Person} = 'People':decode('Person',Bin).</input>
{ok,{'Person',"Some Name",roving,50}}
5> </pre>
</section>
@@ -279,11 +274,8 @@ The encoder and the decoder can also be run from
(including the compiler).</p>
</item>
<item>
- <p>The module <c>asn1rt</c> which provides the run-time functions.
- However, it is preferable to use the generated <c>encode/2</c> and
- <c>decode/2</c> functions in each module, ie.
- Module:encode(Type,Value), in favor of the <c>asn1rt</c>
- interface.</p>
+ <p>The module <c>asn1rt_nif</c> which provides the run-time functions
+ for the ASN.1 decoder for the BER back-end.</p>
</item>
</list>
<p>The reason for the division of the interface into compile-time
@@ -384,25 +376,9 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
<section>
<title>Run-time Functions</title>
- <p>A brief description of the major functions is given here. For a
- complete description of each function see
- <seealso marker="asn1rt"> the Asn1 Reference Manual</seealso>, the <c>asn1rt</c> module.</p>
- <p>The generic run-time encode and decode functions can be invoked as below:</p>
- <pre>
-asn1rt:encode('H323-MESSAGES','SomeChoiceType',{call,"octetstring"}).
-asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
- <p>Or, preferable like:</p>
- <pre>
-'H323-MESSAGES':encode('SomeChoiceType',{call,"octetstring"}).
-'H323-MESSAGES':decode('SomeChoiceType',Bytes). </pre>
- <p>The asn1 nif is enabled in two occasions: encoding of
- asn1 values when the asn1 spec is compiled with <c>per</c> and
- or decode of encoded asn1 values when the asn1 spec is
- compiled with <c>ber</c>. In
- those cases the nif will be loaded automatically at the first call
- to <c>encode</c>/<c>decode</c>. If one doesn't want the performance
- overhead of the nif being loaded at the first call it is possible
- to load the nif separately by loading the <c>asn1rt_nif</c> module.</p>
+ <p>When an ASN.1 specification is compiled with the <c>ber</c>
+ option, the module <c>asn1rt_nif</c> module and the NIF library in
+ <c>asn1/priv_dir</c> will be needed at run-time.</p>
<p>By invoking the function <c>info/0</c> in a generated module, one
gets information about which compiler options were used.</p>
</section>
@@ -414,8 +390,8 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
a line number indicating where in the source file the error
was detected. If no errors are found, an Erlang ASN.1 module will
be created as default.</p>
- <p>The run-time encoders and decoders (in the <c>asn1rt</c> module) do
- execute within a catch and returns <c>{ok, Data}</c> or
+ <p>The run-time encoders and decoders execute within a catch and
+ returns <c>{ok, Data}</c> or
<c>{error, {asn1, Description}}</c> where
<c>Description</c> is
an Erlang term describing the error. </p>
diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml
index 5871c8ad68..4d5a1a402a 100644
--- a/lib/asn1/doc/src/asn1ct.xml
+++ b/lib/asn1/doc/src/asn1ct.xml
@@ -61,7 +61,7 @@
and <c>uper_bin</c> options will still work, but will print a warning.
</p>
<p>Another change in R16 is that the generated <c>encode/2</c>
- function (and <c>asn1rt:encode/3</c>) always returns a binary.
+ function always returns a binary.
The <c>encode/2</c> function for the BER back-end used to return
an iolist.</p>
</note>
@@ -326,6 +326,8 @@ File3.asn </pre>
not always checked. Returns <c>{ok, Bytes}</c> if successful or
<c>{error, Reason}</c> if an error occurred.
</p>
+ <p>This function is deprecated.
+ Use <c>Module:encode(Type, Value)</c> instead.</p>
</desc>
</func>
<func>
@@ -339,6 +341,8 @@ File3.asn </pre>
<desc>
<p>Decodes <c>Type</c> from <c>Module</c> from the binary
<c>Bytes</c>. Returns <c>{ok, Value}</c> if successful.</p>
+ <p>This function is deprecated.
+ Use <c>Module:decode(Type, Bytes)</c> instead.</p>
</desc>
</func>
<func>
diff --git a/lib/asn1/doc/src/asn1rt.xml b/lib/asn1/doc/src/asn1rt.xml
index 6e22e45d93..3cf56b01ca 100644
--- a/lib/asn1/doc/src/asn1rt.xml
+++ b/lib/asn1/doc/src/asn1rt.xml
@@ -34,9 +34,12 @@
<module>asn1rt</module>
<modulesummary>ASN.1 runtime support functions</modulesummary>
<description>
- <p>This module is the interface module for the ASN.1 runtime support functions.
- To encode and decode ASN.1 types in runtime the functions in this module
- should be used.</p>
+ <warning>
+ <p>
+ All functions in this module are deprecated and will be
+ removed in a future release.
+ </p>
+ </warning>
</description>
<funcs>
@@ -52,6 +55,7 @@
<desc>
<p>Decodes <c>Type</c> from <c>Module</c> from the binary <c>Bytes</c>.
Returns <c>{ok,Value}</c> if successful.</p>
+ <p>Use <c>Module:decode(Type, Bytes)</c> instead of this function.</p>
</desc>
</func>
@@ -65,16 +69,13 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 module
- <c>Module</c>. Returns a possibly nested list of bytes and or binaries
- if successful. To get as fast execution as possible the
- encode function only performs rudimentary tests that the input
- <c>Value</c>
- is a correct instance of <c>Type</c>. The length of strings is for example
- not always checked. </p>
- <note>
- <p>Starting in R16, <c>Bytes</c> is always a binary.</p>
- </note>
+ <p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1
+ module <c>Module</c>. Returns a binary if successful. To get
+ as fast execution as possible the encode function only
+ performs rudimentary tests that the input <c>Value</c> is a
+ correct instance of <c>Type</c>. The length of strings is, for
+ example, not always checked. </p>
+ <p>Use <c>Module:encode(Type, Value)</c> instead of this function.</p>
</desc>
</func>
@@ -90,6 +91,7 @@
<p><c>info/1</c> returns the version of the asn1 compiler that was
used to compile the module. It also returns the compiler options
that was used.</p>
+ <p>Use <c>Module:info()</c> instead of this function.</p>
</desc>
</func>
@@ -106,6 +108,7 @@
to a list of integers, where each integer represents one
character as its unicode value. The function fails if the binary
is not a properly encoded UTF8 string.</p>
+ <p>Use <seealso marker="stdlib:unicode#characters_to_list-1">unicode:characters_to_list/1</seealso> instead of this function.</p>
</desc>
</func>
@@ -121,6 +124,7 @@
<p><c>utf8_list_to_binary/1</c> Transforms a list of integers,
where each integer represents one character as its unicode
value, to a UTF8 encoded binary.</p>
+ <p>Use <seealso marker="stdlib:unicode#characters_to_binary-1">unicode:characters_to_binary/1</seealso> instead of this function.</p>
</desc>
</func>
diff --git a/lib/asn1/src/asn1.app.src b/lib/asn1/src/asn1.app.src
index f2ee8deb75..02cbba0f10 100644
--- a/lib/asn1/src/asn1.app.src
+++ b/lib/asn1/src/asn1.app.src
@@ -10,5 +10,6 @@
asn1db
]},
{env, []},
- {applications, [kernel, stdlib]}
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/asn1/src/asn1.appup.src b/lib/asn1/src/asn1.appup.src
index 2d11eddfbf..e4b3508cc4 100644
--- a/lib/asn1/src/asn1.appup.src
+++ b/lib/asn1/src/asn1.appup.src
@@ -1,11 +1,21 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
{"%VSN%",
-% This version does not change anything of the runtime modules
-% Only changes in compile time modules and thus no need for upgrade on target
-[
- ],
- [
- ]}.
-
-
-
-
+ [{<<".*">>,[{restart_application, asn1}]}],
+ [{<<".*">>,[{restart_application, asn1}]}]
+}.
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 531a4935fe..8470e5a1b4 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -19,6 +19,10 @@
%%
%%
-module(asn1ct).
+-deprecated([decode/3,encode/3]).
+-compile([{nowarn_deprecated_function,{asn1rt,decode,3}},
+ {nowarn_deprecated_function,{asn1rt,encode,2}},
+ {nowarn_deprecated_function,{asn1rt,encode,3}}]).
%% Compile Time functions for ASN.1 (e.g ASN.1 compiler).
@@ -562,6 +566,8 @@ get_pos_of_def(#pobjectdef{pos=Pos}) ->
Pos;
get_pos_of_def(#pobjectsetdef{pos=Pos}) ->
Pos;
+get_pos_of_def(#'Externaltypereference'{pos=Pos}) ->
+ Pos;
get_pos_of_def(#'Externalvaluereference'{pos=Pos}) ->
Pos;
get_pos_of_def(_) ->
@@ -1040,7 +1046,7 @@ get_file_list1(Stream,Dir,Includes,Acc) ->
Ret = io:get_line(Stream,''),
case Ret of
eof ->
- file:close(Stream),
+ ok = file:close(Stream),
lists:reverse(Acc);
FileName ->
SuffixedNameList =
@@ -1926,8 +1932,9 @@ read_config_file(ModuleName) ->
Includes = [I || {i,I} <- Options],
read_config_file1(ModuleName,Includes);
{error,Reason} ->
- file:format_error(Reason),
- throw({error,{"error reading asn1 config file",Reason}})
+ Error = "error reading asn1 config file: " ++
+ file:format_error(Reason),
+ throw({error,Error})
end.
read_config_file1(ModuleName,[]) ->
case filename:extension(ModuleName) of
@@ -1945,8 +1952,9 @@ read_config_file1(ModuleName,[H|T]) ->
{error,enoent} ->
read_config_file1(ModuleName,T);
{error,Reason} ->
- file:format_error(Reason),
- throw({error,{"error reading asn1 config file",Reason}})
+ Error = "error reading asn1 config file: " ++
+ file:format_error(Reason),
+ throw({error,Error})
end.
get_config_info(CfgList,InfoType) ->
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index f94f3b56bc..e788aa5c6c 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -270,46 +270,30 @@ check_exports(S,Module = #module{}) ->
end
end.
-check_imports(S,Module = #module{ }) ->
- case Module#module.imports of
- {imports,[]} ->
- [];
- {imports,ImportList} when is_list(ImportList) ->
- check_imports2(S,ImportList,[]);
- _ ->
- []
- end.
-check_imports2(_S,[],Acc) ->
+check_imports(S, #module{imports={imports,Imports}}) ->
+ check_imports_1(S, Imports, []).
+
+check_imports_1(_S, [], Acc) ->
Acc;
-check_imports2(S,[#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],Acc) ->
- NameOfDef =
- fun(#'Externaltypereference'{type=N}) -> N;
- (#'Externalvaluereference'{value=N}) -> N
- end,
- Module = NameOfDef(ModuleRef),
- Refs = [{M,R}||{{M,_},R} <- [{catch get_referenced_type(S,Ref),Ref}||Ref <- Imports]],
- {Illegal,Other} = lists:splitwith(fun({error,_}) -> true;(_) -> false end,
- Refs),
- ChainedRefs = [R||{M,R} <- Other, M =/= Module],
- IllegalRefs = [R||{error,R} <- Illegal] ++
- [R||{M,R} <- ChainedRefs,
- ok =/= chained_import(S,Module,M,NameOfDef(R))],
- ReportError =
- fun(Ref) ->
- NewS=S#state{type=Ref,tname=NameOfDef(Ref)},
- error({import,"imported undefined entity",NewS})
- end,
- check_imports2(S,SFMs,[ReportError(Err)||Err <- IllegalRefs]++Acc).
+check_imports_1(S, [#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs], Acc0) ->
+ Module = name_of_def(ModuleRef),
+ Refs0 = [{catch get_referenced_type(S, Ref),Ref} || Ref <- Imports],
+ Refs = [{M,R} || {{M,_},R} <- Refs0],
+ {Illegal,Other} = lists:splitwith(fun({error,_}) -> true;
+ (_) -> false
+ end, Refs),
+ ChainedRefs = [R || {M,R} <- Other, M =/= Module],
+ IllegalRefs = [R || {error,R} <- Illegal] ++
+ [R || {M,R} <- ChainedRefs,
+ ok =/= chained_import(S, Module, M, name_of_def(R))],
+ Acc = [return_asn1_error(S, Ref, {undefined_import,name_of_def(Ref),Module}) ||
+ Ref <- IllegalRefs] ++ Acc0,
+ check_imports_1(S, SFMs, Acc).
chained_import(S,ImpMod,DefMod,Name) ->
%% Name is a referenced structure that is not defined in ImpMod,
%% but must be present in the Imports list of ImpMod. The chain of
%% imports of Name must end in DefMod.
- NameOfDef =
- fun(#'Externaltypereference'{type=N}) -> N;
- (#'Externalvaluereference'{value=N}) -> N;
- (Other) -> Other
- end,
GetImports =
fun(_M_) ->
case asn1_db:dbget(_M_,'MODULE') of
@@ -321,9 +305,9 @@ chained_import(S,ImpMod,DefMod,Name) ->
FindNameInImports =
fun([],N,_) -> {no_mod,N};
([#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],N,F) ->
- case [NameOfDef(X)||X <- Imports, NameOfDef(X) =:= N] of
+ case [name_of_def(X) || X <- Imports, name_of_def(X) =:= N] of
[] -> F(SFMs,N,F);
- [N] -> {NameOfDef(ModuleRef),N}
+ [N] -> {name_of_def(ModuleRef),N}
end
end,
case GetImports(ImpMod) of
@@ -1567,13 +1551,13 @@ check_defaultfields(S, Fields, ClassFields) ->
[] ->
ok;
[_|_]=Invalid ->
- throw(asn1_error(S, T, {invalid_fields,Invalid,Obj}))
+ asn1_error(S, T, {invalid_fields,Invalid,Obj})
end,
case ordsets:subtract(Mandatory, Present) of
[] ->
check_defaultfields_1(S, Fields, ClassFields, []);
[_|_]=Missing ->
- throw(asn1_error(S, T, {missing_mandatory_fields,Missing,Obj}))
+ asn1_error(S, T, {missing_mandatory_fields,Missing,Obj})
end.
check_defaultfields_1(_S, [], _ClassFields, Acc) ->
@@ -2464,7 +2448,7 @@ normalize_value(S0, Type, {'DEFAULT',Value}, NameList) ->
{'BIT STRING',CType,_} ->
normalize_bitstring(S,Value,CType);
{'OCTET STRING',CType,_} ->
- normalize_octetstring(S,Value,CType);
+ normalize_octetstring(S0, Value, CType);
{'NULL',_CType,_} ->
%%normalize_null(Value);
'NULL';
@@ -2612,20 +2596,9 @@ normalize_octetstring(S,Value,CType) ->
fun normalize_octetstring/3,[]);
{Name,String} when is_atom(Name) ->
normalize_octetstring(S,String,CType);
- List when is_list(List) ->
- %% check if list elements are valid octet values
- lists:map(fun([])-> ok;
- (H)when H > 255->
- asn1ct:warning("not legal octet value ~p in OCTET STRING, ~p~n",
- [H,List],S,
- "not legal octet value ~p in OCTET STRING");
- (_)-> ok
- end, List),
- List;
- Other ->
- asn1ct:warning("unknown default value ~p~n",[Other],S,
- "unknown default value"),
- Value
+ _ ->
+ Item = S#state.value,
+ asn1_error(S, Item, illegal_octet_string_value)
end.
normalize_objectidentifier(S, Value) ->
@@ -2642,18 +2615,21 @@ normalize_objectdescriptor(Value) ->
normalize_real(Value) ->
Value.
-normalize_enumerated(S, Id, {Base,Ext}) ->
+normalize_enumerated(S, Id0, NNL) ->
+ {Id,_} = lookup_enum_value(S, Id0, NNL),
+ Id.
+
+lookup_enum_value(S, Id, {Base,Ext}) ->
%% Extensible ENUMERATED.
- normalize_enumerated(S, Id, Base++Ext);
-normalize_enumerated(S, #'Externalvaluereference'{value=Id},
- NamedNumberList) ->
- normalize_enumerated(S, Id, NamedNumberList);
-normalize_enumerated(S, Id, NamedNumberList) when is_atom(Id) ->
- case lists:keymember(Id, 1, NamedNumberList) of
- true ->
- Id;
+ lookup_enum_value(S, Id, Base++Ext);
+lookup_enum_value(S, #'Externalvaluereference'{value=Id}, NNL) ->
+ lookup_enum_value(S, Id, NNL);
+lookup_enum_value(S, Id, NNL) when is_atom(Id) ->
+ case lists:keyfind(Id, 1, NNL) of
+ {_,_}=Ret ->
+ Ret;
false ->
- throw(asn1_error(S, S#state.value, {undefined,Id}))
+ asn1_error(S, S#state.value, {undefined,Id})
end.
normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) ->
@@ -3088,12 +3064,11 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
Ct=maybe_illicit_implicit_tag(open_type,Tag),
TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
'INTEGER' ->
- check_integer(S,[],Constr),
TempNewDef#newt{tag=
merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
{'INTEGER',NamedNumberList} ->
- TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList,Constr)},
+ TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList)},
tag=
merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
'REAL' ->
@@ -3101,8 +3076,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
TempNewDef#newt{tag=merge_tags(Tag,?TAG_PRIMITIVE(?N_REAL))};
{'BIT STRING',NamedNumberList} ->
- NewL = check_bitstring(S,NamedNumberList,Constr),
-%% erlang:display({asn1ct_check,NamedNumberList,NewL}),
+ NewL = check_bitstring(S, NamedNumberList),
TempNewDef#newt{type={'BIT STRING',NewL},
tag=
merge_tags(Tag,?TAG_PRIMITIVE(?N_BIT_STRING))};
@@ -3786,8 +3760,9 @@ resolv_value(S,Val) ->
resolv_value1(S,Id).
resolv_value1(S, ERef = #'Externalvaluereference'{value=Name}) ->
- case catch resolve_namednumber(S,S#state.type,Name) of
- V when is_integer(V) -> V;
+ case catch resolve_namednumber(S, S#state.type, Name) of
+ V when is_integer(V) ->
+ V;
_ ->
case get_referenced_type(S,ERef) of
{Err,_Reason} when Err == error; Err == 'EXIT' ->
@@ -3840,21 +3815,20 @@ resolve_value_from_object(S,Object,FieldName) ->
end.
-
resolve_namednumber(S,#typedef{typespec=Type},Name) ->
case Type#type.def of
{'ENUMERATED',NameList} ->
- NamedNumberList=check_enumerated(S,NameList,Type#type.constraint),
- N = normalize_enumerated(S,Name,NamedNumberList),
- {value,{_,V}} = lists:keysearch(N,1,NamedNumberList),
- V;
+ resolve_namednumber_1(S, Name, NameList, Type);
{'INTEGER',NameList} ->
- NamedNumberList = check_enumerated(S,NameList,Type#type.constraint),
- {value,{_,V}} = lists:keysearch(Name,1,NamedNumberList),
- V;
+ resolve_namednumber_1(S, Name, NameList, Type);
_ ->
not_enumerated
end.
+
+resolve_namednumber_1(S, Name, NameList, Type) ->
+ NamedNumberList = check_enumerated(S, NameList, Type#type.constraint),
+ {_,N} = lookup_enum_value(S, Name, NamedNumberList),
+ N.
check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) ->
{RefMod,CTDef} = get_referenced_type(S,Type#type.def),
@@ -3935,9 +3909,9 @@ check_constraint(S,{simpletable,Type}) ->
#'Externaltypereference'{} ->
ERef = check_externaltypereference(S,C),
{simpletable,ERef#'Externaltypereference'.type};
- #type{def=#'Externaltypereference'{type=T}} ->
- check_externaltypereference(S,C#type.def),
- {simpletable,T};
+ #type{def=#'Externaltypereference'{}=ExtTypeRef} ->
+ ERef = check_externaltypereference(S, ExtTypeRef),
+ {simpletable,ERef#'Externaltypereference'.type};
{valueset,#type{def=ERef=#'Externaltypereference'{}}} -> % this is an object set
{_,TDef} = get_referenced_type(S,ERef),
case TDef#typedef.typespec of
@@ -4919,73 +4893,46 @@ imported1(Name,
end;
imported1(_Name,[]) ->
false.
-
-check_integer(_S,[],_C) ->
+%% Check the named number list for an INTEGER or a BIT STRING.
+check_named_number_list(_S, []) ->
[];
-check_integer(S,NamedNumberList,_C) ->
- case [X || X <- NamedNumberList, tuple_size(X) =:= 2] of
- NamedNumberList ->
- %% An already checked integer with NamedNumberList
- NamedNumberList;
- _ ->
- case check_unique(NamedNumberList,2) of
- [] ->
- check_int(S,NamedNumberList,[]);
- L when is_list(L) ->
- error({type,{duplicates,L},S}),
- unchanged
- end
+check_named_number_list(_S, [{_,_}|_]=NNL) ->
+ %% The named number list has already been checked.
+ NNL;
+check_named_number_list(S, NNL0) ->
+ %% Check that the names are unique.
+ T = S#state.type,
+ case check_unique(NNL0, 2) of
+ [] ->
+ NNL1 = [{Id,resolve_valueref(S, Val)} || {'NamedNumber',Id,Val} <- NNL0],
+ NNL = lists:keysort(2, NNL1),
+ case check_unique(NNL, 2) of
+ [] ->
+ NNL;
+ [Val|_] ->
+ asn1_error(S, T, {value_reused,Val})
+ end;
+ [H|_] ->
+ asn1_error(S, T, {namelist_redefinition,H})
end.
-
-check_int(S,[{'NamedNumber',Id,Num}|T],Acc) when is_integer(Num) ->
- check_int(S,T,[{Id,Num}|Acc]);
-check_int(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
- Val = dbget_ex(S,S#state.mname,Name),
- check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
-check_int(S,[{'NamedNumber',Id,{'Externalvaluereference',_,Mod,Name}}|T],Acc) ->
- Val = dbget_ex(S,Mod,Name),
- check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
-check_int(_S,[],Acc) ->
- lists:keysort(2,Acc).
+resolve_valueref(S, #'Externalvaluereference'{module=Mod,value=Name}) ->
+ dbget_ex(S, Mod, Name);
+resolve_valueref(_, Val) when is_integer(Val) ->
+ Val.
-check_real(_S,_Constr) ->
- ok.
+check_integer(S, NNL) ->
+ check_named_number_list(S, NNL).
-check_bitstring(_S,[],_Constr) ->
- [];
-check_bitstring(S,NamedNumberList,_Constr) ->
- case check_unique(NamedNumberList,2) of
- [] ->
- check_bitstr(S,NamedNumberList,[]);
- L when is_list(L) ->
- error({type,{duplicates,L},S}),
- unchanged
- end.
+check_bitstring(S, NNL0) ->
+ NNL = check_named_number_list(S, NNL0),
+ _ = [asn1_error(S, S#state.type, {invalid_bit_number,Bit}) ||
+ {_,Bit} <- NNL, Bit < 0],
+ NNL.
-check_bitstr(S,[{'NamedNumber',Id,Num}|T],Acc)when is_integer(Num) ->
- check_bitstr(S,T,[{Id,Num}|Acc]);
-check_bitstr(S,[{'NamedNumber',Id,Name}|T],Acc) when is_atom(Name) ->
-%%check_bitstr(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
-%% io:format("asn1ct_check:check_bitstr/3 hej hop ~w~n",[Name]),
- Val = dbget_ex(S,S#state.mname,Name),
-%% io:format("asn1ct_check:check_bitstr/3: ~w~n",[Val]),
- check_bitstr(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
-check_bitstr(S,[],Acc) ->
- case check_unique(Acc,2) of
- [] ->
- lists:keysort(2,Acc);
- L when is_list(L) ->
- error({type,{duplicate_values,L},S}),
- unchanged
- end;
-%% When a BIT STRING already is checked, for instance a COMPONENTS OF S
-%% where S is a sequence that has a component that is a checked BS, the
-%% NamedNumber list is a list of {atom(),integer()} elements.
-check_bitstr(S,[El={Id,Num}|Rest],Acc) when is_atom(Id),is_integer(Num) ->
- check_bitstr(S,Rest,[El|Acc]).
-
+check_real(_S,_Constr) ->
+ ok.
%% Check INSTANCE OF
%% check that DefinedObjectClass is of TYPE-IDENTIFIER class
@@ -4996,20 +4943,16 @@ check_instance_of(S,DefinedObjectClass,Constraint) ->
check_type_identifier(S,DefinedObjectClass),
iof_associated_type(S,Constraint).
-
-check_type_identifier(_S,'TYPE-IDENTIFIER') ->
- ok;
-check_type_identifier(S,Eref=#'Externaltypereference'{}) ->
- case get_referenced_type(S,Eref) of
- {_,#classdef{name='TYPE-IDENTIFIER'}} -> ok;
- {_,#classdef{typespec=NextEref}}
- when is_record(NextEref,'Externaltypereference') ->
- check_type_identifier(S,NextEref);
+check_type_identifier(S, Eref=#'Externaltypereference'{type=Class}) ->
+ case get_referenced_type(S, Eref) of
+ {_,#classdef{name='TYPE-IDENTIFIER'}} ->
+ ok;
+ {_,#classdef{typespec=#'Externaltypereference'{}=NextEref}} ->
+ check_type_identifier(S, NextEref);
{_,TD=#typedef{typespec=#type{def=#'Externaltypereference'{}}}} ->
- check_type_identifier(S,(TD#typedef.typespec)#type.def);
- Err ->
- error({type,{"object set in type INSTANCE OF "
- "not of class TYPE-IDENTIFIER",Eref,Err},S})
+ check_type_identifier(S, (TD#typedef.typespec)#type.def);
+ _ ->
+ asn1_error(S, S#state.type, {illegal_instance_of,Class})
end.
iof_associated_type(S,[]) ->
@@ -5139,9 +5082,6 @@ check_enumerated(S,NamedNumberList,_Constr) ->
%% the latter is returned if the ENUMERATION contains EXTENSIONMARK
check_enum(S,[{'NamedNumber',Id,Num}|T],Acc1,Acc2,Root) when is_integer(Num) ->
check_enum(S,T,[{Id,Num}|Acc1],Acc2,Root);
-check_enum(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc1,Acc2,Root) ->
- Val = dbget_ex(S,S#state.mname,Name),
- check_enum(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc1,Acc2,Root);
check_enum(S,['EXTENSIONMARK'|T],Acc1,Acc2,_Root) ->
NewAcc2 = lists:keysort(2,Acc1),
NewList = enum_number(lists:reverse(Acc2),NewAcc2,0,[],[]),
@@ -6757,7 +6697,7 @@ storeindb(#state{mname=Module}=S, [H|T], Errors) ->
storeindb(S, T, Errors);
Prev ->
PrevLine = asn1ct:get_pos_of_def(Prev),
- {error,Error} = asn1_error(S, H, {already_defined,Name,PrevLine}),
+ Error = return_asn1_error(S, H, {already_defined,Name,PrevLine}),
storeindb(S, T, [Error|Errors])
end;
storeindb(_, [], []) ->
@@ -6804,20 +6744,37 @@ findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) ->
{lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc),
lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}.
-asn1_error(#state{mname=Where}, Item, Error) ->
+return_asn1_error(#state{mname=Where}, Item, Error) ->
Pos = asn1ct:get_pos_of_def(Item),
- {error,{structured_error,{Where,Pos},?MODULE,Error}}.
+ {structured_error,{Where,Pos},?MODULE,Error}.
+
+asn1_error(S, Item, Error) ->
+ throw({error,return_asn1_error(S, Item, Error)}).
format_error({already_defined,Name,PrevLine}) ->
io_lib:format("the name ~p has already been defined at line ~p",
[Name,PrevLine]);
+format_error({illegal_instance_of,Class}) ->
+ io_lib:format("using INSTANCE OF on class '~s' is illegal, "
+ "because INSTANCE OF may only be used on the class TYPE-IDENTFIER",
+ [Class]);
+format_error(illegal_octet_string_value) ->
+ "expecting a bstring or an hstring as value for an OCTET STRING";
format_error({invalid_fields,Fields,Obj}) ->
io_lib:format("invalid ~s in ~p", [format_fields(Fields),Obj]);
+format_error({invalid_bit_number,Bit}) ->
+ io_lib:format("the bit number '~p' is invalid", [Bit]);
format_error({missing_mandatory_fields,Fields,Obj}) ->
io_lib:format("missing mandatory ~s in ~p",
[format_fields(Fields),Obj]);
+format_error({namelist_redefinition,Name}) ->
+ io_lib:format("the name '~s' can not be redefined", [Name]);
format_error({undefined,Name}) ->
io_lib:format("'~s' is referenced, but is not defined", [Name]);
+format_error({undefined_import,Ref,Module}) ->
+ io_lib:format("'~s' is not exported from ~s", [Ref,Module]);
+format_error({value_reused,Val}) ->
+ io_lib:format("the value '~p' is used more than once", [Val]);
format_error(Other) ->
io_lib:format("~p", [Other]).
@@ -6833,14 +6790,6 @@ error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
Pos = Ref#'Externaltypereference'.pos,
io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
{error,{export,Pos,Mname,Typename,Msg}};
-error({import,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
- PosOfDef =
- fun(#'Externaltypereference'{pos=P}) -> P;
- (#'Externalvaluereference'{pos=P}) -> P
- end,
- Pos = PosOfDef(Ref),
- io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
- {error,{import,Pos,Mname,Typename,Msg}};
% error({type,{Msg1,Msg2},#state{mname=Mname,type=Type,tname=Typename}})
% when is_record(Type,typedef) ->
% io:format("asn1error:~p:~p:~p ~p~n",
@@ -7141,3 +7090,6 @@ check_fold(S, [H|T], Check) ->
[Error|check_fold(S, T, Check)]
end;
check_fold(_, [], Check) when is_function(Check, 3) -> [].
+
+name_of_def(#'Externaltypereference'{type=N}) -> N;
+name_of_def(#'Externalvaluereference'{value=N}) -> N.
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 71d870b4ce..44b050e59d 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -71,7 +71,7 @@ pgen_module(OutFile,Erules,Module,
HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent),
asn1ct_name:start(),
ErlFile = lists:concat([OutFile,".erl"]),
- open_output_file(ErlFile),
+ _ = open_output_file(ErlFile),
asn1ct_func:start_link(),
gen_head(Erules,Module,HrlGenerated),
pgen_exports(Erules,Module,TypeOrVal),
@@ -190,7 +190,7 @@ pgen_check_defaultval(Erules,Module) ->
"********~n~n",[X])
end,
lists:foreach(Fun,CheckObjects),
- file:close(IoDevice);
+ ok = file:close(IoDevice);
_ -> ok
end,
gen_check_defaultval(Erules,Module,CheckObjects).
@@ -1124,8 +1124,23 @@ pgen_info() ->
open_hrl(OutFile,Module) ->
File = lists:concat([OutFile,".hrl"]),
- open_output_file(File),
- gen_hrlhead(Module).
+ _ = open_output_file(File),
+ gen_hrlhead(Module),
+ Protector = hrl_protector(OutFile),
+ emit(["-ifndef(",Protector,").\n",
+ "-define(",Protector,", true).\n"
+ "\n"]).
+
+hrl_protector(OutFile) ->
+ BaseName = filename:basename(OutFile),
+ P = "_" ++ string:to_upper(BaseName) ++ "_HRL_",
+ [if
+ $A =< C, C =< $Z -> C;
+ $a =< C, C =< $a -> C;
+ $0 =< C, C =< $9 -> C;
+ true -> $_
+ end || C <- P].
+
%% EMIT functions ************************
%% ***************************************
@@ -1232,6 +1247,8 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) ->
0 ->
0;
Y ->
+ Protector = hrl_protector(get(outfile)),
+ emit(["-endif. %% ",Protector,"\n"]),
close_output_file(),
asn1ct:verbose("--~p--~n",
[{generated,lists:concat([get(outfile),".hrl"])}],
diff --git a/lib/asn1/src/asn1ct_table.erl b/lib/asn1/src/asn1ct_table.erl
index a5eb6d0413..2eca80eda3 100644
--- a/lib/asn1/src/asn1ct_table.erl
+++ b/lib/asn1/src/asn1ct_table.erl
@@ -22,34 +22,25 @@
%% Table abstraction module for ASN.1 compiler
-export([new/1]).
--export([new/2]).
-export([new_reuse/1]).
--export([new_reuse/2]).
-export([exists/1]).
-export([size/1]).
-export([insert/2]).
-export([lookup/2]).
-export([match/2]).
-export([to_list/1]).
--export([delete/1]). % TODO: Remove (since we run in a separate process)
+-export([delete/1]).
-%% Always creates a new table
-new(Table) -> new(Table, []).
-new(Table, Options) ->
- TableId = case get(Table) of
- undefined ->
- ets:new(Table, Options);
- _ ->
- delete(Table),
- ets:new(Table, Options)
- end,
+%% Always create a new table.
+new(Table) ->
+ undefined = get(Table), %Assertion.
+ TableId = ets:new(Table, []),
put(Table, TableId).
-%% Only create it if it doesn't exist yet
-new_reuse(Table) -> new_reuse(Table, []).
-new_reuse(Table, Options) ->
- not exists(Table) andalso new(Table, Options).
+%% Only create it if it doesn't exist yet.
+new_reuse(Table) ->
+ not exists(Table) andalso new(Table).
exists(Table) -> get(Table) =/= undefined.
@@ -63,14 +54,17 @@ match(Table, MatchSpec) -> ets:match(get(Table), MatchSpec).
to_list(Table) -> ets:tab2list(get(Table)).
+%% Deleting tables is no longer strictly necessary since each compilation
+%% runs in separate process, but it will reduce memory consumption
+%% especially when many compilations are run in parallel.
+
delete(Tables) when is_list(Tables) ->
[delete(T) || T <- Tables],
true;
delete(Table) when is_atom(Table) ->
- case get(Table) of
+ case erase(Table) of
undefined ->
true;
TableId ->
- ets:delete(TableId),
- erase(Table)
+ ets:delete(TableId)
end.
diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl
index 85199c65ec..33f4379173 100644
--- a/lib/asn1/src/asn1ct_tok.erl
+++ b/lib/asn1/src/asn1ct_tok.erl
@@ -36,7 +36,7 @@ process(Stream,Lno,R) ->
process(io:get_line(Stream, ''), Stream,Lno+1,R).
process(eof, Stream,Lno,R) ->
- file:close(Stream),
+ ok = file:close(Stream),
lists:flatten(lists:reverse([{'$end',Lno}|R]));
diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl
index a86c963b9d..221cd991a7 100644
--- a/lib/asn1/src/asn1ct_value.erl
+++ b/lib/asn1/src/asn1ct_value.erl
@@ -18,6 +18,7 @@
%%
%%
-module(asn1ct_value).
+-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}}]).
%% Generate Erlang values for ASN.1 types.
%% The value is randomized within it's constraints
@@ -352,7 +353,7 @@ random_unnamed_bit_string(M, C) ->
random(Upper) ->
{A1,A2,A3} = erlang:now(),
- random:seed(A1,A2,A3),
+ _ = random:seed(A1, A2, A3),
random:uniform(Upper).
size_random(C) ->
diff --git a/lib/asn1/src/asn1rt.erl b/lib/asn1/src/asn1rt.erl
index d18f81346a..ad8b879c38 100644
--- a/lib/asn1/src/asn1rt.erl
+++ b/lib/asn1/src/asn1rt.erl
@@ -18,14 +18,13 @@
%%
%%
-module(asn1rt).
+-deprecated(module).
%% Runtime functions for ASN.1 (i.e encode, decode)
-export([encode/2,encode/3,decode/3,load_driver/0,unload_driver/0,info/1]).
-export([utf8_binary_to_list/1,utf8_list_to_binary/1]).
-
--deprecated([load_driver/0,unload_driver/0]).
encode(Module,{Type,Term}) ->
encode(Module,Type,Term).
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 3b34feb5a3..782217ed2d 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -813,10 +813,10 @@ testExport(Config) ->
testImport(Config) ->
test(Config, fun testImport/3).
testImport(Config, Rule, Opts) ->
- {error, _} = asn1ct:compile(filename:join(?config(data_dir, Config),
- "ImportsFrom"),
- [Rule, {outdir, ?config(priv_dir, Config)}
- |Opts]).
+ Files = ["ImportsFrom","ImportsFrom2","ImportsFrom3"],
+ asn1_test_lib:compile_all(Files, Config, [Rule|Opts]),
+ 42 = 'ImportsFrom':i(),
+ ok.
testMegaco(Config) -> test(Config, fun testMegaco/3).
testMegaco(Config, Rule, Opts) ->
@@ -860,7 +860,7 @@ duplicate_tags(Config) ->
rtUI(Config) -> test(Config, fun rtUI/3).
rtUI(Config, Rule, Opts) ->
asn1_test_lib:compile("Prim", Config, [Rule|Opts]),
- {ok, _} = asn1rt:info('Prim'),
+ _ = 'Prim':info(),
Rule = 'Prim':encoding_rule(),
io:format("Default BIT STRING format: ~p\n",
['Prim':bit_string_format()]).
diff --git a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
index 8dc5f3d7e1..74fa97e7aa 100644
--- a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
@@ -18,6 +18,8 @@ Ext1 ::= ENUMERATED {
magenta(9)
}
+SubExt1 ::= Ext1 ( blue | orange | black )
+
Noext ::= ENUMERATED {
blue(0),
red(1),
diff --git a/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1 b/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1
index 8c4f3a8f7e..b4ea943040 100644
--- a/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1
@@ -16,7 +16,9 @@ Names ::= SEQUENCE {
thirdName [2] INSTANCE OF OTHER-NAME ({TI})
}
-OTHER-NAME ::= TYPE-IDENTIFIER
+OTHER-NAME ::= YET-ANOTHER-NAME
+
+YET-ANOTHER-NAME ::= TYPE-IDENTIFIER
TI OTHER-NAME ::= {{INTEGER IDENTIFIED BY {2 4}} |
{Seq IDENTIFIED BY {2 3 4}} |
diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1
index 896a35d627..32b8f75dde 100644
--- a/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1
@@ -1,16 +1,8 @@
-ImportsFrom DEFINITIONS ::=
-
+ImportsFrom DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
-IMPORTS
-Type1, Type2, Type3
-FROM RemoteFile1 objid
-val1, val2, val3
-FROM RemoteFile2;
-
-objid OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) remote-operations(4) notation(0)}
-
-LocalType ::= INTEGER
+IMPORTS Int FROM ImportsFrom2;
+i Int ::= 42
END
diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1
new file mode 100644
index 0000000000..b0c29d24ae
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1
@@ -0,0 +1,7 @@
+ImportsFrom2 DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+IMPORTS Int FROM ImportsFrom3;
+
+LocalDef ::= OCTET STRING
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1
new file mode 100644
index 0000000000..ca27b20697
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1
@@ -0,0 +1,4 @@
+ImportsFrom3 DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+ Int ::= INTEGER (0..63)
+END
diff --git a/lib/asn1/test/asn1_appup_test.erl b/lib/asn1/test/asn1_appup_test.erl
index a2c1423eda..7391959645 100644
--- a/lib/asn1/test/asn1_appup_test.erl
+++ b/lib/asn1/test/asn1_appup_test.erl
@@ -21,8 +21,8 @@
%% Purpose: Verify the application specifics of the asn1 application
%%----------------------------------------------------------------------
-module(asn1_appup_test).
--compile({no_auto_import,[error/1]}).
-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -44,16 +44,9 @@ end_per_group(_GroupName, Config) ->
init_per_suite(suite) -> [];
init_per_suite(doc) -> [];
init_per_suite(Config) when is_list(Config) ->
- AppFile = file_name(asn1, ".app"),
- AppupFile = file_name(asn1, ".appup"),
- [{app_file, AppFile}, {appup_file, AppupFile}|Config].
+ Config.
-file_name(App, Ext) ->
- LibDir = code:lib_dir(App),
- filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-
-
end_per_suite(suite) -> [];
end_per_suite(doc) -> [];
end_per_suite(Config) when is_list(Config) ->
@@ -62,349 +55,7 @@ end_per_suite(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup(suite) ->
- [];
-appup(doc) ->
- "perform a simple check of the appup file";
+appup() ->
+ [{doc, "perform a simple check of the asn1 appup file"}].
appup(Config) when is_list(Config) ->
- AppupFile = key1search(appup_file, Config),
- AppFile = key1search(app_file, Config),
- Modules = modules(AppFile),
- check_appup(AppupFile, Modules).
-
-modules(File) ->
- case file:consult(File) of
- {ok, [{application,asn1,Info}]} ->
- case lists:keysearch(modules,1,Info) of
- {value, {modules, Modules}} ->
- Modules;
- false ->
- fail({bad_appinfo, Info})
- end;
- Error ->
- fail({bad_appfile, Error})
- end.
-
-
-check_appup(AppupFile, Modules) ->
- case file:consult(AppupFile) of
- {ok, [{V, UpFrom, DownTo}]} ->
- io:format("V= ~p, UpFrom= ~p, DownTo= ~p, Modules= ~p~n",
- [V, UpFrom, DownTo, Modules]),
- check_appup(V, UpFrom, DownTo, Modules);
- Else ->
- fail({bad_appupfile, Else})
- end.
-
-
-check_appup(V, UpFrom, DownTo, Modules) ->
- check_version(V),
- check_depends(up, UpFrom, Modules),
- check_depends(down, DownTo, Modules),
- ok.
-
-
-check_depends(_, [], _) ->
- ok;
-check_depends(UpDown, [Dep|Deps], Modules) ->
- check_depend(UpDown, Dep, Modules),
- check_depends(UpDown, Deps, Modules).
-
-
-check_depend(up,I={add_application,_App},Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [up,I , Modules]),
- ok;
-check_depend(down,I={remove_application,_App},Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [down,I , Modules]),
- ok;
-check_depend(UpDown, {V, Instructions}, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n V: ~p"
- "~n Modules: ~p", [UpDown, V, Modules]),
- check_version(V),
- case check_instructions(UpDown,
- Instructions, Instructions, [], [], Modules) of
- {_Good, []} ->
- ok;
- {_, Bad} ->
- fail({bad_instructions, Bad, UpDown})
- end.
-
-
-check_instructions(_, [], _, Good, Bad, _) ->
- {lists:reverse(Good), lists:reverse(Bad)};
-check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instr: ~p", [UpDown,Instr]),
- case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of
- ok ->
- check_instructions(UpDown, Instrs, AllInstr,
- [Instr|Good], Bad, Modules);
- {error, Reason} ->
- d("check_instructions(~w) -> bad instruction: "
- "~n Reason: ~p", [UpDown,Reason]),
- check_instructions(UpDown, Instrs, AllInstr, Good,
- [{Instr, Reason}|Bad], Modules)
- end.
-
-%% A new module is added
-check_instruction(up, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when up-add_module instruction with"
- "~n Module: ~p", [Module]),
- check_module(Module, Modules);
-
-%% An old module is re-added
-check_instruction(down, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when down-add_module instruction with"
- "~n Module: ~p", [Module]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- error({existing_readded_module, Module})
- end;
-
-%% Removing a module on upgrade:
-%% - the module has been removed from the app-file.
-%% - check that no module depends on this (removed) module
-check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post) ->
- d("check_instruction -> entry when up-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- check_purge(Pre),
- check_purge(Post);
- ok ->
- error({existing_removed_module, Module})
- end;
-
-%% Removing a module on downgrade: the module exist
-%% in the app-file.
-check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post) ->
- d("check_instruction -> entry when down-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- ok ->
- check_purge(Pre),
- check_purge(Post),
- check_no_remove_depends(Module, AllInstr);
- {error, {unknown_module, Module, Modules}} ->
- error({nonexisting_removed_module, Module})
- end;
-
-check_instruction(_, {load_module, Module, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- d("check_instruction -> entry when load_module instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {update, Module, Change, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- d("check_instruction -> entry when update instruction with"
- "~n Module: ~p"
- "~n Change: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Change, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_change(Change),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {apply, {Module, Function, Args}},
- _AllInstr, Modules)
- when is_atom(Module), is_atom(Function), is_list(Args) ->
- d("check_instruction -> entry when apply instruction with"
- "~n Module: ~p"
- "~n Function: ~p"
- "~n Args: ~p", [Module, Function, Args]),
- check_module(Module, Modules),
- check_apply(Module,Function,Args);
-
-check_instruction(_, Instr, _AllInstr, _Modules) ->
- d("check_instruction -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- error({error, {unknown_instruction, Instr}}).
-
-
-%% If Module X depends on Module Y, then module Y must have an update
-%% instruction of some sort (otherwise the depend is faulty).
-updated_modules([], Modules) ->
- d("update_modules -> entry when done with"
- "~n Modules: ~p", [Modules]),
- Modules;
-updated_modules([Instr|Instrs], Modules) ->
- d("update_modules -> entry with"
- "~n Instr: ~p"
- "~n Modules: ~p", [Instr,Modules]),
- Module = instruction_module(Instr),
- d("update_modules -> Module: ~p", [Module]),
- updated_modules(Instrs, [Module|Modules]).
-
-instruction_module({add_module, Module}) ->
- Module;
-instruction_module({remove, {Module, _, _}}) ->
- Module;
-instruction_module({load_module, Module, _, _, _}) ->
- Module;
-instruction_module({update, Module, _, _, _, _}) ->
- Module;
-instruction_module({apply, {Module, _, _}}) ->
- Module;
-instruction_module(Instr) ->
- d("instruction_module -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- error({error, {unknown_instruction, Instr}}).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-check_version(V) when is_list(V) ->
- ok;
-check_version(V) ->
- error({bad_version, V}).
-
-
-check_module(M, Modules) when is_atom(M) ->
- case lists:member(M,Modules) of
- true ->
- ok;
- false ->
- error({unknown_module, M, Modules})
- end;
-check_module(M, _) ->
- error({bad_module, M}).
-
-check_apply(Module,Function,Args) ->
- case (catch Module:module_info()) of
- Info when is_list(Info) ->
- check_exported(Function,Args,Info);
- {'EXIT',{undef,_}} ->
- error({not_existing_module,Module})
- end.
-
-check_exported(Function,Args,Info) ->
- case lists:keysearch(exports,1,Info) of
- {value,{exports,FunList}} ->
- case lists:keysearch(Function,1,FunList) of
- {value,{_,Arity}} when Arity==length(Args) ->
- ok;
- _ ->
- error({not_exported_function,Function,length(Args)})
- end;
- _ ->
- error({bad_export,Info})
- end.
-
-check_module_depend(M, [], _) when is_atom(M) ->
- d("check_module_depend -> entry with"
- "~n M: ~p", [M]),
- ok;
-check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) ->
- d("check_module_depend -> entry with"
- "~n M: ~p"
- "~n Deps: ~p"
- "~n Modules: ~p", [M, Deps, Modules]),
- case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of
- [] ->
- ok;
- Unknown ->
- error({unknown_depend_modules, Unknown})
- end;
-check_module_depend(_M, D, _Modules) ->
- d("check_module_depend -> entry when bad depend with"
- "~n D: ~p", [D]),
- error({bad_depend, D}).
-
-
-check_no_remove_depends(_Module, []) ->
- ok;
-check_no_remove_depends(Module, [Instr|Instrs]) ->
- check_no_remove_depend(Module, Instr),
- check_no_remove_depends(Module, Instrs).
-
-check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, load_module, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, update, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(_, _) ->
- ok.
-
-
-check_change(soft) ->
- ok;
-check_change({advanced, _Something}) ->
- ok;
-check_change(Change) ->
- error({bad_change, Change}).
-
-
-check_purge(soft_purge) ->
- ok;
-check_purge(brutal_purge) ->
- ok;
-check_purge(Purge) ->
- error({bad_purge, Purge}).
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-error(Reason) ->
- throw({error, Reason}).
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-d(F, A) ->
- d(false, F, A).
-
-d(true, F, A) ->
- io:format(F ++ "~n", A);
-d(_, _, _) ->
- ok.
-
-
+ ok = ?t:appup_test(asn1).
diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl
index 6451f81c01..8a0414708d 100644
--- a/lib/asn1/test/error_SUITE.erl
+++ b/lib/asn1/test/error_SUITE.erl
@@ -19,7 +19,8 @@
-module(error_SUITE).
-export([suite/0,all/0,groups/0,
- already_defined/1,enumerated/1,objects/1]).
+ already_defined/1,bitstrings/1,enumerated/1,
+ imports/1,instance_of/1,integers/1,objects/1,values/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -29,9 +30,15 @@ all() ->
[{group,p}].
groups() ->
- [{p,parallel(),[already_defined,
- enumerated,
- objects]}].
+ [{p,parallel(),
+ [already_defined,
+ bitstrings,
+ enumerated,
+ imports,
+ instance_of,
+ integers,
+ objects,
+ values]}].
parallel() ->
case erlang:system_info(schedulers) > 1 of
@@ -68,6 +75,23 @@ already_defined(Config) ->
} = run(P, Config),
ok.
+bitstrings(Config) ->
+ M = 'Bitstrings',
+ P = {M,
+ <<"Bitstrings DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ " Bs1 ::= BIT STRING {a(1), a(1)}\n"
+ " Bs2 ::= BIT STRING {a(1), b(2), a(3)}\n"
+ " Bs3 ::= BIT STRING {x(1), y(1)}\n"
+ " Bs4 ::= BIT STRING {x(-1), y(0)}\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{M,2},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,3},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,4},asn1ct_check,{value_reused,1}},
+ {structured_error,{M,5},asn1ct_check,{invalid_bit_number,-1}}
+ ]} = run(P, Config),
+ ok.
+
enumerated(Config) ->
M = 'Enumerated',
P = {M,
@@ -96,6 +120,63 @@ enumerated(Config) ->
} = run(P, Config),
ok.
+imports(Config) ->
+ Ext = 'ExternalModule',
+ ExtP = {Ext,
+ <<"ExternalModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ "END\n">>},
+ ok = run(ExtP, Config),
+
+ M = 'Imports',
+ P = {M,
+ <<"Imports DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ "IMPORTS NotDefined FROM ExternalModule\n"
+ "X FROM UndefinedModule objid\n"
+ "Y, Z FROM UndefinedModule2;\n"
+ "objid OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) remote-operations(4)\n"
+ " notation(0)}\n"
+ "END\n">>},
+ {error,[{structured_error,{M,2},asn1ct_check,
+ {undefined_import,'NotDefined','ExternalModule'}},
+ {structured_error,{M,3},asn1ct_check,{undefined_import,'X','UndefinedModule'}},
+ {structured_error,{M,4},asn1ct_check,{undefined_import,'Y','UndefinedModule2'}},
+ {structured_error,{M,4},asn1ct_check,{undefined_import,'Z','UndefinedModule2'}}
+ ]} = run(P, Config),
+ ok.
+
+instance_of(Config) ->
+ M = 'InstanceOf',
+ P = {M,
+ <<"InstanceOf DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ "XX ::= INSTANCE OF CL ({TI})\n"
+ "CL ::= CLASS {\n"
+ "&id INTEGER,\n"
+ "&Type\n"
+ "}\n"
+ "o1 CL ::= {&id 1, &Type OCTET STRING}\n"
+ "TI CL ::= { o1 }\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{M,2},asn1ct_check,{illegal_instance_of,'CL'}}
+ ]} = run(P, Config),
+ ok.
+
+integers(Config) ->
+ M = 'Integers',
+ P = {M,
+ <<"Integers DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ " Int1 ::= INTEGER {a(1), a(1)}\n"
+ " Int2 ::= INTEGER {a(1), b(2), a(3)}\n"
+ " Int3 ::= INTEGER {x(1), y(1)}\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{M,2},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,3},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,4},asn1ct_check,{value_reused,1}}
+ ]} = run(P, Config),
+ ok.
+
+
objects(Config) ->
M = 'Objects',
P = {M,
@@ -138,6 +219,25 @@ objects(Config) ->
} = run(P, Config),
ok.
+values(Config) ->
+ M = 'Values',
+ P = {M,
+ <<"Values DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ " os1 OCTET STRING ::= \"abc\"\n"
+ " os2 OCTET STRING ::= 42\n"
+ " os3 OCTET STRING ::= { 1, 3 }\n"
+ "END\n">>},
+ {error,
+ [
+ {structured_error,{M,2},asn1ct_check,
+ illegal_octet_string_value},
+ {structured_error,{M,3},asn1ct_check,
+ illegal_octet_string_value},
+ {structured_error,{M,4},asn1ct_check,
+ illegal_octet_string_value}
+ ]
+ } = run(P, Config),
+ ok.
run({Mod,Spec}, Config) ->
diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl
index c66adaf949..878518be11 100644
--- a/lib/asn1/test/testEnumExt.erl
+++ b/lib/asn1/test/testEnumExt.erl
@@ -59,6 +59,10 @@ main(ber) ->
common(ber).
common(Erule) ->
+ roundtrip('SubExt1', blue),
+ roundtrip('SubExt1', orange),
+ roundtrip('SubExt1', black),
+
roundtrip('Seq', {'Seq',blue,42}),
roundtrip('Seq', {'Seq',red,42}),
roundtrip('Seq', {'Seq',green,42}),
diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl
index 3262d61a4d..155d6f6ff5 100644
--- a/lib/asn1/test/testPrimStrings.erl
+++ b/lib/asn1/test/testPrimStrings.erl
@@ -18,6 +18,8 @@
%%
%%
-module(testPrimStrings).
+-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}},
+ {nowarn_deprecated_function,{asn1rt,utf8_binary_to_list,1}}]).
-export([bit_string/2]).
-export([octet_string/1]).
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 153c64ebdd..1f16f31f6b 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,2 +1,2 @@
#next version number to use is 2.0
-ASN1_VSN = 2.0.4
+ASN1_VSN = 3.0
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index a4a77ee400..ef21948f89 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -589,8 +589,8 @@
Common Test will either execute one test run per specification file, or
join the files and perform all tests within one single test run. The first
behaviour is the default one. The latter requires that the start
- flag/option <c>join_suites</c> is provided, e.g.
- <c>run_test -spec ./my_tests1.ts ./my_tests2.ts -join_suites</c>.</p>
+ flag/option <c>join_specs</c> is provided, e.g.
+ <c>run_test -spec ./my_tests1.ts ./my_tests2.ts -join_specs</c>.</p>
<p>Joining a number of specifications, or running them separately, can
also be accomplished with (and may be combined with) test specification
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 18c1dec784..e28751fb59 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -62,5 +62,10 @@
ct_master,
ct_master_logs]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies,["xmerl-1.3.7","webtool-0.8.10","tools-2.6.14",
+ "test_server-3.7","stdlib-2.0","ssh-3.0.1",
+ "snmp-4.25.1","sasl-2.4","runtime_tools-1.8.14",
+ "kernel-3.0","inets-5.10","erts-6.0",
+ "debugger-4.0","crypto-3.3","compiler-5.0"]}]}.
diff --git a/lib/common_test/src/common_test.appup.src b/lib/common_test/src/common_test.appup.src
index 0fbe5f23f7..4dfd9f1b0d 100644
--- a/lib/common_test/src/common_test.appup.src
+++ b/lib/common_test/src/common_test.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}. \ No newline at end of file
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, common_test}]}],
+ [{<<".*">>,[{restart_application, common_test}]}]
+}.
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
index d733df27dc..cff02a46d9 100644
--- a/lib/common_test/src/ct_conn_log_h.erl
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -204,13 +204,8 @@ pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) ->
micro2milli(MicroS)]).
pretty_title(#conn_log{client=Client}=Info) ->
- case actionstr(Info) of
- {no_server,Action} ->
- io_lib:format("= Client ~w ~s ",[Client,Action]);
- Action ->
- io_lib:format("= Client ~w ~s ~ts ",[Client,Action,
- serverstr(Info)])
- end.
+ io_lib:format("= Client ~w ~s ~ts ",
+ [Client,actionstr(Info),serverstr(Info)]).
actionstr(#conn_log{action=send}) -> "----->";
actionstr(#conn_log{action=cmd}) -> "----->";
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 54510a657a..7d577462b0 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -249,8 +249,8 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
end
end.
-ct_suite_init(Suite, Func, PostInitHook, Config) when is_list(Config) ->
- case ct_hooks:init_tc(Suite, Func, Config) of
+ct_suite_init(Suite, FuncSpec, PostInitHook, Config) when is_list(Config) ->
+ case ct_hooks:init_tc(Suite, FuncSpec, Config) of
NewConfig when is_list(NewConfig) ->
PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig),
{ok, [PostInitHookResult ++ NewConfig]};
@@ -660,10 +660,7 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
ct_util:delete_testdata(comment),
ct_util:delete_suite_data(last_saved_config),
- FuncSpec = case group_or_func(Func,Args) of
- {_,_GroupName,_} = Group -> Group;
- _ -> Func
- end,
+ FuncSpec = group_or_func(Func,Args),
{Result1,FinalNotify} =
case ct_hooks:end_tc(
@@ -1277,28 +1274,35 @@ report(What,Data) ->
ct_util:set_testdata({What,Data}),
ok;
tc_start ->
- %% Data = {{Suite,Func},LogFileName}
+ %% Data = {Suite,{Func,GroupName}},LogFileName}
+ Data1 = case Data of
+ {{Suite,{Func,undefined}},LFN} -> {{Suite,Func},LFN};
+ _ -> Data
+ end,
ct_event:sync_notify(#event{name=tc_logfile,
node=node(),
- data=Data}),
+ data=Data1}),
ok;
tc_done ->
- {_Suite,Case,Result} = Data,
+ {Suite,{Func,GrName},Result} = Data,
+ Data1 = if GrName == undefined -> {Suite,Func,Result};
+ true -> Data
+ end,
case Result of
{failed, _} ->
- ct_hooks:on_tc_fail(What, Data);
+ ct_hooks:on_tc_fail(What, Data1);
{skipped,{failed,{_,init_per_testcase,_}}} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ ct_hooks:on_tc_skip(tc_auto_skip, Data1);
{skipped,{require_failed,_}} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ ct_hooks:on_tc_skip(tc_auto_skip, Data1);
{skipped,_} ->
- ct_hooks:on_tc_skip(tc_user_skip, Data);
+ ct_hooks:on_tc_skip(tc_user_skip, Data1);
{auto_skipped,_} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ ct_hooks:on_tc_skip(tc_auto_skip, Data1);
_Else ->
ok
end,
- case {Case,Result} of
+ case {Func,Result} of
{init_per_suite,_} ->
ok;
{end_per_suite,_} ->
@@ -1327,20 +1331,17 @@ report(What,Data) ->
tc_user_skip ->
%% test case or config function specified as skipped in testspec,
%% or init config func for suite/group has returned {skip,Reason}
- %% Data = {Suite,Case,Comment} |
- %% {Suite,{GroupConfigFunc,GroupName},Comment}
+ %% Data = {Suite,{Func,GroupName},Comment}
{Func,Data1} = case Data of
- {Suite,{ConfigFunc,undefined},Cmt} ->
- {ConfigFunc,{Suite,ConfigFunc,Cmt}};
- {_,{ConfigFunc,_},_} -> {ConfigFunc,Data};
- {_,Case,_} -> {Case,Data}
+ {Suite,{F,undefined},Comment} ->
+ {F,{Suite,F,Comment}};
+ D = {_,{F,_},_} ->
+ {F,D}
end,
-
ct_event:sync_notify(#event{name=tc_user_skip,
node=node(),
data=Data1}),
ct_hooks:on_tc_skip(What, Data1),
-
if Func /= init_per_suite, Func /= init_per_group,
Func /= end_per_suite, Func /= end_per_group ->
add_to_stats(user_skipped);
@@ -1350,13 +1351,12 @@ report(What,Data) ->
tc_auto_skip ->
%% test case skipped because of error in config function, or
%% config function skipped because of error in info function
- %% Data = {Suite,Case,Comment} |
- %% {Suite,{GroupConfigFunc,GroupName},Comment}
+ %% Data = {Suite,{Func,GroupName},Comment}
{Func,Data1} = case Data of
- {Suite,{ConfigFunc,undefined},Cmt} ->
- {ConfigFunc,{Suite,ConfigFunc,Cmt}};
- {_,{ConfigFunc,_},_} -> {ConfigFunc,Data};
- {_,Case,_} -> {Case,Data}
+ {Suite,{F,undefined},Comment} ->
+ {F,{Suite,F,Comment}};
+ D = {_,{F,_},_} ->
+ {F,D}
end,
%% this test case does not have a log, so printouts
%% from event handlers should end up in the main log
@@ -1364,7 +1364,6 @@ report(What,Data) ->
node=node(),
data=Data1}),
ct_hooks:on_tc_skip(What, Data1),
-
if Func /= end_per_suite,
Func /= end_per_group ->
add_to_stats(auto_skipped);
@@ -1417,7 +1416,7 @@ warn(_What) ->
true.
%%%-----------------------------------------------------------------
-%%% @spec add_data_dir(File0) -> File1
+%%% @spec add_data_dir(File0, Config) -> File1
add_data_dir(File,Config) when is_atom(File) ->
add_data_dir(atom_to_list(File),Config);
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 078d6b1a44..56082086f6 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -307,7 +307,8 @@ call(Pid, Msg, Timeout) ->
end.
return({To,Ref},Result) ->
- To ! {Ref, Result}.
+ To ! {Ref, Result},
+ ok.
init_gen(Parent,Opts) ->
process_flag(trap_exit,true),
@@ -344,7 +345,7 @@ loop(Opts) ->
link(NewPid),
put(conn_pid,NewPid),
loop(Opts#gen_opts{conn_pid=NewPid,
- cb_state=NewState});
+ cb_state=NewState});
Error ->
ct_util:unregister_connection(self()),
log("Reconnect failed. Giving up!",
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index e845e9e908..df4c98d9d1 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -64,11 +64,16 @@ terminate(Hooks) ->
%% @doc Called as each test case is started. This includes all configuration
%% tests.
--spec init_tc(Mod :: atom(), Func :: atom(), Args :: list()) ->
+-spec init_tc(Mod :: atom(),
+ FuncSpec :: atom() |
+ {ConfigFunc :: init_per_group | end_per_group,
+ GroupName :: atom(),
+ Properties :: list()},
+ Args :: list()) ->
NewConfig :: proplists:proplist() |
- {skip, Reason :: term()} |
- {auto_skip, Reason :: term()} |
- {fail, Reason :: term()}.
+ {skip, Reason :: term()} |
+ {auto_skip, Reason :: term()} |
+ {fail, Reason :: term()}.
init_tc(Mod, init_per_suite, Config) ->
Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of
@@ -82,8 +87,8 @@ init_tc(Mod, init_per_suite, Config) ->
call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]);
init_tc(Mod, end_per_suite, Config) ->
call(fun call_generic/3, Config, [pre_end_per_suite, Mod]);
-init_tc(Mod, {init_per_group, GroupName, Opts}, Config) ->
- maybe_start_locker(Mod, GroupName, Opts),
+init_tc(Mod, {init_per_group, GroupName, Properties}, Config) ->
+ maybe_start_locker(Mod, GroupName, Properties),
call(fun call_generic/3, Config, [pre_init_per_group, GroupName]);
init_tc(_Mod, {end_per_group, GroupName, _}, Config) ->
call(fun call_generic/3, Config, [pre_end_per_group, GroupName]);
@@ -93,15 +98,18 @@ init_tc(_Mod, TC, Config) ->
%% @doc Called as each test case is completed. This includes all configuration
%% tests.
-spec end_tc(Mod :: atom(),
- Func :: atom(),
+ FuncSpec :: atom() |
+ {ConfigFunc :: init_per_group | end_per_group,
+ GroupName :: atom(),
+ Properties :: list()},
Args :: list(),
Result :: term(),
- Resturn :: term()) ->
+ Return :: term()) ->
NewConfig :: proplists:proplist() |
- {skip, Reason :: term()} |
- {auto_skip, Reason :: term()} |
- {fail, Reason :: term()} |
- ok | '$ct_no_change'.
+ {skip, Reason :: term()} |
+ {auto_skip, Reason :: term()} |
+ {fail, Reason :: term()} |
+ ok | '$ct_no_change'.
end_tc(Mod, init_per_suite, Config, _Result, Return) ->
call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config],
@@ -112,18 +120,20 @@ end_tc(Mod, end_per_suite, Config, Result, _Return) ->
end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) ->
call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config],
'$ct_no_change');
-end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) ->
+end_tc(Mod, {end_per_group, GroupName, Properties}, Config, Result, _Return) ->
Res = call(fun call_generic/3, Result,
[post_end_per_group, GroupName, Config], '$ct_no_change'),
- maybe_stop_locker(Mod, GroupName,Opts),
+ maybe_stop_locker(Mod, GroupName, Properties),
Res;
end_tc(_Mod, TC, Config, Result, _Return) ->
call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config],
'$ct_no_change').
+%% Case = TestCase | {TestCase,GroupName}
on_tc_skip(How, {Suite, Case, Reason}) ->
call(fun call_cleanup/3, {How, Reason}, [on_tc_skip, Suite, Case]).
+%% Case = TestCase | {TestCase,GroupName}
on_tc_fail(_How, {Suite, Case, Reason}) ->
call(fun call_cleanup/3, Reason, [on_tc_fail, Suite, Case]).
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index a7fb45a4e4..a4ad65c0a4 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -76,7 +76,7 @@
tests = []}).
%%%-----------------------------------------------------------------
-%%% @spec init(Mode) -> Result
+%%% @spec init(Mode, Verbosity) -> Result
%%% Mode = normal | interactive
%%% Result = {StartTime,LogDir}
%%% StartTime = term()
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 35920ec1dc..6fc840745d 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -1,7 +1,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2014. 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
@@ -1334,7 +1334,7 @@ handle_data(NewData,#state{connection=Connection,buff=Buff} = State) ->
%% first answer
P=#pending{tref=TRef,caller=Caller} =
lists:last(Pending),
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
Reason1 = {failed_to_parse_received_data,Reason},
ct_gen_conn:return(Caller,{error,Reason1}),
lists:delete(P,Pending)
@@ -1454,7 +1454,7 @@ decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
{noreply,State#state{hello_status = {error,Reason}}}
end;
#pending{tref=TRef,caller=Caller} ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
case decode_hello(E) of
{ok,SessionId,Capabilities} ->
ct_gen_conn:return(Caller,ok),
@@ -1482,7 +1482,7 @@ decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
[#pending{tref=TRef,
caller=Caller}] ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
ct_gen_conn:return(Caller,E),
{noreply,State#state{pending=[]}};
_ ->
@@ -1504,7 +1504,7 @@ get_msg_id(Attrs) ->
decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
case lists:keytake(MsgId,#pending.msg_id,Pending) of
{value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
Content = forward_xmlns_attr(Attrs,Content0),
{CallerReply,{ServerReply,State2}} =
do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}),
@@ -1519,7 +1519,7 @@ decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
msg_id=undefined,
op=undefined,
caller=Caller}] ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
ct_gen_conn:return(Caller,E),
{noreply,State#state{pending=[]}};
_ ->
@@ -1862,10 +1862,7 @@ ssh_open(#options{host=Host,timeout=Timeout,port=Port,ssh=SshOpts,name=Name}) ->
end;
{error, Reason} ->
ssh:close(CM),
- {error,{ssh,could_not_open_channel,Reason}};
- Other ->
- %% Bug in ssh?? got {closed,0} here once...
- {error,{ssh,unexpected_from_session_channel,Other}}
+ {error,{ssh,could_not_open_channel,Reason}}
end;
{error,Reason} ->
{error,{ssh,could_not_connect_to_server,Reason}}
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 7c797be03e..03cf06abed 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -71,6 +71,7 @@
enable_builtin_hooks,
include = [],
auto_compile,
+ abort_if_missing_suites,
silent_connections = [],
stylesheet,
multiply_timetraps = 1,
@@ -246,9 +247,11 @@ script_start1(Parent, Args) ->
Vts = get_start_opt(vts, true, Args),
Shell = get_start_opt(shell, true, Args),
Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args),
- CoverStop = get_start_opt(cover_stop, fun([CS]) -> list_to_atom(CS) end, Args),
+ CoverStop = get_start_opt(cover_stop,
+ fun([CS]) -> list_to_atom(CS) end, Args),
LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args),
- LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end,
+ LogOpts = get_start_opt(logopts,
+ fun(Os) -> [list_to_atom(O) || O <- Os] end,
[], Args),
Verbosity = verbosity_args2opts(Args),
MultTT = get_start_opt(multiply_timetraps,
@@ -311,6 +314,12 @@ script_start1(Parent, Args) ->
application:set_env(common_test, auto_compile, false),
{false,[]}
end,
+
+ %% abort test run if some suites can't be compiled
+ AbortIfMissing = get_start_opt(abort_if_missing_suites,
+ fun([]) -> true;
+ ([Bool]) -> list_to_atom(Bool)
+ end, false, Args),
%% silent connections
SilentConns =
get_start_opt(silent_connections,
@@ -347,6 +356,7 @@ script_start1(Parent, Args) ->
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = IncludeDirs,
silent_connections = SilentConns,
stylesheet = Stylesheet,
@@ -551,6 +561,9 @@ combine_test_opts(TS, Specs, Opts) ->
ACBool
end,
+ AbortIfMissing = choose_val(Opts#opts.abort_if_missing_suites,
+ TSOpts#opts.abort_if_missing_suites),
+
BasicHtml =
case choose_val(Opts#opts.basic_html,
TSOpts#opts.basic_html) of
@@ -578,6 +591,7 @@ combine_test_opts(TS, Specs, Opts) ->
enable_builtin_hooks = EnableBuiltinHooks,
stylesheet = Stylesheet,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = AllInclude,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
@@ -753,6 +767,7 @@ script_usage() ->
"\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-abort_if_missing_suites]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
@@ -775,6 +790,7 @@ script_usage() ->
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-abort_if_missing_suites]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
@@ -799,6 +815,7 @@ script_usage() ->
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-abort_if_missing_suites]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
@@ -1026,6 +1043,10 @@ run_test2(StartOpts) ->
{ACBool,[]}
end,
+ %% abort test run if some suites can't be compiled
+ AbortIfMissing = get_start_opt(abort_if_missing_suites, value, false,
+ StartOpts),
+
%% decrypt config file
case proplists:get_value(decrypt, StartOpts) of
undefined ->
@@ -1067,6 +1088,7 @@ run_test2(StartOpts) ->
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = Include,
silent_connections = SilentConns,
stylesheet = Stylesheet,
@@ -1401,6 +1423,7 @@ get_data_for_node(#testspec{label = Labels,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = ACs,
+ abort_if_missing_suites = AiMSs,
include = Incl,
multiply_timetraps = MTs,
scale_timetraps = STs,
@@ -1435,6 +1458,7 @@ get_data_for_node(#testspec{label = Labels,
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node],
AutoCompile = proplists:get_value(Node, ACs),
+ AbortIfMissing = proplists:get_value(Node, AiMSs),
Include = [I || {N,I} <- Incl, N==Node],
#opts{label = Label,
profile = Profile,
@@ -1451,6 +1475,7 @@ get_data_for_node(#testspec{label = Labels,
ct_hooks = FiltCTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = Include,
multiply_timetraps = MT,
scale_timetraps = ST,
@@ -1722,8 +1747,8 @@ compile_and_run(Tests, Skip, Opts, Args) ->
{SuiteErrs,HelpErrs} = auto_compile(TestSuites),
{TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
end,
-
- case continue(AllMakeErrors) of
+
+ case continue(AllMakeErrors, Opts#opts.abort_if_missing_suites) of
true ->
SavedErrors = save_make_errors(SuiteMakeErrors),
ct_repeat:log_loop_info(Args),
@@ -2047,9 +2072,9 @@ final_skip([Skip|Skips], Final) ->
final_skip([], Final) ->
lists:reverse(Final).
-continue([]) ->
+continue([], _) ->
true;
-continue(_MakeErrors) ->
+continue(_MakeErrors, AbortIfMissingSuites) ->
io:nl(),
OldGl = group_leader(),
case set_group_leader_same_as_shell() of
@@ -2077,26 +2102,26 @@ continue(_MakeErrors) ->
true
end;
false -> % no shell process to use
- true
+ not AbortIfMissingSuites
end.
set_group_leader_same_as_shell() ->
%%! Locate the shell process... UGLY!!!
GS2or3 = fun(P) ->
- case process_info(P,initial_call) of
- {initial_call,{group,server,X}} when X == 2 ; X == 3 ->
- true;
- _ ->
- false
- end
- end,
+ case process_info(P,initial_call) of
+ {initial_call,{group,server,X}} when X == 2 ; X == 3 ->
+ true;
+ _ ->
+ false
+ end
+ end,
case [P || P <- processes(), GS2or3(P),
- true == lists:keymember(shell,1,
- element(2,process_info(P,dictionary)))] of
- [GL|_] ->
- group_leader(GL, self());
- [] ->
- false
+ true == lists:keymember(shell,1,
+ element(2,process_info(P,dictionary)))] of
+ [GL|_] ->
+ group_leader(GL, self());
+ [] ->
+ false
end.
check_and_add([{TestDir0,M,_} | Tests], Added, PA) ->
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index b4d82a53cf..bc22a99741 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -281,8 +281,16 @@ open(KeyOrName,ConnType,TargetMod,Extra) ->
end,
log(undefined,open,"Connecting to ~p(~p)",
[KeyOrName,Addr1]),
- ct_gen_conn:start(KeyOrName,full_addr(Addr1,ConnType),
- {TargetMod,KeepAlive,Extra},?MODULE)
+ Reconnect =
+ case ct:get_config({telnet_settings,reconnection_attempts}) of
+ 0 -> false;
+ _ -> true
+ end,
+ ct_gen_conn:start(full_addr(Addr1,ConnType),
+ {TargetMod,KeepAlive,Extra},
+ ?MODULE, [{name,KeyOrName},
+ {reconnect,Reconnect},
+ {old,true}])
end.
%%%-----------------------------------------------------------------
@@ -300,7 +308,7 @@ close(Connection) ->
{ok,Pid} ->
log(undefined,close,"Connection closed, handle: ~w",[Pid]),
case ct_gen_conn:stop(Pid) of
- {error,{process_down,Pid,noproc}} ->
+ {error,{process_down,Pid,_}} ->
{error,already_closed};
Result ->
Result
@@ -405,9 +413,11 @@ expect(Connection,Patterns) ->
%%% Prompt = string()
%%% Tag = term()
%%% Opts = [Opt]
-%%% Opt = {timeout,Timeout} | repeat | {repeat,N} | sequence |
-%%% {halt,HaltPatterns} | ignore_prompt | no_prompt_check
-%%% Timeout = integer()
+%%% Opt = {idle_timeout,IdleTimeout} | {total_timeout,TotalTimeout} |
+%%% repeat | {repeat,N} | sequence | {halt,HaltPatterns} |
+%%% ignore_prompt | no_prompt_check
+%%% IdleTimeout = infinity | integer()
+%%% TotalTimeout = infinity | integer()
%%% N = integer()
%%% HaltPatterns = Patterns
%%% MatchList = [Match]
@@ -433,11 +443,16 @@ expect(Connection,Patterns) ->
%%% will also include the matched <code>Tag</code>. Else, only
%%% <code>RxMatch</code> is returned.</p>
%%%
-%%% <p>The <code>timeout</code> option indicates that the function
+%%% <p>The <code>idle_timeout</code> option indicates that the function
%%% shall return if the telnet client is idle (i.e. if no data is
-%%% received) for more than <code>Timeout</code> milliseconds. Default
+%%% received) for more than <code>IdleTimeout</code> milliseconds. Default
%%% timeout is 10 seconds.</p>
%%%
+%%% <p>The <code>total_timeout</code> option sets a time limit for
+%%% the complete expect operation. After <code>TotalTimeout</code>
+%%% milliseconds, <code>{error,timeout}</code> is returned. The default
+%%% value is <code>infinity</code> (i.e. no time limit).</p>
+%%%
%%% <p>The function will always return when a prompt is found, unless
%%% any of the <code>ignore_prompt</code> or
%%% <code>no_prompt_check</code> options are used, in which case it
@@ -570,14 +585,14 @@ handle_msg({cmd,Cmd,Timeout},State) ->
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,2000}]);
+ [{idle_timeout,2000}]);
{ip,false} ->
silent_teln_expect(State#state.name,
State#state.teln_pid,
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,200}]);
+ [{idle_timeout,200}]);
{ip,true} ->
ok
end,
@@ -601,11 +616,9 @@ handle_msg({cmd,Cmd,Timeout},State) ->
end_gen_log(),
{Return,State#state{buffer=NewBuffer,prompt=Prompt}};
handle_msg({send,Cmd},State) ->
- log(State,send,"Cmd: ~p",[Cmd]),
-
+ log(State,send,"Sending: ~p",[Cmd]),
debug_cont_gen_log("Throwing Buffer:",[]),
debug_log_lines(State#state.buffer),
-
case {State#state.type,State#state.prompt} of
{ts,_} ->
silent_teln_expect(State#state.name,
@@ -613,14 +626,14 @@ handle_msg({send,Cmd},State) ->
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,2000}]);
+ [{idle_timeout,2000}]);
{ip,false} ->
silent_teln_expect(State#state.name,
State#state.teln_pid,
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,200}]);
+ [{idle_timeout,200}]);
{ip,true} ->
ok
end,
@@ -783,66 +796,61 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
true -> Name
end,
Silent = get(silent),
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- Result when Result /= undefined, Result /= silent, Silent /= true ->
- {PrintHeader,PreBR} = if Action==undefined ->
- {false,""};
- true ->
- {true,"\n"}
- end,
- error_logger:info_report(#conn_log{header=PrintHeader,
- client=self(),
- conn_pid=TelnPid,
- address={Host,Port},
- name=Name1,
- action=Action,
- module=?MODULE},
- {PreBR++String,Args});
- Result when Result /= undefined ->
- ok;
- _ when Action == open; Action == close; Action == reconnect;
- Action == info; Action == error ->
- ct_gen_conn:log(heading(Action,Name1),String,Args);
- _ when ForcePrint == false ->
- case ct_util:is_silenced(telnet) of
- true ->
- ok;
- false ->
- ct_gen_conn:cont_log(String,Args)
+
+ if Action == general_io ->
+ case ct_util:get_testdata({cth_conn_log,?MODULE}) of
+ HookMode when HookMode /= undefined, HookMode /= silent,
+ Silent /= true ->
+ error_logger:info_report(#conn_log{header=false,
+ client=self(),
+ conn_pid=TelnPid,
+ address={Host,Port},
+ name=Name1,
+ action=Action,
+ module=?MODULE},
+ {String,Args});
+ _ -> %% hook inactive or silence requested
+ ok
end;
- _ when ForcePrint == true ->
- case ct_util:is_silenced(telnet) of
- true ->
- %% call log/3 now instead of cont_log/2 since
- %% start_gen_log/1 will not have been previously called
+
+ true ->
+ if Action == open; Action == close; Action == reconnect;
+ Action == info; Action == error ->
ct_gen_conn:log(heading(Action,Name1),String,Args);
- false ->
- ct_gen_conn:cont_log(String,Args)
+
+ ForcePrint == false ->
+ case ct_util:is_silenced(telnet) of
+ true ->
+ ok;
+ false ->
+ ct_gen_conn:cont_log(String,Args)
+ end;
+
+ ForcePrint == true ->
+ case ct_util:is_silenced(telnet) of
+ true ->
+ %% call log/3 now instead of cont_log/2 since
+ %% start_gen_log/1 will not have been previously
+ %% called
+ ct_gen_conn:log(heading(Action,Name1),String,Args);
+ false ->
+ ct_gen_conn:cont_log(String,Args)
+ end
end
end.
start_gen_log(Heading) ->
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- undefined ->
- %% check if output is suppressed
- case ct_util:is_silenced(telnet) of
- true -> ok;
- false -> ct_gen_conn:start_log(Heading)
- end;
- _ ->
- ok
+ %% check if output is suppressed
+ case ct_util:is_silenced(telnet) of
+ true -> ok;
+ false -> ct_gen_conn:start_log(Heading)
end.
end_gen_log() ->
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- undefined ->
- %% check if output is suppressed
- case ct_util:is_silenced(telnet) of
- true -> ok;
- false -> ct_gen_conn:end_log()
- end;
- _ ->
- ok
+ %% check if output is suppressed
+ case ct_util:is_silenced(telnet) of
+ true -> ok;
+ false -> ct_gen_conn:end_log()
end.
%%% @hidden
@@ -879,7 +887,8 @@ teln_get_all_data(Pid,Prx,Data,Acc,LastLine) ->
%% Expect options record
-record(eo,{teln_pid,
prx,
- timeout,
+ idle_timeout,
+ total_timeout,
haltpatterns=[],
seq=false,
repeat=false,
@@ -921,11 +930,12 @@ teln_expect(Name,Pid,Data,Pattern0,Prx,Opts) ->
Seq = get_seq(Opts),
Pattern = convert_pattern(Pattern0,Seq),
- Timeout = get_timeout(Opts),
+ {IdleTimeout,TotalTimeout} = get_timeouts(Opts),
EO = #eo{teln_pid=Pid,
prx=Prx,
- timeout=Timeout,
+ idle_timeout=IdleTimeout,
+ total_timeout=TotalTimeout,
seq=Seq,
haltpatterns=HaltPatterns,
prompt_check=PromptCheck},
@@ -964,11 +974,22 @@ rm_dupl([P|Ps],Acc) ->
rm_dupl([],Acc) ->
lists:reverse(Acc).
-get_timeout(Opts) ->
- case lists:keysearch(timeout,1,Opts) of
- {value,{timeout,T}} -> T;
- false -> ?DEFAULT_TIMEOUT
- end.
+get_timeouts(Opts) ->
+ {case lists:keysearch(idle_timeout,1,Opts) of
+ {value,{_,T}} ->
+ T;
+ false ->
+ %% this check is for backwards compatibility (pre CT v1.8)
+ case lists:keysearch(timeout,1,Opts) of
+ {value,{_,T}} -> T;
+ false -> ?DEFAULT_TIMEOUT
+ end
+ end,
+ case lists:keysearch(total_timeout,1,Opts) of
+ {value,{_,T}} -> T;
+ false -> infinity
+ end}.
+
get_repeat(Opts) ->
case lists:keysearch(repeat,1,Opts) of
{value,{repeat,N}} when is_integer(N) ->
@@ -1010,7 +1031,8 @@ repeat_expect(Name,Pid,Data,Pattern,Acc,EO) ->
{error,Reason}
end.
-teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
+teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
+ total_timeout=TotalTO}) ->
ExpectFun = case EO#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EO)
@@ -1027,12 +1049,12 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
NotFinished ->
%% Get more data
Fun = fun() -> get_data1(EO#eo.teln_pid) end,
- case ct_gen_conn:do_within_time(Fun, EO#eo.timeout) of
- {error,Reason} ->
+ case timer:tc(ct_gen_conn, do_within_time, [Fun, IdleTO]) of
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
- %% is idle for EO#eo.timeout milliseconds.
+ %% is idle for EO#eo.idle_timeout milliseconds.
{error,Reason};
- {ok,Data1} ->
+ {_,{ok,Data1}} when TotalTO == infinity ->
case NotFinished of
{nomatch,Rest} ->
%% One expect
@@ -1040,6 +1062,21 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
{continue,Patterns1,Acc1,Rest} ->
%% Sequence
teln_expect1(Name,Pid,Rest++Data1,Patterns1,Acc1,EO)
+ end;
+ {Elapsed,{ok,Data1}} ->
+ TVal = trunc(TotalTO - (Elapsed/1000)),
+ if TVal =< 0 ->
+ {error,timeout};
+ true ->
+ EO1 = EO#eo{total_timeout = TVal},
+ case NotFinished of
+ {nomatch,Rest} ->
+ %% One expect
+ teln_expect1(Name,Pid,Rest++Data1,Pattern,[],EO1);
+ {continue,Patterns1,Acc1,Rest} ->
+ %% Sequence
+ teln_expect1(Name,Pid,Rest++Data1,Patterns1,Acc1,EO1)
+ end
end
end
end.
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index 2cbcba9c77..ce30dcb74b 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -32,7 +32,9 @@
-module(ct_telnet_client).
--export([open/1, open/2, open/3, open/4, close/1]).
+%% -define(debug, true).
+
+-export([open/2, open/3, open/4, open/5, close/1]).
-export([send_data/2, get_data/1]).
-define(TELNET_PORT, 23).
@@ -64,20 +66,23 @@
-define(TERMINAL_TYPE, 24).
-define(WINDOW_SIZE, 31).
--record(state,{get_data, keep_alive=true}).
+-record(state,{conn_name, get_data, keep_alive=true, log_pos=1}).
-open(Server) ->
- open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true).
+open(Server, ConnName) ->
+ open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true, ConnName).
-open(Server, Port) ->
- open(Server, Port, ?OPEN_TIMEOUT, true).
+open(Server, Port, ConnName) ->
+ open(Server, Port, ?OPEN_TIMEOUT, true, ConnName).
-open(Server, Port, Timeout) ->
- open(Server, Port, Timeout, true).
+open(Server, Port, Timeout, ConnName) ->
+ open(Server, Port, Timeout, true, ConnName).
-open(Server, Port, Timeout, KeepAlive) ->
+open(Server, Port, Timeout, KeepAlive, ConnName) ->
Self = self(),
- Pid = spawn(fun() -> init(Self, Server, Port, Timeout, KeepAlive) end),
+ Pid = spawn(fun() ->
+ init(Self, Server, Port, Timeout,
+ KeepAlive, ConnName)
+ end),
receive
{open,Pid} ->
{ok,Pid};
@@ -86,29 +91,34 @@ open(Server, Port, Timeout, KeepAlive) ->
end.
close(Pid) ->
- Pid ! close.
+ Pid ! {close,self()},
+ receive closed -> ok
+ after 5000 -> ok
+ end.
send_data(Pid, Data) ->
Pid ! {send_data, Data++"\n"},
ok.
get_data(Pid) ->
- Pid ! {get_data, self()},
+ Pid ! {get_data,self()},
receive
{data,Data} ->
- {ok, Data}
+ {ok,Data}
end.
%%%-----------------------------------------------------------------
%%% Internal functions
-init(Parent, Server, Port, Timeout, KeepAlive) ->
+init(Parent, Server, Port, Timeout, KeepAlive, ConnName) ->
case gen_tcp:connect(Server, Port, [list,{packet,0}], Timeout) of
{ok,Sock} ->
- dbg("Connected to: ~p (port: ~w, keep_alive: ~w)\n", [Server,Port,KeepAlive]),
- send([?IAC,?DO,?SUPPRESS_GO_AHEAD], Sock),
+ dbg("~p connected to: ~p (port: ~w, keep_alive: ~w)\n",
+ [ConnName,Server,Port,KeepAlive]),
+ send([?IAC,?DO,?SUPPRESS_GO_AHEAD], Sock, ConnName),
Parent ! {open,self()},
- loop(#state{get_data=10, keep_alive=KeepAlive}, Sock, []),
+ loop(#state{conn_name=ConnName, get_data=10, keep_alive=KeepAlive},
+ Sock, []),
gen_tcp:close(Sock);
Error ->
Parent ! {Error,self()}
@@ -118,6 +128,13 @@ loop(State, Sock, Acc) ->
receive
{tcp_closed,_} ->
dbg("Connection closed\n", []),
+ Data = lists:reverse(lists:append(Acc)),
+ dbg("Printing queued messages: ~tp",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ length(Data))]),
receive
{get_data,Pid} ->
Pid ! closed
@@ -125,11 +142,11 @@ loop(State, Sock, Acc) ->
ok
end;
{tcp,_,Msg0} ->
- dbg("tcp msg: ~p~n",[Msg0]),
+ dbg("tcp msg: ~tp~n",[Msg0]),
Msg = check_msg(Sock,Msg0,[]),
loop(State, Sock, [Msg | Acc]);
{send_data,Data} ->
- send(Data, Sock),
+ send(Data, Sock, State#state.conn_name),
loop(State, Sock, Acc);
{get_data,Pid} ->
NewState =
@@ -144,54 +161,100 @@ loop(State, Sock, Acc) ->
end;
_ ->
Data = lists:reverse(lists:append(Acc)),
- dbg("get_data ~p\n",[Data]),
+ Len = length(Data),
+ dbg("get_data ~tp\n",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ Len)]),
Pid ! {data,Data},
- State
+ State#state{log_pos = 1}
end,
loop(NewState, Sock, []);
{get_data_delayed,Pid} ->
NewState =
case State of
#state{keep_alive = true, get_data = 0} ->
- if Acc == [] -> send([?IAC,?NOP], Sock);
+ if Acc == [] -> send([?IAC,?NOP], Sock,
+ State#state.conn_name);
true -> ok
end,
State#state{get_data=10};
_ ->
State
end,
- NewAcc =
+ {NewAcc,Pos} =
case erlang:is_process_alive(Pid) of
- true ->
+ true when Acc /= [] ->
Data = lists:reverse(lists:append(Acc)),
- dbg("get_data_delayed ~p\n",[Data]),
+ Len = length(Data),
+ dbg("get_data_delayed ~tp\n",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ Len)]),
Pid ! {data,Data},
- [];
+ {[],1};
+ true when Acc == [] ->
+ dbg("get_data_delayed nodata\n",[]),
+ Pid ! {data,[]},
+ {[],1};
false ->
- Acc
+ {Acc,NewState#state.log_pos}
end,
- loop(NewState, Sock, NewAcc);
- close ->
+ loop(NewState#state{log_pos=Pos}, Sock, NewAcc);
+ {close,Pid} ->
dbg("Closing connection\n", []),
+ if Acc == [] ->
+ ok;
+ true ->
+ Data = lists:reverse(lists:append(Acc)),
+ dbg("Printing queued messages: ~tp",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ length(Data))])
+ end,
gen_tcp:close(Sock),
- ok
+ Pid ! closed
after wait(State#state.keep_alive,?IDLE_TIMEOUT) ->
- if
- Acc == [] -> send([?IAC,?NOP], Sock);
- true -> ok
- end,
- loop(State, Sock, Acc)
+ Data = lists:reverse(lists:append(Acc)),
+ case Data of
+ [] ->
+ send([?IAC,?NOP], Sock, State#state.conn_name),
+ loop(State, Sock, Acc);
+ _ when State#state.log_pos == length(Data)+1 ->
+ loop(State, Sock, Acc);
+ _ ->
+ dbg("Idle timeout, printing ~tp\n",[Data]),
+ Len = length(Data),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ Len)]),
+ loop(State#state{log_pos = Len+1}, Sock, Acc)
+ end
end.
wait(true, Time) -> Time;
wait(false, _) -> infinity.
-send(Data, Sock) ->
+send(Data, Sock, ConnName) ->
case Data of
[?IAC|_] = Cmd ->
cmd_dbg(Cmd);
_ ->
- dbg("Sending: ~p\n", [Data])
+ dbg("Sending: ~tp\n", [Data]),
+ try io_lib:format("[~w] ~ts", [?MODULE,Data]) of
+ Str ->
+ ct_telnet:log(ConnName, general_io, Str, [])
+ catch
+ _:_ -> ok
+ end
end,
gen_tcp:send(Sock, Data),
ok.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index c07ea323e6..10a9bdac67 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1120,8 +1120,9 @@ should_be_added(Tag,Node,_Data,Spec) ->
%% list terms *without* possible duplicates here
Tag == logdir; Tag == logopts;
Tag == basic_html; Tag == label;
- Tag == auto_compile; Tag == stylesheet;
- Tag == verbosity; Tag == silent_connections ->
+ Tag == auto_compile; Tag == abort_if_missing_suites;
+ Tag == stylesheet; Tag == verbosity;
+ Tag == silent_connections ->
lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
read_field(Spec,Tag)) == false;
%% for terms *with* possible duplicates
@@ -1496,6 +1497,8 @@ valid_terms() ->
{include,3},
{auto_compile,2},
{auto_compile,3},
+ {abort_if_missing_suites,2},
+ {abort_if_missing_suites,3},
{stylesheet,2},
{stylesheet,3},
{suites,3},
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 1d851f8d2f..f5eb3a72f0 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -77,6 +77,8 @@
-record(suite_data, {key,name,value}).
%%%-----------------------------------------------------------------
+start() ->
+ start(normal, ".", ?default_verbosity).
%%% @spec start(Mode) -> Pid | exit(Error)
%%% Mode = normal | interactive
%%% Pid = pid()
@@ -91,9 +93,6 @@
%%% <code>ct_util_server</code>.</p>
%%%
%%% @see ct
-start() ->
- start(normal, ".", ?default_verbosity).
-
start(LogDir) when is_list(LogDir) ->
start(normal, LogDir, ?default_verbosity);
start(Mode) ->
@@ -382,13 +381,17 @@ loop(Mode,TestData,StartDir) ->
TestData1 =
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
- case Fun(Val) of
+ try Fun(Val) of
'$delete' ->
return(From,deleted),
lists:keydelete(Key,1,TestData);
NewVal ->
return(From,NewVal),
[{Key,NewVal}|lists:keydelete(Key,1,TestData)]
+ catch
+ _:Error ->
+ return(From,{error,Error}),
+ TestData
end;
_ ->
case lists:member(create,Opts) of
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index a82d58cc42..845bb55486 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -48,6 +48,7 @@
release_shell=false,
include=[],
auto_compile=[],
+ abort_if_missing_suites=[],
stylesheet=[],
multiply_timetraps=[],
scale_timetraps=[],
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
index a731c8054c..0e6c877c5d 100644
--- a/lib/common_test/src/cth_conn_log.erl
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -100,7 +100,6 @@ get_log_opts(Opts) ->
Hosts = proplists:get_value(hosts,Opts,[]),
{LogType,Hosts}.
-
pre_init_per_testcase(TestCase,Config,CthState) ->
Logs =
lists:map(
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index 7ed2018bdf..bb12171ea7 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -79,6 +79,10 @@ init(Path, Opts) ->
url_base = proplists:get_value(url_base,Opts),
timer = now() }.
+pre_init_per_suite(Suite,SkipOrFail,State) when is_tuple(SkipOrFail) ->
+ {SkipOrFail, init_tc(State#state{curr_suite = Suite,
+ curr_suite_ts = now()},
+ SkipOrFail) };
pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) ->
TcLog = proplists:get_value(tc_logfile,Config),
CurrLogDir = filename:dirname(TcLog),
diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl
index e049c3bf39..b05386a5ab 100644
--- a/lib/common_test/src/unix_telnet.erl
+++ b/lib/common_test/src/unix_telnet.erl
@@ -109,7 +109,7 @@ connect(ConnName,Ip,Port,Timeout,KeepAlive,Extra) ->
connect1(Name,Ip,Port,Timeout,KeepAlive,Username,Password) ->
start_gen_log("unix_telnet connect"),
Result =
- case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive) of
+ case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive,Name) of
{ok,Pid} ->
case ct_telnet:silent_teln_expect(Name,Pid,[],
[prompt],?prx,[]) of
@@ -143,13 +143,13 @@ connect1(Name,Ip,Port,Timeout,KeepAlive,Username,Password) ->
{ok,[{prompt,_OtherPrompt1},{prompt,_OtherPrompt2}],_} ->
{ok,Pid};
Error ->
- log(Name,error,
+ log(Name,conn_error,
"Did not get expected prompt from ~p:~p\n~p\n",
[Ip,Port,Error]),
{error,Error}
end;
Error ->
- log(Name,error,
+ log(Name,conn_error,
"Could not open telnet connection to ~p:~p\n~p\n",
[Ip,Port,Error]),
Error
diff --git a/lib/common_test/test/ct_config_info_SUITE.erl b/lib/common_test/test/ct_config_info_SUITE.erl
index 8f2f0eb75f..9c242a41df 100644
--- a/lib/common_test/test/ct_config_info_SUITE.erl
+++ b/lib/common_test/test/ct_config_info_SUITE.erl
@@ -125,7 +125,7 @@ test_events(config_info) ->
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g1,[]},
{failed,{timetrap_timeout,350}}}},
- {?eh,tc_auto_skip,{config_info_1_SUITE,t11,
+ {?eh,tc_auto_skip,{config_info_1_SUITE,{t11,g1},
{failed,{config_info_1_SUITE,init_per_group,{timetrap_timeout,350}}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,{end_per_group,g1},
{failed,{config_info_1_SUITE,init_per_group,
@@ -142,7 +142,7 @@ test_events(config_info) ->
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g4,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g4,[]},
{failed,{timetrap_timeout,400}}}},
- {?eh,tc_auto_skip,{config_info_1_SUITE,t41,
+ {?eh,tc_auto_skip,{config_info_1_SUITE,{t41,g4},
{failed,{config_info_1_SUITE,init_per_group,
{timetrap_timeout,400}}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,{end_per_group,g4},
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index 28f0494d20..ecf231529a 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -66,7 +66,7 @@ all() ->
[cfg_error, lib_error, no_compile, timetrap_end_conf,
timetrap_normal, timetrap_extended, timetrap_parallel,
timetrap_fun, timetrap_fun_group, misc_errors,
- config_restored].
+ config_restored, config_func_errors].
groups() ->
[].
@@ -310,6 +310,25 @@ config_restored(Config) when is_list(Config) ->
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
+%%%
+config_func_errors(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "error/test/config_func_error_1_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite}],
+ Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(config_func_errors,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(config_func_errors),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -323,8 +342,6 @@ setup(Test, Config) ->
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
- %reformat(Events, _EH) ->
- % Events.
%%%-----------------------------------------------------------------
%%% TEST EVENTS
@@ -352,8 +369,8 @@ test_events(cfg_error) ->
{'EXIT',init_per_suite_fails}}}}},
{?eh,test_stats,{0,0,{0,1}}},
{?eh,tc_auto_skip,
- {cfg_error_1_SUITE,tc2,{failed,{cfg_error_1_SUITE,init_per_suite,
- {'EXIT',init_per_suite_fails}}}}},
+ {cfg_error_1_SUITE,{tc2,g1},{failed,{cfg_error_1_SUITE,init_per_suite,
+ {'EXIT',init_per_suite_fails}}}}},
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_auto_skip,
{cfg_error_1_SUITE,end_per_suite,{failed,{cfg_error_1_SUITE,init_per_suite,
@@ -369,7 +386,7 @@ test_events(cfg_error) ->
{'EXIT',{{badmatch,[1,2]},'_'}}}}}},
{?eh,test_stats,{0,0,{0,3}}},
{?eh,tc_auto_skip,
- {cfg_error_2_SUITE,tc2,
+ {cfg_error_2_SUITE,{tc2,g1},
{failed,{cfg_error_2_SUITE,init_per_suite,
{'EXIT',{{badmatch,[1,2]},'_'}}}}}},
{?eh,test_stats,{0,0,{0,4}}},
@@ -386,7 +403,7 @@ test_events(cfg_error) ->
{failed,{cfg_error_3_SUITE,init_per_suite,{timetrap_timeout,2000}}}}},
{?eh,test_stats,{0,0,{0,5}}},
{?eh,tc_auto_skip,
- {cfg_error_3_SUITE,tc2,
+ {cfg_error_3_SUITE,{tc2,g1},
{failed,{cfg_error_3_SUITE,init_per_suite,{timetrap_timeout,2000}}}}},
{?eh,test_stats,{0,0,{0,6}}},
{?eh,tc_auto_skip,
@@ -400,7 +417,7 @@ test_events(cfg_error) ->
{failed,{cfg_error_4_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,7}}},
{?eh,tc_auto_skip,
- {cfg_error_4_SUITE,tc2,
+ {cfg_error_4_SUITE,{tc2,g1},
{failed,{cfg_error_4_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,8}}},
{?eh,tc_auto_skip,
@@ -414,7 +431,7 @@ test_events(cfg_error) ->
{failed,{cfg_error_5_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,9}}},
{?eh,tc_auto_skip,
- {cfg_error_5_SUITE,tc2,
+ {cfg_error_5_SUITE,{tc2,g1},
{failed,{cfg_error_5_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,10}}},
{?eh,tc_auto_skip,
@@ -460,7 +477,7 @@ test_events(cfg_error) ->
{cfg_error_8_SUITE,{init_per_group,g1,[]},
{failed,{error,{init_per_group_fails,g1}}}}},
{?eh,tc_auto_skip,
- {cfg_error_8_SUITE,tc1,
+ {cfg_error_8_SUITE,{tc1,g1},
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{init_per_group_fails,g1}}}}}},
{?eh,test_stats,{4,0,{0,11}}},
@@ -472,7 +489,7 @@ test_events(cfg_error) ->
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g2,[]}}},
{?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g2,[]},
{failed,{timetrap_timeout,2000}}}},
- {?eh,tc_auto_skip,{cfg_error_8_SUITE,tc1,
+ {?eh,tc_auto_skip,{cfg_error_8_SUITE,{tc1,g2},
{failed,{cfg_error_8_SUITE,init_per_group,
{timetrap_timeout,2000}}}}},
{?eh,test_stats,{4,0,{0,12}}},
@@ -485,7 +502,7 @@ test_events(cfg_error) ->
{cfg_error_8_SUITE,{init_per_group,g3,[]},
{failed,{error,{{badmatch,42},'_'}}}}},
{?eh,tc_auto_skip,
- {cfg_error_8_SUITE,tc1,
+ {cfg_error_8_SUITE,{tc1,g3},
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{{badmatch,42},'_'}}}}}},
{?eh,test_stats,{4,0,{0,13}}},
@@ -511,7 +528,7 @@ test_events(cfg_error) ->
{?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g6,[]},
{failed,{error,{sub_group_failed,g6}}}}},
{?eh,tc_auto_skip,
- {cfg_error_8_SUITE,tc2,
+ {cfg_error_8_SUITE,{tc2,g6},
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{sub_group_failed,g6}}}}}},
{?eh,test_stats,{6,0,{0,14}}},
@@ -1094,11 +1111,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g4,[]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g4,[]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,g4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,1}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,g4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,2}}},
@@ -1109,11 +1126,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g5,[]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g5,[]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,g5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,3}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,g5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,4}}},
@@ -1124,11 +1141,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g6,[]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g6,[]},
{failed,{timetrap_timeout,{'$approx',500}}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,g6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{0,11,{0,5}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,g6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{0,11,{0,6}}},
@@ -1277,11 +1294,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,pg4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,7}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,pg4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,8}}},
@@ -1293,11 +1310,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,pg5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,9}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,pg5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,10}}},
@@ -1309,11 +1326,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]},
{failed,{timetrap_timeout,{'$approx',500}}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,pg6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{4,26,{0,11}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,pg6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{4,26,{0,12}}},
@@ -1390,10 +1407,10 @@ test_events(timetrap_fun_group) ->
{?eh,tc_done,{timetrap_8_SUITE,tc0,
{user_timetrap_error,{kaboom,'_'}}}},
{?eh,test_stats,{9,31,{0,12}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc1,sg1},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{9,31,{0,13}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,sg1},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{9,31,{0,14}}},
{?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg1,[sequence]}}},
@@ -1408,10 +1425,10 @@ test_events(timetrap_fun_group) ->
{?eh,tc_done,{timetrap_8_SUITE,tc0,
{failed,{timetrap_timeout,{'$approx',1000}}}}},
{?eh,test_stats,{10,32,{0,14}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc1,sg2},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{10,32,{0,15}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,sg2},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{10,32,{0,16}}},
{?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg2,[sequence]}}},
@@ -1498,4 +1515,42 @@ test_events(config_restored) ->
{?eh,tc_done,{config_restored_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
+ ];
+
+test_events(config_func_errors) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{config_func_error_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{config_func_error_1_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_iptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_iptc,'_'}},
+ {?eh,test_stats,{0,1,{0,0}}},
+
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_eptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_eptc,'_'}},
+ {?eh,test_stats,{0,2,{0,0}}},
+
+ [{?eh,tc_start,{config_func_error_1_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{init_per_group,g1,[]},ok}},
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_iptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_iptc,'_'}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{config_func_error_1_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{end_per_group,g1,[]},ok}}],
+
+ [{?eh,tc_start,{config_func_error_1_SUITE,{init_per_group,g2,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{init_per_group,g2,[]},ok}},
+ {?eh,tc_start,{config_func_error_1_SUITE,exit_in_eptc}},
+ {?eh,tc_done,{config_func_error_1_SUITE,exit_in_eptc,'_'}},
+ {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{config_func_error_1_SUITE,{end_per_group,g2,[]}}},
+ {?eh,tc_done,{config_func_error_1_SUITE,{end_per_group,g2,[]},ok}}],
+
+ {?eh,tc_start,{config_func_error_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{config_func_error_1_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl
new file mode 100644
index 0000000000..f1025213dc
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/config_func_error_1_SUITE.erl
@@ -0,0 +1,138 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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(config_func_error_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,5}}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+bad_proc(Config) ->
+ ct:pal("Bye bye from ~p", [self()]),
+ %% this call will either generate an exit immediately
+ %% or return a fun to be executed here
+ ErrorFun = ct_test_support:random_error(Config),
+ ct:log("Calling error fun now...", []),
+ ErrorFun(),
+ ct:sleep(10000),
+ ok.
+
+init_per_testcase(exit_in_iptc, Config) ->
+ spawn_link(?MODULE, bad_proc, [Config]),
+ ct:sleep(10000),
+ Config;
+init_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(exit_in_eptc, Config) ->
+ spawn_link(?MODULE, bad_proc, [Config]),
+ ct:sleep(10000),
+ ok;
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%%--------------------------------------------------------------------
+groups() ->
+ [{g1, [], [exit_in_iptc]},
+ {g2, [], [exit_in_eptc]}].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [exit_in_iptc,
+ exit_in_eptc,
+ {group, g1},
+ {group, g2}].
+
+exit_in_iptc(_) ->
+ ok.
+
+exit_in_eptc(_) ->
+ ok.
+
diff --git a/lib/common_test/test/ct_group_info_SUITE.erl b/lib/common_test/test/ct_group_info_SUITE.erl
index e7bc5baaa1..83ac7dbbcf 100644
--- a/lib/common_test/test/ct_group_info_SUITE.erl
+++ b/lib/common_test/test/ct_group_info_SUITE.erl
@@ -273,7 +273,7 @@ test_events(timetrap_all) ->
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
{?eh,tc_auto_skip,
- {group_timetrap_1_SUITE,t111,{group0_failed,bad_return_value}}},
+ {group_timetrap_1_SUITE,{t111,g11},{group0_failed,bad_return_value}}},
{?eh,test_stats,{0,13,{0,1}}},
{?eh,tc_auto_skip,{group_timetrap_1_SUITE,
{end_per_group,g11},
@@ -431,7 +431,7 @@ test_events(timetrap_all_no_ips) ->
{?eh,tc_done,{group_timetrap_2_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_timetrap_2_SUITE,t111,
+ {?eh,tc_auto_skip,{group_timetrap_2_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{0,13,{0,1}}},
{?eh,tc_auto_skip,{group_timetrap_2_SUITE,
@@ -512,7 +512,7 @@ test_events(timetrap_all_no_ipg) ->
{?eh,tc_done,{ct_framework,
{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_timetrap_3_SUITE,t111,{group0_failed,bad_return_value}}},
+ {?eh,tc_auto_skip,{group_timetrap_3_SUITE,{t111,g11},{group0_failed,bad_return_value}}},
{?eh,test_stats,{0,13,{0,1}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g11},
{group0_failed,bad_return_value}}}],
@@ -551,7 +551,7 @@ test_events(require) ->
{?eh,tc_done,{group_require_1_SUITE,{init_per_group,g4,[]},
{auto_skipped,{require_failed,
{name_in_use,common2_alias,common2}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t41,g4},
{require_failed,
{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
@@ -580,7 +580,7 @@ test_events(require) ->
{init_per_group,g8,[]},
{auto_skipped,{require_failed,
{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,{end_per_group,g8},
@@ -604,7 +604,7 @@ test_events(require) ->
{?eh,tc_done,{group_require_1_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t111,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,
@@ -646,7 +646,7 @@ test_events(require_default) ->
{?eh,tc_done,{group_require_1_SUITE,
{init_per_group,g4,[]},
{auto_skipped,{require_failed,{not_available,common3}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t41,g4},
{require_failed,{not_available,common3}}}},
{?eh,test_stats,{4,0,{0,1}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,{end_per_group,g4},
@@ -674,7 +674,7 @@ test_events(require_default) ->
{init_per_group,g8,[]},
{auto_skipped,{require_failed,
{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,{end_per_group,g8},
@@ -699,7 +699,7 @@ test_events(require_default) ->
{?eh,tc_done,{group_require_1_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t111,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,
@@ -740,7 +740,7 @@ test_events(require_no_ips) ->
{?eh,tc_done,{group_require_2_SUITE,{init_per_group,g4,[]},
{auto_skipped,{require_failed,
{name_in_use,common2_alias,common2}}}}},
- {?eh,tc_auto_skip,{group_require_2_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_2_SUITE,{t41,g4},
{require_failed,{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
{?eh,tc_auto_skip,{group_require_2_SUITE,{end_per_group,g4},
@@ -768,7 +768,7 @@ test_events(require_no_ips) ->
{init_per_group,g8,[]},
{auto_skipped,{require_failed,
{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_2_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_2_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{group_require_2_SUITE,{end_per_group,g8},
@@ -792,7 +792,7 @@ test_events(require_no_ips) ->
{?eh,tc_done,{group_require_2_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_2_SUITE,t111,
+ {?eh,tc_auto_skip,{group_require_2_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{group_require_2_SUITE,
@@ -831,7 +831,7 @@ test_events(require_no_ipg) ->
[{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]},
{auto_skipped,{require_failed,{name_in_use,common2_alias,common2}}}}},
- {?eh,tc_auto_skip,{group_require_3_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_3_SUITE,{t41,g4},
{require_failed,{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g4},
@@ -857,7 +857,7 @@ test_events(require_no_ipg) ->
[{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]},
{auto_skipped,{require_failed,{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_3_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_3_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g8},
@@ -879,7 +879,7 @@ test_events(require_no_ipg) ->
[{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_3_SUITE,t111,{group0_failed,bad_return_value}}},
+ {?eh,tc_auto_skip,{group_require_3_SUITE,{t111,g11},{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g11},
{group0_failed,bad_return_value}}}],
diff --git a/lib/common_test/test/ct_groups_spec_SUITE.erl b/lib/common_test/test/ct_groups_spec_SUITE.erl
index 5a6d5ac0ac..de4ab77229 100644
--- a/lib/common_test/test/ct_groups_spec_SUITE.erl
+++ b/lib/common_test/test/ct_groups_spec_SUITE.erl
@@ -246,7 +246,8 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t11,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t12,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t13,{failed,{groups_spec_1_SUITE,t12}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t13,g1},
+ {failed,{groups_spec_1_SUITE,t12}}}},
{?eh,test_stats,{3,2,{0,1}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]},ok}}],
@@ -327,19 +328,27 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{14,9,{0,2}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
{?eh,tc_done,{groups_spec_1_SUITE,t22,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t41,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t51,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t52,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t42,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t23,{failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t41,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t51,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t52,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t42,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t23,g2},
+ {failed,{groups_spec_1_SUITE,t22}}}},
{?eh,test_stats,{14,10,{0,9}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g2,[sequence]}}},
@@ -355,7 +364,8 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{16,11,{0,10}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
@@ -372,8 +382,10 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g5,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t51,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t52,{failed,{timetrap_timeout,2000}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t52}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
{?eh,test_stats,{18,13,{0,12}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]},ok}}],
@@ -417,7 +429,8 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t11,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t12,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t13,{failed,{groups_spec_1_SUITE,t12}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t13,g1},
+ {failed,{groups_spec_1_SUITE,t12}}}},
{?eh,test_stats,{3,2,{0,1}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]},ok}}],
@@ -493,18 +506,26 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{14,9,{0,2}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
{?eh,tc_done,{groups_spec_1_SUITE,t22,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t41,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t51,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t52,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t42,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t23,{failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t41,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t51,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t52,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t42,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t23,g2},
+ {failed,{groups_spec_1_SUITE,t22}}}},
{?eh,test_stats,{14,10,{0,9}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g2,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g2,[sequence]},ok}}],
@@ -521,7 +542,8 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{16,11,{0,10}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
@@ -535,8 +557,10 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g5,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t51,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t52,{failed,{timetrap_timeout,2000}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t52}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
{?eh,test_stats,{18,13,{0,12}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]},ok}}],
@@ -555,7 +579,8 @@ test_events(override_with_spec) ->
[{?eh,tc_start,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t12,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t13,{failed,{groups_spec_1_SUITE,t12}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t13,g1},
+ {failed,{groups_spec_1_SUITE,t12}}}},
{?eh,test_stats,{19,15,{0,13}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]},ok}}],
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index b5855da9df..c8fc4bd59b 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -786,7 +786,7 @@ test_events(skip_pre_end_cth) ->
{?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]},
{skipped,"Test skip"}}}],
- {?eh,cth,{'_',on_tc_skip,[end_per_group,
+ {?eh,cth,{'_',on_tc_skip,[{end_per_group,group1},
{tc_user_skip,{skipped,"Test skip"}},
[]]}},
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,end_per_suite}},
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
index 9ee2a90896..6caac7e447 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
@@ -229,9 +229,9 @@ post_end_per_testcase(TC,Config,Return,State) ->
%% This function should be used for extra cleanup which might be needed.
%% It is not possible to modify the config or the status of the test run.
-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
- init_per_group | end_per_group | atom(),
- Reason :: term(), State :: #state{}) ->
- NewState :: #state{}.
+ init_per_group | end_per_group | atom() |
+ {Function :: atom(), GroupName :: atom()},
+ Reason :: term(), State :: #state{}) -> NewState :: #state{}.
on_tc_fail(TC, Reason, State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
@@ -243,11 +243,11 @@ on_tc_fail(TC, Reason, State) ->
%% or due to an init function failing. Test case can be
%% end_per_suite, init_per_group, end_per_group and the actual test cases.
-spec on_tc_skip(TC :: end_per_suite |
- init_per_group | end_per_group | atom(),
+ init_per_group | end_per_group | atom() |
+ {Function :: atom(), GroupName :: atom()},
{tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
- {tc_user_skip, {skipped, Reason :: term()}},
- State :: #state{}) ->
- NewState :: #state{}.
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) -> NewState :: #state{}.
on_tc_skip(TC, Reason, State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
index 7408cbe376..e90513f888 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -81,7 +81,8 @@ end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{timetrap,{seconds,60}},
+ {ct_hooks,[ts_install_cth]}].
all() ->
[ct_master_test].
diff --git a/lib/common_test/test/ct_repeat_1_SUITE.erl b/lib/common_test/test/ct_repeat_1_SUITE.erl
index 98eaa28763..e37aeb196c 100644
--- a/lib/common_test/test/ct_repeat_1_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_1_SUITE.erl
@@ -225,7 +225,7 @@ test_events(repeat_cs_and_grs) ->
{?eh,test_stats,{3,1,{0,0}}},
[{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]},
{failed,{error,fails_on_purpose}}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_fail_init},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',fails_on_purpose}}}}},
{?eh,test_stats,{3,1,{0,1}}},
@@ -247,7 +247,7 @@ test_events(repeat_cs_and_grs) ->
{?eh,test_stats,{7,2,{0,1}}},
[{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]},
{failed,{error,fails_on_purpose}}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_fail_init},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',fails_on_purpose}}}}},
{?eh,test_stats,{7,2,{0,2}}},
@@ -269,7 +269,7 @@ test_events(repeat_seq) ->
ok}},
{?eh,test_stats,{1,0,{0,0}}},
{?eh,test_stats,{1,1,{0,0}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_1},
{failed,{repeat_1_SUITE,tc_fail_1}}}},
{?eh,test_stats,{1,1,{0,1}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -291,7 +291,7 @@ test_events(repeat_seq) ->
{?eh,tc_done,{repeat_1_SUITE,
{end_per_group,gr_fail_result,[]},
{return_group_result,failed}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_2},
{group_result,gr_fail_result,failed}}},
{?eh,test_stats,{4,2,{0,3}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -315,7 +315,7 @@ test_events(repeat_seq) ->
{failed,
{repeat_1_SUITE,init_per_group,
{'EXIT',fails_on_purpose}}}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_3},
{group_result,gr_fail_init,failed}}},
{?eh,test_stats,{7,2,{0,6}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -329,12 +329,13 @@ test_events(repeat_seq) ->
[{?eh,tc_done,{repeat_1_SUITE,
{init_per_group,repeat_seq_4,[sequence,{repeat,2}]},
ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_1,'_'}},
{?eh,test_stats,{8,3,{0,8}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,
- tc_ok_1,{failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1},
+ {failed,{repeat_1_SUITE,tc_fail_1}}}},
{?eh,test_stats,{8,3,{0,9}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,
- tc_ok_1,{failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,repeat_seq_4},
+ {failed,{repeat_1_SUITE,tc_fail_1}}}},
{?eh,test_stats,{8,3,{0,10}}},
{?eh,tc_done,{repeat_1_SUITE,
{end_per_group,repeat_seq_4,[sequence,{repeat,2}]},
@@ -764,7 +765,7 @@ test_events(repeat_gr_until_any_fail) ->
{init_per_group,gr_ok_then_fail_init,[]},
{failed,{error,failing_this_time}}}},
{?eh,tc_auto_skip,
- {repeat_1_SUITE,tc_ok_1,
+ {repeat_1_SUITE,{tc_ok_1,gr_ok_then_fail_init},
{failed,
{repeat_1_SUITE,init_per_group,
{'EXIT',failing_this_time}}}}},
@@ -963,7 +964,7 @@ test_events(repeat_gr_until_all_ok) ->
[{?eh,tc_done,{repeat_1_SUITE,
{init_per_group,gr_fail_init_then_ok,[]},
{failed,{error,failing_this_time}}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_fail_init_then_ok},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',failing_this_time}}}}},
{?eh,test_stats,{7,1,{0,1}}},
@@ -1237,10 +1238,10 @@ test_events(repeat_seq_until_any_fail) ->
{?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
{failed,{error,failing_this_time}}}},
{?eh,test_stats,{15,1,{0,0}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_until_any_fail_3},
{failed,{repeat_1_SUITE,tc_ok_then_fail_1}}}},
{?eh,test_stats,{15,1,{0,1}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1},
{failed,{repeat_1_SUITE,tc_ok_then_fail_1}}}},
{?eh,test_stats,{15,1,{0,2}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -1264,10 +1265,10 @@ test_events(repeat_seq_until_any_fail) ->
[{?eh,tc_done,{repeat_1_SUITE,
{end_per_group,gr_ok_then_fail_result,[]},
{return_group_result,failed}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1},
{group_result,gr_ok_then_fail_result,failed}}},
{?eh,test_stats,{19,1,{0,3}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,repeat_seq_until_any_fail_4},
{group_result,gr_ok_then_fail_result,failed}}},
{?eh,test_stats,{19,1,{0,4}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -1296,10 +1297,10 @@ test_events(repeat_seq_until_any_fail) ->
{?eh,tc_auto_skip,{repeat_1_SUITE,{end_per_group,gr_ok_then_fail_init},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',failing_this_time}}}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_2},
{group_result,gr_ok_then_fail_init,failed}}},
{?eh,test_stats,{24,1,{0,6}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,repeat_seq_until_any_fail_5},
{group_result,gr_ok_then_fail_init,failed}}},
{?eh,test_stats,{24,1,{0,7}}},
{?eh,tc_done,{repeat_1_SUITE,
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
index bb2aba2c5a..b6f285322d 100644
--- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
@@ -343,9 +343,9 @@ skip_first_tc1(Suite) ->
{?eh,tc_done,{Suite,tc2,?skipped}},
{?eh,test_stats,{'_',0,{0,1}}},
{?eh,tc_done,{Suite,{init_per_group,g,[]},?skipped}},
- {?eh,tc_auto_skip,{Suite,tc1,?skip_reason}},
+ {?eh,tc_auto_skip,{Suite,{tc1,g},?skip_reason}},
{?eh,test_stats,{'_',0,{0,2}}},
- {?eh,tc_auto_skip,{Suite,tc2,?skip_reason}},
+ {?eh,tc_auto_skip,{Suite,{tc2,g},?skip_reason}},
{?eh,test_stats,{'_',0,{0,3}}},
{?eh,tc_auto_skip,{Suite,{end_per_group,g},?skip_reason}},
{?eh,tc_done,{Suite,tc2,?skipped}},
diff --git a/lib/common_test/test/ct_sequence_1_SUITE.erl b/lib/common_test/test/ct_sequence_1_SUITE.erl
index 8c87236838..5a775a1117 100644
--- a/lib/common_test/test/ct_sequence_1_SUITE.erl
+++ b/lib/common_test/test/ct_sequence_1_SUITE.erl
@@ -185,7 +185,8 @@ test_events(subgroup_return_fail) ->
{?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},
{return_group_result,failed}}}],
{?eh,tc_auto_skip,
- {subgroups_1_SUITE,ok_tc,{group_result,return_fail,failed}}},
+ {subgroups_1_SUITE,{ok_tc,ok_group},
+ {group_result,return_fail,failed}}},
{?eh,test_stats,{0,1,{0,1}}},
{?eh,tc_start,
{subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]}}},
@@ -208,14 +209,15 @@ test_events(subgroup_init_fail) ->
[{?eh,tc_start,{subgroups_1_SUITE,{init_per_group,fail_init,[]}}},
{?eh,tc_done,{subgroups_1_SUITE,{init_per_group,fail_init,[]},
{failed,{error,init_per_group_fails_on_purpose}}}},
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,fail_init},
{failed,{subgroups_1_SUITE,init_per_group,
{'EXIT',init_per_group_fails_on_purpose}}}}},
{?eh,test_stats,{0,0,{0,1}}},
{?eh,tc_auto_skip,{subgroups_1_SUITE,{end_per_group,fail_init},
{failed,{subgroups_1_SUITE,init_per_group,
{'EXIT',init_per_group_fails_on_purpose}}}}}],
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{group_result,fail_init,failed}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,ok_group},
+ {group_result,fail_init,failed}}},
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_start,{subgroups_1_SUITE,{end_per_group,subgroup_init_fail,[sequence]}}},
{?eh,tc_done,{subgroups_1_SUITE,
@@ -237,7 +239,8 @@ test_events(subgroup_after_failed_case) ->
{?eh,tc_start,{subgroups_1_SUITE,failing_tc}},
{?eh,tc_done,{subgroups_1_SUITE,failing_tc,{failed,{error,{{badmatch,3},'_'}}}}},
{?eh,test_stats,{0,1,{0,0}}},
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{failed,{subgroups_1_SUITE,failing_tc}}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,ok_group},
+ {failed,{subgroups_1_SUITE,failing_tc}}}},
{?eh,test_stats,{0,1,{0,1}}},
{?eh,tc_start,{subgroups_1_SUITE,
{end_per_group,subgroup_after_failed_case,[sequence]}}},
@@ -265,7 +268,8 @@ test_events(case_after_subgroup_return_fail) ->
{?eh,tc_start,{subgroups_1_SUITE,{end_per_group,return_fail,[]}}},
{?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},
{return_group_result,failed}}}],
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{group_result,return_fail,failed}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,case_after_subgroup_return_fail},
+ {group_result,return_fail,failed}}},
{?eh,test_stats,{0,1,{0,1}}},
{?eh,tc_start,{subgroups_1_SUITE,
{end_per_group,case_after_subgroup_return_fail,[sequence]}}},
@@ -289,7 +293,7 @@ test_events(case_after_subgroup_fail_init) ->
{?eh,tc_done,{subgroups_1_SUITE,
{init_per_group,fail_init,[]},
{failed,{error,init_per_group_fails_on_purpose}}}},
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,fail_init},
{failed,
{subgroups_1_SUITE,init_per_group,
{'EXIT',init_per_group_fails_on_purpose}}}}},
@@ -300,7 +304,8 @@ test_events(case_after_subgroup_fail_init) ->
{'EXIT',init_per_group_fails_on_purpose}}}}}],
{?eh,tc_auto_skip,
- {subgroups_1_SUITE,ok_tc,{group_result,fail_init,failed}}},
+ {subgroups_1_SUITE,{ok_tc,case_after_subgroup_fail_init},
+ {group_result,fail_init,failed}}},
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_start,{subgroups_1_SUITE,
{end_per_group,case_after_subgroup_fail_init,[sequence]}}},
diff --git a/lib/common_test/test/ct_skip_SUITE.erl b/lib/common_test/test/ct_skip_SUITE.erl
index b0a6c839a2..6fb803b928 100644
--- a/lib/common_test/test/ct_skip_SUITE.erl
+++ b/lib/common_test/test/ct_skip_SUITE.erl
@@ -153,10 +153,10 @@ testspec_skip(Config) when is_list(Config) ->
{skip_groups, TestDir, user_skip_6_SUITE, psub1, "SKIPPED"}],
{Opts,ERPid} = setup_testspec([{ts1,TestSpec1},
- {ts2,TestSpec2},
- {ts3,TestSpec3},
- {ts4,TestSpec4},
- {ts5,TestSpec5}], Config),
+ {ts2,TestSpec2},
+ {ts3,TestSpec3},
+ {ts4,TestSpec4},
+ {ts5,TestSpec5}], Config),
ok = ct_test_support:run(Opts, Config),
@@ -234,8 +234,8 @@ test_events(auto_skip) ->
{?eh,tc_done,
{auto_skip_2_SUITE,init_per_suite,{failed,{error,init_per_suite_failed}}}},
{?eh,tc_auto_skip,
- {auto_skip_2_SUITE,tc1,{failed,{auto_skip_2_SUITE,init_per_suite,
- {'EXIT',init_per_suite_failed}}}}},
+ {auto_skip_2_SUITE,{tc1,g1},{failed,{auto_skip_2_SUITE,init_per_suite,
+ {'EXIT',init_per_suite_failed}}}}},
{?eh,test_stats,{0,0,{0,3}}},
{?eh,tc_auto_skip,
{auto_skip_2_SUITE,end_per_suite,{failed,{auto_skip_2_SUITE,init_per_suite,
@@ -274,12 +274,12 @@ test_events(auto_skip) ->
{?eh,tc_done,
{auto_skip_5_SUITE,{init_per_group,g1,[]},{failed,{error,{group,g1,failed}}}}},
{?eh,tc_auto_skip,
- {auto_skip_5_SUITE,tc1,{failed,{auto_skip_5_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_5_SUITE,{tc1,g1},{failed,{auto_skip_5_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,6}}},
{?eh,tc_auto_skip,
- {auto_skip_5_SUITE,tc2,{failed,{auto_skip_5_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_5_SUITE,{tc2,g1},{failed,{auto_skip_5_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,7}}},
{?eh,tc_auto_skip,
{auto_skip_5_SUITE,{end_per_group,g1},
@@ -295,20 +295,20 @@ test_events(auto_skip) ->
{?eh,tc_done,
{auto_skip_6_SUITE,{init_per_group,g1,[]},{failed,{error,{group,g1,failed}}}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc1,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc1,g1},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,8}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc3,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc3,g2},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,9}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc4,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc4,g2},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,10}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc2,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc2,g1},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,11}}},
{?eh,tc_auto_skip,
{auto_skip_6_SUITE,{end_per_group,g1},
@@ -324,12 +324,12 @@ test_events(auto_skip) ->
{?eh,tc_done,{auto_skip_6_SUITE,{init_per_group,g4,[]},
{failed,{error,{group,g4,failed}}}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc3,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g4,failed}}}}}},
+ {auto_skip_6_SUITE,{tc3,g4},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g4,failed}}}}}},
{?eh,test_stats,{3,0,{0,12}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc4,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g4,failed}}}}}},
+ {auto_skip_6_SUITE,{tc4,g4},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g4,failed}}}}}},
{?eh,test_stats,{3,0,{0,13}}},
{?eh,tc_auto_skip,
{auto_skip_6_SUITE,{end_per_group,g4},
@@ -498,13 +498,13 @@ test_events(auto_skip) ->
[{suite,auto_skip_12_SUITE}]},
{auto_skipped,
{require_failed,{not_available,unknown_variable_g1}}}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc1,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc1,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,25}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc2,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc2,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,26}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc3,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc3,g2},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,27}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g1},
@@ -516,13 +516,13 @@ test_events(auto_skip) ->
[{suite,auto_skip_12_SUITE}]},
{auto_skipped,
{require_failed,{not_available,unknown_variable_g1}}}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc1,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc1,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,28}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc2,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc2,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,29}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc3,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc3,g2},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,30}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g1},
@@ -544,7 +544,7 @@ test_events(auto_skip) ->
[{suite,auto_skip_12_SUITE}]},
{auto_skipped,
{require_failed,{not_available,unknown_variable_g4}}}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc3,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc3,g4},
{require_failed,{not_available,unknown_variable_g4}}}},
{?eh,test_stats,{12,0,{0,31}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g4},
@@ -574,10 +574,10 @@ test_events(user_skip) ->
{user_skip_1_SUITE,tc1,"Whole suite skipped"}},
{?eh,test_stats,{0,0,{1,0}}},
{?eh,tc_user_skip,
- {user_skip_1_SUITE,tc2,"Whole suite skipped"}},
+ {user_skip_1_SUITE,{tc2,g1},"Whole suite skipped"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,
- {user_skip_1_SUITE,tc3,"Whole suite skipped"}},
+ {user_skip_1_SUITE,{tc3,g1},"Whole suite skipped"}},
{?eh,test_stats,{0,0,{3,0}}},
{?eh,tc_user_skip,
{user_skip_1_SUITE,tc4,"Whole suite skipped"}},
@@ -638,9 +638,9 @@ test_events(user_skip) ->
[{?eh,tc_start,{user_skip_4_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{user_skip_4_SUITE,{init_per_group,g1,[]},{skipped,"Group skipped"}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc1,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc1,g1},"Group skipped"}},
{?eh,test_stats,{3,0,{10,0}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc2,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc2,g1},"Group skipped"}},
{?eh,test_stats,{3,0,{11,0}}},
{?eh,tc_user_skip,{user_skip_4_SUITE,{end_per_group,g1},"Group skipped"}}],
@@ -657,10 +657,10 @@ test_events(user_skip) ->
[{?eh,tc_start,{user_skip_4_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,{user_skip_4_SUITE,{init_per_group,g3,[]},{skipped,"Group skipped"}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc5,"Group skipped"}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc6,"Group skipped"}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc7,"Group skipped"}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc8,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc5,g3},"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc6,g4},"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc7,g4},"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc8,g3},"Group skipped"}},
{?eh,test_stats,{5,0,{15,0}}},
{?eh,tc_user_skip,{user_skip_4_SUITE,{end_per_group,g3},"Group skipped"}}],
@@ -671,9 +671,9 @@ test_events(user_skip) ->
{?eh,test_stats,{6,0,{15,0}}},
[{?eh,tc_start,{user_skip_4_SUITE,{init_per_group,g6,[]}}},
{?eh,tc_done,{user_skip_4_SUITE,{init_per_group,g6,[]},{skipped,"Group skipped"}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc10,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc10,g6},"Group skipped"}},
{?eh,test_stats,{6,0,{16,0}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc11,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc11,g6},"Group skipped"}},
{?eh,test_stats,{6,0,{17,0}}},
{?eh,tc_user_skip,{user_skip_4_SUITE,{end_per_group,g6},"Group skipped"}}],
{?eh,tc_start,{user_skip_4_SUITE,{end_per_group,g5,[]}}},
@@ -687,9 +687,9 @@ test_events(user_skip) ->
{skipped,{bad,'Whole suite skipped'}}}},
{?eh,tc_user_skip,{user_skip_5_SUITE,tc1,{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{18,0}}},
- {?eh,tc_user_skip,{user_skip_5_SUITE,tc2,{bad,'Whole suite skipped'}}},
+ {?eh,tc_user_skip,{user_skip_5_SUITE,{tc2,g1},{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{19,0}}},
- {?eh,tc_user_skip,{user_skip_5_SUITE,tc3,{bad,'Whole suite skipped'}}},
+ {?eh,tc_user_skip,{user_skip_5_SUITE,{tc3,g1},{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{20,0}}},
{?eh,tc_user_skip,{user_skip_5_SUITE,tc4,{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{21,0}}},
@@ -700,10 +700,10 @@ test_events(user_skip) ->
{?eh,tc_done,{user_skip_6_SUITE,
{init_per_group,ptop1,[parallel]},
{skipped,"Top group skipped"}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc1,"Top group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc3,"Top group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc4,"Top group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc2,"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc1,ptop1},"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc3,psub1},"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc4,psub1},"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc2,ptop1},"Top group skipped"}},
{?eh,tc_user_skip,{user_skip_6_SUITE,{end_per_group,ptop1},
"Top group skipped"}}]},
@@ -718,8 +718,8 @@ test_events(user_skip) ->
{?eh,tc_done,{user_skip_6_SUITE,
{init_per_group,psub2,[parallel]},
{skipped,"Sub group skipped"}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc3,"Sub group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc4,"Sub group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc3,psub2},"Sub group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc4,psub2},"Sub group skipped"}},
{?eh,tc_user_skip,{user_skip_6_SUITE,{end_per_group,psub2},
"Sub group skipped"}}]},
@@ -745,14 +745,14 @@ test_events(testspec_skip) ->
{user_skip_7_SUITE,{init_per_group,ptop1,[parallel]}}},
{?eh,tc_done,
{user_skip_7_SUITE,{init_per_group,ptop1,[parallel]},ok}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc1,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc1,ptop1},"SKIPPED"}},
{?eh,test_stats,{0,0,{1,0}}},
{parallel,
[{?eh,tc_start,
{user_skip_7_SUITE,{init_per_group,psub1,[parallel]}}},
{?eh,tc_done,
{user_skip_7_SUITE,{init_per_group,psub1,[parallel]},ok}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc3,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc3,psub1},"SKIPPED"}},
{?eh,tc_start,{user_skip_7_SUITE,tc4}},
{?eh,tc_done,{user_skip_7_SUITE,tc4,ok}},
{?eh,test_stats,{1,0,{2,0}}},
@@ -778,13 +778,13 @@ test_events(testspec_skip) ->
{?eh,tc_start,{ct_framework,init_per_suite}},
{?eh,tc_done,{ct_framework,init_per_suite,ok}},
{?eh,tc_user_skip,{user_skip_7_SUITE,{init_per_group,ptop1},"SKIPPED"}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc1,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc1,ptop1},"SKIPPED"}},
{?eh,test_stats,{0,0,{1,0}}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc3,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc3,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{2,0}}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc4,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc4,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{3,0}}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc2,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc2,ptop1},"SKIPPED"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,{user_skip_7_SUITE,{end_per_group,ptop1},"SKIPPED"}},
{?eh,tc_start,{ct_framework,end_per_suite}},
@@ -804,8 +804,8 @@ test_events(testspec_skip) ->
{user_skip_7_SUITE,{init_per_group,ptop1,[parallel]},ok}},
{?eh,tc_user_skip,
{user_skip_7_SUITE,{init_per_group,psub1},"SKIPPED"}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc3,"SKIPPED"}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc4,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc3,psub1},"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc4,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,{user_skip_7_SUITE,{end_per_group,psub1},"SKIPPED"}},
{?eh,tc_start,{user_skip_7_SUITE,tc1}},
@@ -837,13 +837,13 @@ test_events(testspec_skip) ->
{?eh,tc_done,{user_skip_6_SUITE,
{init_per_group,ptop1,[parallel]},
{skipped,"Top group skipped"}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc1,"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc1,ptop1},"Top group skipped"}},
{?eh,test_stats,{0,0,{1,0}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc3,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc3,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{2,0}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc4,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc4,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{3,0}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc2,"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc2,ptop1},"Top group skipped"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,
{user_skip_6_SUITE,{end_per_group,ptop1},"Top group skipped"}}]},
diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
index c5e44682b0..db7a0be915 100644
--- a/lib/common_test/test/ct_surefire_SUITE.erl
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -205,7 +205,7 @@ test_events(_) ->
[{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}},
{?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]},
{failed,{error,all_cases_should_be_skipped}}}},
- {?eh,tc_auto_skip,{surefire_SUITE,tc_ok,
+ {?eh,tc_auto_skip,{surefire_SUITE,{tc_ok,g_fail},
{failed,
{surefire_SUITE,init_per_group,
{'EXIT',all_cases_should_be_skipped}}}}},
diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl
index 25debf09d4..f5cff76fd1 100644
--- a/lib/common_test/test/ct_telnet_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE.erl
@@ -72,19 +72,32 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
-init_per_testcase(TestCase, Config) when TestCase=/=unix_telnet->
+init_per_testcase(TestCase, Config) when TestCase /= unix_telnet ->
+ ct:pal("Testcase ~p starting!", [TestCase]),
TS = telnet_server:start([{port,?erl_telnet_server_port},
{users,[{?erl_telnet_server_user,
?erl_telnet_server_pwd}]}]),
ct_test_support:init_per_testcase(TestCase, [{telnet_server,TS}|Config]);
init_per_testcase(TestCase, Config) ->
- ct_test_support:init_per_testcase(TestCase, Config).
-
+ ct:pal("Testcase ~p starting. Checking connection to telnet server...",
+ [TestCase]),
+ ct:require(testconn, {unix,[telnet]}),
+ case {os:type(),ct_telnet:open(testconn)} of
+ {_,{ok,Handle}} ->
+ ok = ct_telnet:close(Handle),
+ ct:pal("Connection ok, starting tests!", []),
+ ct_test_support:init_per_testcase(TestCase, Config);
+ {{unix,_},{error,Reason}} ->
+ ct:fail("No connection to telnet server! Reason: ~tp", [Reason]);
+ {_,{error,Reason}} ->
+ {skip,{no_access_to_telnet_server,Reason}}
+ end.
+
+end_per_testcase(TestCase, Config) when TestCase /= unix_telnet ->
+ ct:pal("Stopping the telnet_server now!", []),
+ telnet_server:stop(?config(telnet_server,Config)),
+ ct_test_support:end_per_testcase(TestCase, Config);
end_per_testcase(TestCase, Config) ->
- case ?config(telnet_server,Config) of
- undefined -> ok;
- TS -> telnet_server:stop(TS)
- end,
ct_test_support:end_per_testcase(TestCase, Config).
@@ -179,7 +192,12 @@ telnet_config(_, LogType) ->
{port, ?erl_telnet_server_port},
{username,?erl_telnet_server_user},
{password,?erl_telnet_server_pwd},
- {keep_alive,true}]} |
+ {keep_alive,true}]},
+ {telnet_settings, [{connect_timeout,10000},
+ {command_timeout,10000},
+ {reconnection_attempts,0},
+ {reconnection_interval,0},
+ {keep_alive,true}]} |
if LogType == legacy ->
[{ct_conn_log,[]}];
true ->
@@ -215,23 +233,18 @@ all_cases(Suite,Config) ->
fun({group,G}) ->
{value,{G,Props,GTCs}} =
lists:keysearch(G,1,Suite:groups()),
- GTCs1 = case lists:member(parallel,Props) of
- true ->
- %%! TEMPORARY WORKAROUND FOR PROBLEM
- %%! WITH ct_test_support NOT HANDLING
- %%! VERIFICATION OF PARALLEL GROUPS
- %%! CORRECTLY!
- [];
- false ->
- [[{?eh,tc_start,{Suite,GTC}},
- {?eh,tc_done,{Suite,GTC,ok}}] ||
- GTC <- GTCs]
- end,
- [{?eh,tc_start,{Suite,{init_per_group,G,Props}}},
- {?eh,tc_done,{Suite,{init_per_group,G,Props},ok}} |
- GTCs1] ++
- [{?eh,tc_start,{Suite,{end_per_group,G,Props}}},
- {?eh,tc_done,{Suite,{end_per_group,G,Props},ok}}];
+ GTCs1 = [[{?eh,tc_start,{Suite,GTC}},
+ {?eh,tc_done,{Suite,GTC,ok}}] ||
+ GTC <- GTCs],
+ GEvs = [{?eh,tc_start,{Suite,{init_per_group,G,Props}}},
+ {?eh,tc_done,{Suite,{init_per_group,G,Props},ok}} |
+ GTCs1] ++
+ [{?eh,tc_start,{Suite,{end_per_group,G,Props}}},
+ {?eh,tc_done,{Suite,{end_per_group,G,Props},ok}}],
+ case lists:member(parallel, Props) of
+ true -> [{parallel,GEvs}];
+ false -> GEvs
+ end;
(TC) ->
[{?eh,tc_done,{Suite,TC,ok}}]
end, GroupsAndTCs),
diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
index 8d142e85a8..0ee0525216 100644
--- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
@@ -20,7 +20,9 @@ all() ->
expect_repeat,
expect_sequence,
expect_error_prompt,
- expect_error_timeout,
+ expect_error_timeout1,
+ expect_error_timeout2,
+ expect_error_timeout3,
no_prompt_check,
no_prompt_check_repeat,
no_prompt_check_sequence,
@@ -28,7 +30,9 @@ all() ->
ignore_prompt,
ignore_prompt_repeat,
ignore_prompt_sequence,
- ignore_prompt_timeout].
+ ignore_prompt_timeout,
+ server_speaks,
+ server_disconnects].
groups() ->
[].
@@ -85,13 +89,34 @@ expect_error_prompt(_) ->
%% Check that expect returns after idle timeout, and even if the
%% expected pattern is received - as long as not newline or prompt is
%% received it will not match.
-expect_error_timeout(_) ->
+expect_error_timeout1(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
{error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{timeout,1000}]),
ok = ct_telnet:close(Handle),
ok.
+expect_error_timeout2(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
+ {error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{idle_timeout,1000},
+ {total_timeout,infinity}]),
+ ok = ct_telnet:close(Handle),
+ ok.
+
+%% Check that if server loops and pattern not matching, the operation
+%% can be aborted
+expect_error_timeout3(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo_loop 5000 xxx"),
+ {error,timeout} = ct_telnet:expect(Handle, ["yyy"],
+ [{idle_timeout,infinity},
+ {total_timeout,3000}]),
+ ok = ct_telnet:send(Handle, "echo ayt"),
+ {ok,["ayt"]} = ct_telnet:expect(Handle, ["ayt"]),
+ ok = ct_telnet:close(Handle),
+ ok.
+
%% expect with ignore_prompt option should not return even if a prompt
%% is found. The pattern after the prompt (here "> ") can be matched.
ignore_prompt(_) ->
@@ -188,3 +213,37 @@ no_prompt_check_timeout(_) ->
{timeout,1000}]),
ok = ct_telnet:close(Handle),
ok.
+
+%% The server says things. Manually check that it gets printed correctly
+%% in the general IO log.
+server_speaks(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the first message\r\n"),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the second message\r\n"),
+ %% let ct_telnet_client get an idle timeout
+ timer:sleep(15000),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the third message\r\n"),
+ {ok,_} = ct_telnet:expect(Handle, ["the"], [no_prompt_check]),
+ {error,timeout} = ct_telnet:expect(Handle, ["the"], [no_prompt_check,
+ {timeout,1000}]),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the fourth message\r\n"),
+ %% give the server time to respond
+ timer:sleep(2000),
+ %% closing the connection should print last message in log
+ ok = ct_telnet:close(Handle),
+ ok.
+
+%% Let the server close the connection. Make sure buffered data gets printed
+%% to the general IO log.
+server_disconnects(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "disconnect_after 1500"),
+ %% wait until the get_data operation (triggered by send/2) times out
+ %% before sending the msg
+ timer:sleep(500),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the message\r\n"),
+ %% when the server closes the connection, the last message should be
+ %% printed in the log
+ timer:sleep(3000),
+ _ = ct_telnet:close(Handle),
+ ok.
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
index 9882fa980c..b6ef3062d4 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
@@ -168,7 +168,7 @@ test_events(ts_if_1) ->
{?eh,tc_start,{ts_if_1_SUITE,tc4}},
{?eh,tc_done,{ts_if_1_SUITE,tc4,{failed,{error,failed_on_purpose}}}},
{?eh,test_stats,{1,3,{0,2}}},
- {?eh,tc_auto_skip,{ts_if_1_SUITE,tc5,{failed,{ts_if_1_SUITE,tc4}}}},
+ {?eh,tc_auto_skip,{ts_if_1_SUITE,{tc5,seq2},{failed,{ts_if_1_SUITE,tc4}}}},
{?eh,test_stats,{1,3,{0,3}}},
{?eh,tc_start,{ts_if_1_SUITE,{end_per_group,seq2,[sequence]}}},
{?eh,tc_done,{ts_if_1_SUITE,{end_per_group,seq2,[sequence]},ok}}],
@@ -199,7 +199,7 @@ test_events(ts_if_1) ->
[{?eh,tc_start,{ts_if_1_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{ts_if_1_SUITE,{init_per_group,g1,[]},
{skipped,g1_got_skipped}}},
- {?eh,tc_user_skip,{ts_if_1_SUITE,gtc1,g1_got_skipped}},
+ {?eh,tc_user_skip,{ts_if_1_SUITE,{gtc1,g1},g1_got_skipped}},
{?eh,test_stats,{1,4,{3,6}}},
{?eh,tc_user_skip,{ts_if_1_SUITE,{end_per_group,g1},g1_got_skipped}}],
@@ -208,7 +208,7 @@ test_events(ts_if_1) ->
{?eh,tc_done,{ts_if_1_SUITE,{init_per_group,g2,[parallel]},ok}},
[{?eh,tc_start,{ts_if_1_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,{ts_if_1_SUITE,{init_per_group,g3,[]},{skipped,g3_got_skipped}}},
- {?eh,tc_user_skip,{ts_if_1_SUITE,gtc2,g3_got_skipped}},
+ {?eh,tc_user_skip,{ts_if_1_SUITE,{gtc2,g3},g3_got_skipped}},
{?eh,test_stats,{1,4,{4,6}}},
{?eh,tc_user_skip,{ts_if_1_SUITE,{end_per_group,g3},g3_got_skipped}}],
{?eh,tc_start,{ts_if_1_SUITE,{end_per_group,g2,[parallel]}}},
@@ -279,7 +279,7 @@ test_events(ts_if_1) ->
{init_per_group,g1,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
{?eh,tc_auto_skip,
- {ts_if_7_SUITE,tc2,{group0_failed,bad_return_value}}},
+ {ts_if_7_SUITE,{tc2,g1},{group0_failed,bad_return_value}}},
{?eh,test_stats,{2,7,{4,11}}},
{?eh,tc_auto_skip,
{ts_if_7_SUITE,{end_per_group,g1},{group0_failed,bad_return_value}}},
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 772274ce7e..2e2b45d59f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -40,6 +40,8 @@
-export([ct_test_halt/1, ct_rpc/2]).
+-export([random_error/1]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
@@ -110,7 +112,6 @@ start_slave(NodeName, Config, Level) ->
_ ->
ok
end,
-
TraceFile = filename:join(DataDir, "ct.trace"),
case file:read_file_info(TraceFile) of
{ok,_} ->
@@ -395,6 +396,55 @@ ct_rpc({M,F,A}, Config) ->
%%%-----------------------------------------------------------------
+%%% random_error/1
+random_error(Config) when is_list(Config) ->
+ random:seed(now()),
+ Gen = fun(0,_) -> ok; (N,Fun) -> Fun(N-1, Fun) end,
+ Gen(random:uniform(100), Gen),
+
+ ErrorTypes = ['BADMATCH','BADARG','CASE_CLAUSE','FUNCTION_CLAUSE',
+ 'EXIT','THROW','UNDEF'],
+ Type = lists:nth(random:uniform(length(ErrorTypes)), ErrorTypes),
+ Where = case random:uniform(2) of
+ 1 ->
+ io:format("ct_test_support *returning* error of type ~w",
+ [Type]),
+ tc;
+ 2 ->
+ io:format("ct_test_support *generating* error of type ~w",
+ [Type]),
+ lib
+ end,
+ ErrorFun =
+ fun() ->
+ case Type of
+ 'BADMATCH' ->
+ ok = proplists:get_value(undefined, Config);
+ 'BADARG' ->
+ size(proplists:get_value(priv_dir, Config));
+ 'FUNCTION_CLAUSE' ->
+ random_error(x);
+ 'EXIT' ->
+ spawn_link(fun() ->
+ undef_proc ! hello,
+ ok
+ end);
+ 'THROW' ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ if is_list(PrivDir) -> throw(generated_throw) end;
+ 'UNDEF' ->
+ apply(?MODULE, random_error, [])
+ end
+ end,
+ %% either call the fun here or return it to the caller (to be
+ %% executed in a test case instead)
+ case Where of
+ tc -> ErrorFun;
+ lib -> ErrorFun()
+ end.
+
+
+%%%-----------------------------------------------------------------
%%% EVENT HANDLING
handle_event(EH, Event) ->
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index 187b5e6d3a..c2670316b6 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -763,35 +763,35 @@ test_events(skip_all_groups) ->
{?eh,start_info,{1,1,12}},
{?eh,tc_start,{groups_11_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1a},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{0,0,{1,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1a},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1b},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{0,0,{3,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_2},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2a,test_group_2},"SKIPPED!"}},
{?eh,test_stats,{0,0,{5,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3a,test_group_3},"SKIPPED!"}},
{?eh,test_stats,{0,0,{6,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3b,test_group_3},"SKIPPED!"}},
{?eh,test_stats,{0,0,{7,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2b,test_group_2},"SKIPPED!"}},
{?eh,test_stats,{0,0,{8,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_2},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_4},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_5a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{9,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_7a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{0,0,{10,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_7b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{0,0,{11,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{12,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_4},"SKIPPED!"}},
{?eh,tc_start,{groups_11_SUITE,end_per_suite}},
@@ -826,17 +826,17 @@ test_events(skip_group) ->
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},ok}}],
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1b},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{2,0,{2,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},
"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_2},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2a,test_group_2},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3a,test_group_3},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3b,test_group_3},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2b,test_group_2},"SKIPPED!"}},
{?eh,test_stats,{2,0,{6,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_2},
"SKIPPED!"}},
@@ -864,15 +864,15 @@ test_events(skip_group_all_testcases) ->
{?eh,tc_start,{groups_11_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1a},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1a},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1a},
"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1b},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},
"SKIPPED!"}},
@@ -901,13 +901,13 @@ test_events(skip_group_testcase) ->
{?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1a,[]}}},
{?eh,tc_start,{groups_11_SUITE,testcase_1a}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{1,0,{1,0}}},
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},'_'}},
{?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1b,[]}}},
{?eh,tc_start,{groups_11_SUITE,testcase_1b}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{2,0,{2,0}}},
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1b,[]},'_'}},
@@ -1045,8 +1045,8 @@ test_events(skip_subgroup) ->
{?eh,tc_user_skip,{groups_12_SUITE,
{init_per_group,test_group_8},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_8},"SKIPPED!"}},
@@ -1146,12 +1146,12 @@ test_events(skip_subgroup_all_testcases) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_4,[]},ok}},
{?eh,tc_user_skip,{groups_12_SUITE,
{init_per_group,test_group_5},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{6,0}}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_5},"SKIPPED!"}},
@@ -1240,9 +1240,9 @@ test_events(skip_subgroup_testcase) ->
{?eh,tc_done,
{groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
- {?eh,tc_user_skip, {groups_12_SUITE,testcase_7a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{1,0,{1,0}}},
- {?eh,tc_user_skip, {groups_12_SUITE,testcase_7b,"SKIPPED!"}},
+ {?eh,tc_user_skip, {groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{1,0,{2,0}}},
{?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
{shuffle,
@@ -1281,22 +1281,22 @@ test_events(sub_skipped_by_top) ->
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_12_SUITE,{init_per_group,test_group_4},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_4},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_12_SUITE,
{init_per_group,test_group_4},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{12,0}}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_4},"SKIPPED!"}},
diff --git a/lib/common_test/test/telnet_server.erl b/lib/common_test/test/telnet_server.erl
index 1760100d8e..ae56787819 100644
--- a/lib/common_test/test/telnet_server.erl
+++ b/lib/common_test/test/telnet_server.erl
@@ -51,32 +51,51 @@ stop(Pid) ->
init(Opts) ->
Port = proplists:get_value(port,Opts),
Users = proplists:get_value(users,Opts,[]),
- {ok, LSock} = gen_tcp:listen(Port, [list, {packet, 0},
- {active, true}]),
+ {ok, LSock} = listen(5, Port, [list, {packet, 0},
+ {active, true},
+ {reuseaddr,true}]),
State = #state{listen=LSock,users=Users},
accept(State),
- ok = gen_tcp:close(LSock).
+ ok = gen_tcp:close(LSock),
+ dbg("telnet_server closed the listen socket ~p\n", [LSock]),
+ timer:sleep(1000),
+ ok.
+
+listen(0, _Port, _Opts) ->
+ {error,eaddrinuse};
+listen(Retries, Port, Opts) ->
+ case gen_tcp:listen(Port, Opts) of
+ {error,eaddrinuse} ->
+ dbg("Listen port not released, trying again..."),
+ timer:sleep(5000),
+ listen(Retries-1, Port, Opts);
+ Ok = {ok,_LSock} ->
+ Ok;
+ Error ->
+ exit(Error)
+ end.
accept(#state{listen=LSock}=State) ->
Server = self(),
Acceptor = spawn_link(fun() -> do_accept(LSock,Server) end),
receive
{Acceptor,Sock} when is_port(Sock) ->
+ dbg("Connected to client on socket ~p\n", [Sock]),
case init_client(State#state{client=Sock}) of
stopped ->
- io:format("[telnet_server] telnet_server stopped\n"),
+ dbg("telnet_server stopped\n"),
ok;
R ->
- io:format("[telnet_server] connection to client"
- "closed with reason ~p~n",[R]),
+ dbg("Connection to client "
+ "closed with reason ~p~n",[R]),
accept(State)
end;
{Acceptor,closed} ->
- io:format("[telnet_server] listen socket closed unexpectedly, "
- "terminating telnet_server\n"),
+ dbg("Listen socket closed unexpectedly, "
+ "terminating telnet_server\n"),
ok;
stop ->
- io:format("[telnet_server] telnet_server stopped\n"),
+ dbg("telnet_server stopped\n"),
ok
end.
@@ -97,19 +116,21 @@ init_client(#state{client=Sock}=State) ->
dbg("Server sending: ~p~n",["login: "]),
R = case gen_tcp:send(Sock,"login: ") of
ok ->
- loop(State);
+ loop(State, 1);
Error ->
Error
end,
_ = gen_tcp:close(Sock),
R.
-loop(State) ->
+loop(State, N) ->
receive
{tcp,_,Data} ->
try handle_data(Data,State) of
{ok,State1} ->
- loop(State1)
+ loop(State1, N);
+ closed ->
+ closed
catch
throw:Error ->
Error
@@ -118,6 +139,11 @@ loop(State) ->
closed;
{tcp_error,_,Error} ->
{error,tcp,Error};
+ disconnect ->
+ Sock = State#state.client,
+ dbg("Server closing connection on socket ~p~n", [Sock]),
+ ok = gen_tcp:close(Sock),
+ closed;
stop ->
stopped
end.
@@ -130,10 +156,16 @@ handle_data(Data,State) ->
case get_line(Data,[]) of
{Line,Rest} ->
WholeLine = lists:flatten(lists:reverse(State#state.buffer,Line)),
- {ok,State1} = do_handle_data(WholeLine,State),
- case Rest of
- [] -> {ok,State1};
- _ -> handle_data(Rest,State1)
+ case do_handle_data(WholeLine,State) of
+ {ok,State1} ->
+ case Rest of
+ [] -> {ok,State1};
+ _ -> handle_data(Rest,State1)
+ end;
+ {close,State1} ->
+ dbg("Server closing connection~n",[]),
+ gen_tcp:close(State1#state.client),
+ closed
end;
false ->
{ok,State#state{buffer=[Data|State#state.buffer]}}
@@ -163,22 +195,34 @@ do_handle_data(Data,#state{authorized=false}=State) ->
check_user(Data,State);
do_handle_data(Data,#state{authorized={user,_}}=State) ->
check_pwd(Data,State);
-do_handle_data("echo "++ Data,State) ->
+do_handle_data("echo " ++ Data,State) ->
send(Data++"\r\n> ",State),
{ok,State};
-do_handle_data("echo_no_prompt "++ Data,State) ->
+do_handle_data("echo_no_prompt " ++ Data,State) ->
send(Data,State),
{ok,State};
-do_handle_data("echo_ml "++ Data,State) ->
+do_handle_data("echo_ml " ++ Data,State) ->
Lines = string:tokens(Data," "),
ReturnData = string:join(Lines,"\n"),
send(ReturnData++"\r\n> ",State),
{ok,State};
-do_handle_data("echo_ml_no_prompt "++ Data,State) ->
+do_handle_data("echo_ml_no_prompt " ++ Data,State) ->
Lines = string:tokens(Data," "),
ReturnData = string:join(Lines,"\n"),
send(ReturnData,State),
{ok,State};
+do_handle_data("echo_loop " ++ Data,State) ->
+ [TStr|Lines] = string:tokens(Data," "),
+ ReturnData = string:join(Lines,"\n"),
+ send_loop(list_to_integer(TStr),ReturnData,State),
+ {ok,State};
+do_handle_data("disconnect_after " ++WaitStr,State) ->
+ Wait = list_to_integer(string:strip(WaitStr,right,$\n)),
+ dbg("Server will close connection in ~w ms...", [Wait]),
+ erlang:send_after(Wait,self(),disconnect),
+ {ok,State};
+do_handle_data("disconnect" ++_,State) ->
+ {close,State};
do_handle_data([],State) ->
send("> ",State),
{ok,State};
@@ -212,6 +256,20 @@ send(Data,State) ->
throw({error,send,Error})
end.
+send_loop(T,Data,State) ->
+ dbg("Server sending ~p in loop for ~w ms...~n",[Data,T]),
+ send_loop(now(),T,Data,State).
+
+send_loop(T0,T,Data,State) ->
+ ElapsedMS = trunc(timer:now_diff(now(),T0)/1000),
+ if ElapsedMS >= T ->
+ ok;
+ true ->
+ send(Data,State),
+ timer:sleep(500),
+ send_loop(T0,T,Data,State)
+ end.
+
get_line([$\r,$\n|Rest],Acc) ->
{lists:reverse(Acc),Rest};
get_line([$\r,0|Rest],Acc) ->
@@ -226,4 +284,4 @@ get_line([],_) ->
dbg(_F) ->
dbg(_F,[]).
dbg(_F,_A) ->
- io:format("[telnet_server] "++_F,_A).
+ io:format("[telnet_server] " ++ _F,_A).
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 568405b110..f8a5aab686 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.7.4
+COMMON_TEST_VSN = 1.8
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 1459f696a0..5fccdcdcb5 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -408,6 +408,11 @@ module.beam: module.erl \
<code>-compile({no_auto_import,[error/1]}).</code>
</item>
+ <tag><c>no_auto_import</c></tag>
+ <item>
+ <p>Do not auto import any functions from the module <c>erlang</c>.</p>
+ </item>
+
<tag><c>no_line_info</c></tag>
<item>
@@ -546,6 +551,14 @@ module.beam: module.erl \
<c>{Module,Name,Arity}</c> or a list of such tuples.</p>
</item>
+ <tag><c>nowarn_deprecated_type</c></tag>
+ <item>
+ <p>Turns off warnings for uses of deprecated types. By
+ default (<c>warn_deprecated_type</c>), warnings are
+ emitted for every use of a type known by the compiler
+ to be deprecated.</p>
+ </item>
+
<tag><c>warn_obsolete_guard</c></tag>
<item>
<p>Causes warnings to be emitted for calls to old type
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index 3dfa67a771..fe4f473846 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -92,6 +92,8 @@ rename_instr({put_map_assoc,Fail,S,D,R,L}) ->
{put_map,Fail,assoc,S,D,R,L};
rename_instr({put_map_exact,Fail,S,D,R,L}) ->
{put_map,Fail,exact,S,D,R,L};
+rename_instr({test,has_map_fields,Fail,Src,{list,List}}) ->
+ {test,has_map_fields,Fail,[Src|List]};
rename_instr({select_val=I,Reg,Fail,{list,List}}) ->
{select,I,Reg,Fail,List};
rename_instr({select_tuple_arity=I,Reg,Fail,{list,List}}) ->
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 112b087f3c..f8cf178d2e 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -324,6 +324,8 @@ make_op({gc_bif,Bif,Fail,Live,Args,Dest}, Dict) ->
encode_op(BifOp, [Fail,Live,{extfunc,erlang,Bif,Arity}|Args++[Dest]],Dict);
make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) ->
encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict);
+make_op({test,Cond,Fail,Src,{list,_}=Ops}, Dict) ->
+ encode_op(Cond, [Fail,Src,Ops], Dict);
make_op({test,Cond,Fail,Ops}, Dict) when is_list(Ops) ->
encode_op(Cond, [Fail|Ops], Dict);
make_op({test,Cond,Fail,Live,[Op|Ops],Dst}, Dict) when is_list(Ops) ->
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 3723cc19e1..7a30c68593 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -154,8 +154,8 @@ collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
collect(remove_message) -> {set,[],[],remove_message};
collect({put_map,F,Op,S,D,R,{list,Puts}}) ->
{set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}};
-collect({get_map_element,F,S,K,D}) ->
- {set,[D],[S],{get_map_element,K,F}};
+collect({get_map_elements,F,S,{list,Gets}}) ->
+ {set,Gets,[S],{get_map_elements,F}};
collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
collect(fclearerror) -> {set,[],[],fclearerror};
collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror};
@@ -240,7 +240,7 @@ move_allocates_2(Alloc, [], Acc) ->
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
-alloc_may_pass({set,_,_,{get_map_element,_,_}}) -> false;
+alloc_may_pass({set,_,_,{get_map_elements,_}}) -> false;
alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
@@ -291,7 +291,11 @@ opt_moves([X0,Y0], Is0) ->
not_possible -> {[X,Y0],Is2};
{X,_} -> {[X,Y0],Is2};
{Y,Is} -> {[X,Y],Is}
- end.
+ end;
+opt_moves(Ds, Is) ->
+ %% multiple destinations -> pass through
+ {Ds,Is}.
+
%% opt_move(Dest, [Instruction]) -> {UpdatedDest,[Instruction]} | not_possible
%% If there is a {move,Dest,FinalDest} instruction
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index 124abd13c1..5a4621dc37 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -318,6 +318,8 @@ split_block_label_used([{set,[_],_,{bif,_,{f,Fail}}}|_], Fail) ->
true;
split_block_label_used([{set,[_],_,{alloc,_,{gc_bif,_,{f,Fail}}}}|_], Fail) ->
true;
+split_block_label_used([{set,[_],_,{alloc,_,{put_map,_,{f,Fail}}}}|_], Fail) ->
+ true;
split_block_label_used([_|Is], Fail) ->
split_block_label_used(Is, Fail);
split_block_label_used([], _) -> false.
@@ -391,10 +393,14 @@ bopt_tree([{set,_,_,{bif,'xor',_}}|_], _, _) ->
throw('xor');
bopt_tree([{protected,[Dst],Code,_}|Is], Forest0, Pre) ->
ProtForest0 = gb_trees:from_orddict([P || {_,any}=P <- gb_trees:to_list(Forest0)]),
- {ProtPre,[{_,ProtTree}]} = bopt_tree(Code, ProtForest0, []),
- Prot = {prot,ProtPre,ProtTree},
- Forest = gb_trees:enter(Dst, Prot, Forest0),
- bopt_tree(Is, Forest, Pre);
+ case bopt_tree(Code, ProtForest0, []) of
+ {ProtPre,[{_,ProtTree}]} ->
+ Prot = {prot,ProtPre,ProtTree},
+ Forest = gb_trees:enter(Dst, Prot, Forest0),
+ bopt_tree(Is, Forest, Pre);
+ _Res ->
+ throw(not_boolean_expr)
+ end;
bopt_tree([{set,[Dst],[Src],move}=Move|Is], Forest, Pre) ->
case {Src,Dst} of
{{tmp,_},_} -> throw(move);
@@ -432,9 +438,10 @@ bopt_bool_args(As, Forest) ->
mapfoldl(fun bopt_bool_arg/2, Forest, As).
bopt_bool_arg({T,_}=R, Forest) when T =:= x; T =:= y; T =:= tmp ->
- Val = case gb_trees:get(R, Forest) of
- any -> {test,is_eq_exact,fail,[R,{atom,true}]};
- Val0 -> Val0
+ Val = case gb_trees:lookup(R, Forest) of
+ {value,any} -> {test,is_eq_exact,fail,[R,{atom,true}]};
+ {value,Val0} -> Val0;
+ none -> throw(mixed)
end,
{Val,gb_trees:delete(R, Forest)};
bopt_bool_arg(Term, Forest) ->
@@ -525,7 +532,9 @@ bopt_cg({prot,Pre0,Tree}, Fail, Rs0, Acc, St0) ->
bopt_cg({atom,true}, _Fail, _Rs, Acc, St) ->
{Acc,St};
bopt_cg({atom,false}, Fail, _Rs, Acc, St) ->
- {[{jump,{f,Fail}}|Acc],St}.
+ {[{jump,{f,Fail}}|Acc],St};
+bopt_cg(_, _, _, _, _) ->
+ throw(not_boolean_expr).
bopt_cg_not({'and',As0}) ->
As = [bopt_cg_not(A) || A <- As0],
@@ -538,7 +547,9 @@ bopt_cg_not({'not',Arg}) ->
bopt_cg_not({test,Test,Fail,As}) ->
{inverted_test,Test,Fail,As};
bopt_cg_not({atom,Bool}) when is_boolean(Bool) ->
- {atom,not Bool}.
+ {atom,not Bool};
+bopt_cg_not(_) ->
+ throw(not_boolean_expr).
bopt_cg_not_not({'and',As}) ->
{'and',[bopt_cg_not_not(A) || A <- As]};
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index fdfcb08125..d54c2a9fde 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -209,6 +209,7 @@ btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->
btb_call(Arity+2, apply, Regs, Is, D);
btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) ->
+ btb_ensure_not_used([{x,Live}], I, Regs),
btb_call(Live, I, Regs, Is, D);
btb_reaches_match_2([{make_fun2,_,_,_,Live}|Is], Regs, D) ->
btb_call(Live, make_fun2, Regs, Is, D);
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 55f985ad0e..b653998252 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -262,8 +262,8 @@ replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D)
when Lbl =/= 0 ->
replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D);
-replace([{get_map_element=I,{f,Lbl},Src,Key,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},Src,Key,Dst}|Acc], D);
+replace([{get_map_elements=I,{f,Lbl},Src,List}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Src,List}|Acc], D);
replace([I|Is], Acc, D) ->
replace(Is, [I|Acc], D);
replace([], Acc, _) -> Acc.
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index 212b9fb03a..ea51673fa3 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -29,16 +29,24 @@
-type label() :: non_neg_integer().
+-type index() :: non_neg_integer().
+
+-type atom_tab() :: gb_trees:tree(atom(), index()).
+-type import_tab() :: gb_trees:tree(mfa(), index()).
+-type fname_tab() :: gb_trees:tree(Name :: term(), index()).
+-type line_tab() :: gb_trees:tree({Fname :: index(), Line :: term()}, index()).
+-type literal_tab() :: dict:dict(Literal :: term(), index()).
+
-record(asm,
- {atoms = gb_trees:empty() :: gb_tree(), %{Atom,Index}
+ {atoms = gb_trees:empty() :: atom_tab(),
exports = [] :: [{label(), arity(), label()}],
locals = [] :: [{label(), arity(), label()}],
- imports = gb_trees:empty() :: gb_tree(), %{{M,F,A},Index}
+ imports = gb_trees:empty() :: import_tab(),
strings = <<>> :: binary(), %String pool
lambdas = [], %[{...}]
- literals = dict:new() :: dict(), %Format: {Literal,Number}
- fnames = gb_trees:empty() :: gb_tree(), %{Name,Index}
- lines = gb_trees:empty() :: gb_tree(), %{{Fname,Line},Index}
+ literals = dict:new() :: literal_tab(),
+ fnames = gb_trees:empty() :: fname_tab(),
+ lines = gb_trees:empty() :: line_tab(),
num_lines = 0 :: non_neg_integer(), %Number of line instructions
next_import = 0 :: non_neg_integer(),
string_offset = 0 :: non_neg_integer(),
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index e0d0d0fd1d..c45596f236 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. 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
@@ -37,7 +37,8 @@
%%-----------------------------------------------------------------------
--type literals() :: 'none' | gb_tree().
+-type index() :: non_neg_integer().
+-type literals() :: 'none' | gb_trees:tree(index(), term()).
-type symbolic_tag() :: 'a' | 'f' | 'h' | 'i' | 'u' | 'x' | 'y' | 'z'.
-type disasm_tag() :: symbolic_tag() | 'fr' | 'atom' | 'float' | 'literal'.
-type disasm_term() :: 'nil' | {disasm_tag(), _}.
@@ -216,7 +217,8 @@ optional_chunk(F, ChunkTag) ->
%%-----------------------------------------------------------------------
-type l_info() :: {non_neg_integer(), {_,_,_,_,_,_}}.
--spec beam_disasm_lambdas('none' | binary(), gb_tree()) -> 'none' | [l_info()].
+-spec beam_disasm_lambdas('none' | binary(), gb_trees:tree(index(), _)) ->
+ 'none' | [l_info()].
beam_disasm_lambdas(none, _) -> none;
beam_disasm_lambdas(<<_:32,Tab/binary>>, Atoms) ->
@@ -366,9 +368,13 @@ disasm_instr(B, Bs, Atoms, Literals) ->
select_tuple_arity ->
disasm_select_inst(select_tuple_arity, Bs, Atoms, Literals);
put_map_assoc ->
- disasm_map_inst(put_map_assoc, Bs, Atoms, Literals);
+ disasm_map_inst(put_map_assoc, Arity, Bs, Atoms, Literals);
put_map_exact ->
- disasm_map_inst(put_map_exact, Bs, Atoms, Literals);
+ disasm_map_inst(put_map_exact, Arity, Bs, Atoms, Literals);
+ get_map_elements ->
+ disasm_map_inst(get_map_elements, Arity, Bs, Atoms, Literals);
+ has_map_fields ->
+ disasm_map_inst(has_map_fields, Arity, Bs, Atoms, Literals);
_ ->
try decode_n_args(Arity, Bs, Atoms, Literals) of
{Args, RestBs} ->
@@ -399,16 +405,15 @@ disasm_select_inst(Inst, Bs, Atoms, Literals) ->
{List, RestBs} = decode_n_args(Len, Bs4, Atoms, Literals),
{{Inst, [X,F,{Z,U,List}]}, RestBs}.
-disasm_map_inst(Inst, Bs0, Atoms, Literals) ->
- {F, Bs1} = decode_arg(Bs0, Atoms, Literals),
- {S, Bs2} = decode_arg(Bs1, Atoms, Literals),
- {X, Bs3} = decode_arg(Bs2, Atoms, Literals),
- {N, Bs4} = decode_arg(Bs3, Atoms, Literals),
- {Z, Bs5} = decode_arg(Bs4, Atoms, Literals),
- {U, Bs6} = decode_arg(Bs5, Atoms, Literals),
- {u, Len} = U,
- {List, RestBs} = decode_n_args(Len, Bs6, Atoms, Literals),
- {{Inst, [F,S,X,N,{Z,U,List}]}, RestBs}.
+disasm_map_inst(Inst, Arity, Bs0, Atoms, Literals) ->
+ {Args0,Bs1} = decode_n_args(Arity, Bs0, Atoms, Literals),
+ %% no droplast ..
+ [Z|Args1] = lists:reverse(Args0),
+ Args = lists:reverse(Args1),
+ {U, Bs2} = decode_arg(Bs1, Atoms, Literals),
+ {u, Len} = U,
+ {List, RestBs} = decode_n_args(Len, Bs2, Atoms, Literals),
+ {{Inst, Args ++ [{Z,U,List}]}, RestBs}.
%%-----------------------------------------------------------------------
%% decode_arg([Byte]) -> {Arg, [Byte]}
@@ -432,7 +437,8 @@ decode_arg([B|Bs]) ->
decode_int(Tag, B, Bs)
end.
--spec decode_arg([byte(),...], gb_tree(), literals()) -> {disasm_term(), [byte()]}.
+-spec decode_arg([byte(),...], gb_trees:tree(index(), _), literals()) ->
+ {disasm_term(), [byte()]}.
decode_arg([B|Bs0], Atoms, Literals) ->
Tag = decode_tag(B band 2#111),
@@ -1024,6 +1030,7 @@ resolve_inst({gc_bif2,Args},Imports,_,_) ->
[F,Live,Bif,A1,A2,Reg] = resolve_args(Args),
{extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
{gc_bif,BifName,F,Live,[A1,A2],Reg};
+
%%
%% New instruction in R14, gc_bif with 3 arguments
%%
@@ -1134,25 +1141,27 @@ resolve_inst({line,[Index]},_,_,_) ->
{line,resolve_arg(Index)};
%%
-%% R17A.
+%% 17.0
%%
resolve_inst({put_map_assoc,Args},_,_,_) ->
[FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
List = resolve_args(List0),
{put_map_assoc,FLbl,Src,Dst,N,{list,List}};
-
resolve_inst({put_map_exact,Args},_,_,_) ->
[FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
List = resolve_args(List0),
{put_map_exact,FLbl,Src,Dst,N,{list,List}};
-
-resolve_inst({is_map,Args0},_,_,_) ->
+resolve_inst({is_map=I,Args0},_,_,_) ->
[FLbl|Args] = resolve_args(Args0),
- {test, is_map, FLbl, Args};
-
-resolve_inst({get_map_element,Args},_,_,_) ->
- [FLbl,Src,Key,Dst] = resolve_args(Args),
- {get_map_element,FLbl,Src,Key,Dst};
+ {test,I,FLbl,Args};
+resolve_inst({has_map_fields,Args0},_,_,_) ->
+ [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0,
+ List = resolve_args(List0),
+ {test,has_map_fields,FLbl,Src,{list,List}};
+resolve_inst({get_map_elements,Args0},_,_,_) ->
+ [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0,
+ List = resolve_args(List0),
+ {get_map_elements,FLbl,Src,{list,List}};
%%
%% Catches instructions that are not yet handled.
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 534bc6d954..46835bece1 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -63,8 +63,8 @@ norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
-norm({set,[D],[S],{get_map_element,K,F}}) ->
- {get_map_element,F,S,K,D};
+norm({set,Gets,[S],{get_map_elements,F}}) ->
+ {get_map_elements,F,S,{list,Gets}};
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 1f720b94c3..b952139f2c 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -446,11 +446,13 @@ is_label_used_in_2({set,_,_,Info}, Lbl) ->
case Info of
{bif,_,{f,F}} -> F =:= Lbl;
{alloc,_,{gc_bif,_,{f,F}}} -> F =:= Lbl;
+ {alloc,_,{put_map,_,{f,F}}} -> F =:= Lbl;
{'catch',{f,F}} -> F =:= Lbl;
{alloc,_,_} -> false;
{put_tuple,_} -> false;
{get_tuple_element,_} -> false;
{set_tuple_element,_} -> false;
+ {get_map_elements,{f,F}} -> F =:= Lbl;
{line,_} -> false;
_ when is_atom(Info) -> false
end.
@@ -529,7 +531,7 @@ ulbl({bs_put,Lbl,_,_}, Used) ->
mark_used(Lbl, Used);
ulbl({put_map,Lbl,_Op,_Src,_Dst,_Live,_List}, Used) ->
mark_used(Lbl, Used);
-ulbl({get_map_element,Lbl,_Src,_Key,_Dst}, Used) ->
+ulbl({get_map_elements,Lbl,_Src,_List}, Used) ->
mark_used(Lbl, Used);
ulbl(_, Used) -> Used.
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index 638a4826ea..688bba9a94 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -53,9 +53,9 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}|
make_block(Bl, Acc)]);
-split_block([{set,[D],[S],{get_map_element,K,{f,Lbl}=Fail}}|Is], Bl, Acc)
+split_block([{set,Gets,[S],{get_map_elements,{f,Lbl}=Fail}}|Is], Bl, Acc)
when Lbl =/= 0 ->
- split_block(Is, [], [{get_map_element,Fail,S,K,D}|make_block(Bl, Acc)]);
+ split_block(Is, [], [{get_map_elements,Fail,S,{list,Gets}}|make_block(Bl, Acc)]);
split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) ->
split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]);
split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) ->
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index a3f16cfa8f..8ca368c167 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -185,6 +185,7 @@ is_pure_test({test,is_lt,_,[_,_]}) -> true;
is_pure_test({test,is_nil,_,[_]}) -> true;
is_pure_test({test,is_nonempty_list,_,[_]}) -> true;
is_pure_test({test,test_arity,_,[_,_]}) -> true;
+is_pure_test({test,has_map_fields,_,[_,{list,_}]}) -> true;
is_pure_test({test,Op,_,Ops}) ->
erl_internal:new_type_test(Op, length(Ops)).
@@ -747,6 +748,8 @@ live_opt([{try_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
+live_opt([{wait_timeout,_,nil}=I|Is], Regs, D, Acc) ->
+ live_opt(Is, Regs, D, [I|Acc]);
live_opt([{wait_timeout,_,{Tag,_}}=I|Is], Regs, D, Acc) when Tag =/= x ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{line,_}=I|Is], Regs, D, Acc) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 97f84da08f..9d5563d13b 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -213,9 +213,12 @@ validate_error_1(Error, Module, Name, Ar) ->
{{Module,Name,Ar},
{internal_error,'_',{Error,erlang:get_stacktrace()}}}.
+-type index() :: non_neg_integer().
+-type reg_tab() :: gb_trees:tree(index(), 'none' | {'value', _}).
+
-record(st, %Emulation state
- {x=init_regs(0, term) :: gb_tree(), %x register info.
- y=init_regs(0, initialized) :: gb_tree(), %y register info.
+ {x=init_regs(0, term) :: reg_tab(),%x register info.
+ y=init_regs(0, initialized) :: reg_tab(),%y register info.
f=init_fregs(), %
numy=none, %Number of y registers.
h=0, %Available heap size.
@@ -227,11 +230,16 @@ validate_error_1(Error, Module, Name, Ar) ->
setelem=false %Previous instruction was setelement/3.
}).
+-type label() :: integer().
+-type label_set() :: gb_sets:set(label()).
+-type branched_tab() :: gb_trees:tree(label(), #st{}).
+-type ft_tab() :: gb_trees:tree().
+
-record(vst, %Validator state
{current=none :: #st{} | 'none', %Current state
- branched=gb_trees:empty() :: gb_tree(), %States at jumps
- labels=gb_sets:empty() :: gb_set(), %All defined labels
- ft=gb_trees:empty() :: gb_tree() %Some other functions
+ branched=gb_trees:empty() :: branched_tab(), %States at jumps
+ labels=gb_sets:empty() :: label_set(), %All defined labels
+ ft=gb_trees:empty() :: ft_tab() %Some other functions
% in the module (those that start with bs_start_match2).
}).
@@ -574,6 +582,7 @@ valfun_4({apply,Live}, Vst) ->
valfun_4({apply_last,Live,_}, Vst) ->
tail_call(apply, Live+2, Vst);
valfun_4({call_fun,Live}, Vst) ->
+ validate_src([{x,Live}], Vst),
call('fun', Live+1, Vst);
valfun_4({call,Live,Func}, Vst) ->
call(Func, Live, Vst);
@@ -769,6 +778,10 @@ valfun_4({test,is_nonempty_list,{f,Lbl},[Cons]}, Vst) ->
valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
assert_type(tuple, Tuple, Vst),
set_type_reg({tuple,Sz}, Tuple, branch_state(Lbl, Vst));
+valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
+ validate_src([Src], Vst),
+ assert_strict_literal_termorder(List),
+ branch_state(Lbl, Vst);
valfun_4({test,_Op,{f,Lbl},Src}, Vst) ->
validate_src(Src, Vst),
branch_state(Lbl, Vst);
@@ -870,18 +883,27 @@ valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
verify_put_map(Fail, Src, Dst, Live, List, Vst);
valfun_4({put_map_exact,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
verify_put_map(Fail, Src, Dst, Live, List, Vst);
-valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) ->
- assert_term(Src, Vst0),
- assert_term(Key, Vst0),
- Vst = branch_state(Fail, Vst0),
- set_type_reg(term, Dst, Vst);
+valfun_4({get_map_elements,{f,Fail},Src,{list,List}}, Vst) ->
+ verify_get_map(Fail, Src, List, Vst);
valfun_4(_, _) ->
error(unknown_instruction).
+verify_get_map(Fail, Src, List, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst1 = branch_state(Fail, Vst0),
+ Lits = mmap(fun(L,_R) -> [L] end, List),
+ assert_strict_literal_termorder(Lits),
+ verify_get_map_pair(List,Vst0,Vst1).
+
+verify_get_map_pair([],_,Vst) -> Vst;
+verify_get_map_pair([Src,Dst|Vs],Vst0,Vsti) ->
+ assert_term(Src, Vst0),
+ verify_get_map_pair(Vs,Vst0,set_type_reg(term,Dst,Vsti)).
+
verify_put_map(Fail, Src, Dst, Live, List, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
- [assert_term(Term, Vst0) || Term <- List],
+ foreach(fun (Term) -> assert_term(Term, Vst0) end, List),
assert_term(Src, Vst0),
Vst1 = heap_alloc(0, Vst0),
Vst2 = branch_state(Fail, Vst1),
@@ -908,7 +930,7 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->
branch_state(Fail, Vst).
%%
-%% Special state handling for setelement/3 and the set_tuple_element/3 instruction.
+%% Special state handling for setelement/3 and set_tuple_element/3 instructions.
%% A possibility for garbage collection must not occur between setelement/3 and
%% set_tuple_element/3.
%%
@@ -1098,6 +1120,39 @@ assert_freg_set({fr,Fr}=Freg, #vst{current=#st{f=Fregs}})
end;
assert_freg_set(Fr, _) -> error({bad_source,Fr}).
+%%% Maps
+
+%% ensure that a list of literals has a strict
+%% ascending term order (also meaning unique literals)
+assert_strict_literal_termorder(Ls) ->
+ Vs = lists:map(fun (L) -> get_literal(L) end, Ls),
+ case check_strict_value_termorder(Vs) of
+ true -> ok;
+ false -> error({not_strict_order, Ls})
+ end.
+
+%% usage:
+%% mmap(fun(A,B) -> [{A,B}] end, [1,2,3,4]),
+%% [{1,2},{3,4}]
+
+mmap(F,List) ->
+ {arity,Ar} = erlang:fun_info(F,arity),
+ mmap(F,Ar,List).
+mmap(_F,_,[]) -> [];
+mmap(F,Ar,List) ->
+ {Hd,Tl} = lists:split(Ar,List),
+ apply(F,Hd) ++ mmap(F,Ar,Tl).
+
+check_strict_value_termorder([]) -> true;
+check_strict_value_termorder([_]) -> true;
+check_strict_value_termorder([V1,V2]) ->
+ erts_internal:cmp_term(V1,V2) < 0;
+check_strict_value_termorder([V1,V2|Vs]) ->
+ case erts_internal:cmp_term(V1,V2) < 0 of
+ true -> check_strict_value_termorder([V2|Vs]);
+ false -> false
+ end.
+
%%%
%%% Binary matching.
%%%
@@ -1333,6 +1388,7 @@ assert_term(Src, Vst) ->
%% number Integer or Float of unknown value
%%
+
assert_type(WantedType, Term, Vst) ->
assert_type(WantedType, get_term_type(Term, Vst)).
@@ -1348,7 +1404,6 @@ assert_type({tuple_element,I}, {tuple,Sz})
assert_type(Needed, Actual) ->
error({bad_type,{needed,Needed},{actual,Actual}}).
-
%% upgrade_tuple_type(NewTupleType, OldType) -> TupleType.
%% upgrade_tuple_type/2 is used when linear code finds out more and
%% more information about a tuple type, so that the type gets more
@@ -1429,6 +1484,15 @@ get_term_type_1({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
get_term_type_1(Src, _) -> error({bad_source,Src}).
+%% get_literal(Src) -> literal_value().
+get_literal(nil) -> [];
+get_literal({atom,A}) when is_atom(A) -> A;
+get_literal({float,F}) when is_float(F) -> F;
+get_literal({integer,I}) when is_integer(I) -> I;
+get_literal({literal,L}) -> L;
+get_literal(T) -> error({not_literal,T}).
+
+
branch_arities([], _, #vst{}=Vst) -> Vst;
branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
when is_integer(Sz) ->
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
index 9953a48710..c2a6ef604e 100644
--- a/lib/compiler/src/beam_z.erl
+++ b/lib/compiler/src/beam_z.erl
@@ -78,6 +78,18 @@ undo_rename({put_map,Fail,assoc,S,D,R,L}) ->
{put_map_assoc,Fail,S,D,R,L};
undo_rename({put_map,Fail,exact,S,D,R,L}) ->
{put_map_exact,Fail,S,D,R,L};
+undo_rename({test,has_map_fields,Fail,[Src|List]}) ->
+ {test,has_map_fields,Fail,Src,{list,[to_typed_literal(V)||V<-List]}};
+undo_rename({get_map_elements,Fail,Src,{list, List}}) ->
+ {get_map_elements,Fail,Src,{list,[to_typed_literal(V)||V<-List]}};
undo_rename({select,I,Reg,Fail,List}) ->
{I,Reg,Fail,{list,List}};
undo_rename(I) -> I.
+
+%% to_typed_literal(Arg)
+%% transform Arg to specific literal i.e. float | integer | atom if applicable
+to_typed_literal({literal, V}) when is_float(V) -> {float, V};
+to_typed_literal({literal, V}) when is_atom(V) -> {atom, V};
+to_typed_literal({literal, V}) when is_integer(V) -> {integer, V};
+to_typed_literal({literal, []}) -> nil;
+to_typed_literal(V) -> V.
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 60a8559950..ed11c8de4d 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -124,20 +124,17 @@
%% keep map exports here for now
map_es/1,
- update_c_map/2,
- ann_c_map/2,
+ map_arg/1,
+ update_c_map/3,
+ ann_c_map/2, ann_c_map/3,
map_pair_op/1,map_pair_key/1,map_pair_val/1,
update_c_map_pair/4,
ann_c_map_pair/4
]).
--export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0,
- c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]).
-
-%%
-%% needed by the include file below -- do not move
-%%
--type var_name() :: integer() | atom() | {atom(), integer()}.
+-export_type([c_binary/0, c_bitstr/0, c_call/0, c_clause/0, c_cons/0, c_fun/0,
+ c_literal/0, c_map_pair/0, c_module/0, c_tuple/0,
+ c_values/0, c_var/0, cerl/0, var_name/0]).
-include("core_parse.hrl").
@@ -172,6 +169,8 @@
| c_module() | c_primop() | c_receive() | c_seq()
| c_try() | c_tuple() | c_values() | c_var().
+-type var_name() :: integer() | atom() | {atom(), integer()}.
+
%% =====================================================================
%% Representation (general)
%%
@@ -203,13 +202,15 @@
%% <td>call</td>
%% <td>case</td>
%% <td>catch</td>
-%% </tr><tr>
%% <td>clause</td>
+%% </tr><tr>
%% <td>cons</td>
%% <td>fun</td>
%% <td>let</td>
%% <td>letrec</td>
%% <td>literal</td>
+%% <td>map</td>
+%% <td>map_pair</td>
%% <td>module</td>
%% </tr><tr>
%% <td>primop</td>
@@ -260,10 +261,10 @@
%% @see subtrees/1
%% @see meta/1
--type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case'
- | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec'
- | 'literal' | 'map' | 'module' | 'primop' | 'receive' | 'seq'
- | 'try' | 'tuple' | 'values' | 'var'.
+-type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case'
+ | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec'
+ | 'literal' | 'map' | 'map_pair' | 'module' | 'primop'
+ | 'receive' | 'seq' | 'try' | 'tuple' | 'values' | 'var'.
-spec type(cerl()) -> ctype().
@@ -1574,16 +1575,70 @@ ann_make_list(_, [], Node) ->
%% ---------------------------------------------------------------------
%% maps
--spec map_es(c_map()) -> [cerl()].
+-spec map_es(c_map()) -> [c_map_pair()].
map_es(#c_map{es = Es}) ->
Es.
-ann_c_map(As, Es) ->
- #c_map{es = Es, anno = As }.
+-spec map_arg(c_map()) -> c_map() | c_literal().
+
+map_arg(#c_map{arg = M}) ->
+ M.
+
+-spec ann_c_map([term()], [cerl()]) -> c_map() | c_literal().
+
+ann_c_map(As,Es) ->
+ ann_c_map(As, #c_literal{val=#{}}, Es).
+
+-spec ann_c_map([term()], c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal().
+
+ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 ->
+ Pairs = [[Ck,Cv]||#c_map_pair{key=Ck,val=Cv}<-Es],
+ IsLit = lists:foldl(fun(Pair,Res) ->
+ Res andalso is_lit_list(Pair)
+ end, true, Pairs),
+ Fun = fun(Pair) -> [K,V] = lit_list_vals(Pair), {K,V} end,
+ case IsLit of
+ false ->
+ #c_map{arg=M, es=Es, anno=As };
+ true ->
+ #c_literal{anno=As, val=maps:from_list(lists:map(Fun, Pairs))}
+ end;
+ann_c_map(As,#c_literal{val=M},Es) when is_map(M) ->
+ fold_map_pairs(As,Es,M);
+ann_c_map(As,M,Es) ->
+ #c_map{arg=M, es=Es, anno=As }.
+
+fold_map_pairs(As,[],M) -> #c_literal{anno=As,val=M};
+%% M#{ K => V}
+fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=assoc},key=Ck,val=Cv}=E|Es],M) ->
+ case is_lit_list([Ck,Cv]) of
+ true ->
+ [K,V] = lit_list_vals([Ck,Cv]),
+ fold_map_pairs(As,Es,maps:put(K,V,M));
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+%% M#{ K := V}
+fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) ->
+ case is_lit_list([Ck,Cv]) of
+ true ->
+ [K,V] = lit_list_vals([Ck,Cv]),
+ case maps:is_key(K,M) of
+ true -> fold_map_pairs(As,Es,maps:put(K,V,M));
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+fold_map_pairs(As,Es,M) ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=Es, anno=As }.
+
+%-spec update_c_map(c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal().
-update_c_map(Old, Es) ->
- #c_map{es = Es, anno = get_ann(Old)}.
+update_c_map(Old,M,Es) ->
+ #c_map{arg=M, es = Es, anno = get_ann(Old)}.
map_pair_key(#c_map_pair{key=K}) -> K.
map_pair_val(#c_map_pair{val=V}) -> V.
@@ -4319,12 +4374,8 @@ meta_1(cons, Node) ->
%% we get exactly one element, we generate a 'c_cons' call
%% instead of 'make_list' to reconstruct the node.
case split_list(Node) of
- {[H], none} ->
- meta_call(c_cons, [meta(H), meta(c_nil())]);
{[H], Node1} ->
meta_call(c_cons, [meta(H), meta(Node1)]);
- {L, none} ->
- meta_call(make_list, [make_list(meta_list(L))]);
{L, Node1} ->
meta_call(make_list,
[make_list(meta_list(L)), meta(Node1)])
@@ -4411,8 +4462,6 @@ split_list(Node, L) ->
case type(Node) of
cons when A =:= [] ->
split_list(cons_tl(Node), [cons_hd(Node) | L]);
- nil when A =:= [] ->
- {lists:reverse(L), none};
_ ->
{lists:reverse(L), Node}
end.
diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl
index 99fa8dd9d5..87bd47c08b 100644
--- a/lib/compiler/src/cerl_clauses.erl
+++ b/lib/compiler/src/cerl_clauses.erl
@@ -354,6 +354,29 @@ match(P, E, Bs) ->
{false, Bs}
end
end;
+ map ->
+ %% The most we can do is to say "definitely no match" if a
+ %% map pattern is matched against non-map data.
+ case E of
+ any ->
+ {false, Bs};
+ _ ->
+ case type(E) of
+ literal ->
+ case is_map(concrete(E)) of
+ false ->
+ none;
+ true ->
+ {false, Bs}
+ end;
+ cons ->
+ none;
+ tuple ->
+ none;
+ _ ->
+ {false, Bs}
+ end
+ end;
_ ->
match_1(P, E, Bs)
end.
diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl
index 3837b57750..75740e8b9d 100644
--- a/lib/compiler/src/cerl_inline.erl
+++ b/lib/compiler/src/cerl_inline.erl
@@ -42,7 +42,7 @@
bitstr_flags/1, binary_segments/1, update_c_alias/3,
update_c_apply/3, update_c_binary/2, update_c_bitstr/6,
update_c_call/4, update_c_case/3, update_c_catch/2,
- update_c_clause/4, c_fun/2, c_int/1, c_let/3,
+ update_c_clause/4, c_fun/2, c_int/1, c_let/3, ann_c_let/4,
update_c_let/4, update_c_letrec/3, update_c_module/5,
update_c_primop/3, update_c_receive/4, update_c_seq/3,
c_seq/2, update_c_try/6, c_tuple/1, update_c_values/2,
@@ -51,7 +51,7 @@
catch_body/1, clause_body/1, clause_guard/1,
clause_pats/1, clause_vars/1, concrete/1, cons_hd/1,
cons_tl/1, data_arity/1, data_es/1, data_type/1,
- fun_body/1, fun_vars/1, get_ann/1, int_val/1,
+ fname_arity/1, fun_body/1, fun_vars/1, get_ann/1, int_val/1,
is_c_atom/1, is_c_cons/1, is_c_fname/1, is_c_int/1,
is_c_list/1, is_c_seq/1, is_c_tuple/1, is_c_var/1,
is_data/1, is_literal/1, is_literal_term/1, let_arg/1,
@@ -64,7 +64,7 @@
seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
type/1, values_es/1, var_name/1,
- map_es/1, update_c_map/2,
+ map_arg/1, map_es/1, update_c_map/3,
update_c_map_pair/4,
map_pair_op/1, map_pair_key/1, map_pair_val/1
]).
@@ -1030,8 +1030,17 @@ i_apply(E, Ctxt, Ren, Env, S) ->
visit_and_count_size(Opnd, S)
end,
S3, Opnds),
- N = apply_size(length(Es)),
- {update_c_apply(E, E1, Es), count_size(N, S4)}
+ Arity = length(Es),
+ E2 = case is_c_fname(E1) andalso length(Es) =/= fname_arity(E1) of
+ true ->
+ V = new_var(Env),
+ ann_c_let(get_ann(E), [V], E1,
+ update_c_apply(E, V, Es));
+ false ->
+ update_c_apply(E, E1, Es)
+ end,
+ N = apply_size(Arity),
+ {E2, count_size(N, S4)}
end.
apply_size(A) ->
@@ -1334,12 +1343,12 @@ i_bitstr(E, Ren, Env, S) ->
i_map(E, Ctx, Ren, Env, S) ->
%% Visit the segments for value.
- {Es, S1} = mapfoldl(fun (E, S) ->
- i_map_pair(E, Ctx, Ren, Env, S)
- end,
- S, map_es(E)),
- S2 = count_size(weight(map), S1),
- {update_c_map(E, Es), S2}.
+ {M1, S1} = i(map_arg(E), value, Ren, Env, S),
+ {Es, S2} = mapfoldl(fun (E, S) ->
+ i_map_pair(E, Ctx, Ren, Env, S)
+ end, S1, map_es(E)),
+ S3 = count_size(weight(map), S2),
+ {update_c_map(E, M1,Es), S3}.
i_map_pair(E, Ctx, Ren, Env, S) ->
%% It is not necessary to visit the Op and Key fields,
@@ -1411,13 +1420,15 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) ->
S2 = count_size(weight(binary), S1),
{update_c_binary(E, Es), S2};
map ->
+ %% map patterns should not have args
+ M = map_arg(E),
+
{Es, S1} = mapfoldl(fun (E, S) ->
- i_map_pair_pattern(E, Ren, Env,
- Ren0, Env0, S)
- end,
- S, map_es(E)),
+ i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S)
+ end,
+ S, map_es(E)),
S2 = count_size(weight(map), S1),
- {update_c_map(E, Es), S2};
+ {update_c_map(E, M, Es), S2};
_ ->
case is_literal(E) of
true ->
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 2542841eef..e53bdd4efb 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -57,9 +57,9 @@
update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2,
update_c_values/2, values_es/1, var_name/1,
- map_es/1,
- ann_c_map/2,
- update_c_map/2,
+ map_arg/1, map_es/1,
+ ann_c_map/3,
+ update_c_map/3,
map_pair_key/1,map_pair_val/1,map_pair_op/1,
ann_c_map_pair/4,
update_c_map_pair/4
@@ -138,7 +138,7 @@ map_1(F, T) ->
tuple ->
update_c_tuple_skel(T, map_list(F, tuple_es(T)));
map ->
- update_c_map(T, map_list(F, map_es(T)));
+ update_c_map(T, map(F, map_arg(T)), map_list(F, map_es(T)));
map_pair ->
update_c_map_pair(T, map(F, map_pair_op(T)),
map(F, map_pair_key(T)),
@@ -372,8 +372,9 @@ mapfold(F, S0, T) ->
{Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
F(update_c_tuple_skel(T, Ts), S1);
map ->
- {Ts, S1} = mapfold_list(F, S0, map_es(T)),
- F(update_c_map(T, Ts), S1);
+ {M , S1} = mapfold(F, S0, map_arg(T)),
+ {Ts, S2} = mapfold_list(F, S1, map_es(T)),
+ F(update_c_map(T, M, Ts), S2);
map_pair ->
{Op, S1} = mapfold(F, S0, map_pair_op(T)),
{Key, S2} = mapfold(F, S1, map_pair_key(T)),
@@ -723,9 +724,10 @@ label(T, N, Env) ->
{As, N2} = label_ann(T, N1),
{ann_c_tuple_skel(As, Ts), N2};
map ->
- {Ts, N1} = label_list(map_es(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_map(As, Ts), N2};
+ {M, N1} = label(map_arg(T), N, Env),
+ {Ts, N2} = label_list(map_es(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_map(As, M, Ts), N3};
map_pair ->
{Op, N1} = label(map_pair_op(T), N, Env),
{Val, N2} = label(map_pair_key(T), N1, Env),
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 0bb4de6f17..c7d91070f6 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -234,7 +234,9 @@ format_error({crash,Pass,Reason}) ->
format_error({bad_return,Pass,Reason}) ->
io_lib:format("internal error in ~p;\nbad return value: ~ts", [Pass,format_error_reason(Reason)]);
format_error({module_name,Mod,Filename}) ->
- io_lib:format("Module name '~s' does not match file name '~ts'", [Mod,Filename]).
+ io_lib:format("Module name '~s' does not match file name '~ts'", [Mod,Filename]);
+format_error(reparsing_invalid_unicode) ->
+ "Non-UTF-8 character(s) detected, but no encoding declared. Encode the file in UTF-8 or add \"%% coding: latin-1\" at the beginning of the file. Retrying with latin-1 encoding.".
format_error_reason({Reason, Stack}) when is_list(Stack) ->
StackFun = fun
@@ -246,7 +248,7 @@ format_error_reason({Reason, Stack}) when is_list(Stack) ->
end,
FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
[io_lib:format("~tp", [Reason]),"\n\n",
- lib:format_stacktrace(1, erlang:get_stacktrace(), StackFun, FormatFun)];
+ lib:format_stacktrace(1, Stack, StackFun, FormatFun)];
format_error_reason(Reason) ->
io_lib:format("~tp", [Reason]).
@@ -430,10 +432,9 @@ pass(from_core) ->
pass(from_asm) ->
{".S",[?pass(beam_consult_asm)|asm_passes()]};
pass(asm) ->
- %% TODO: remove 'asm' in R18
- io:format("compile:file/2 option 'asm' has been deprecated and will be "
- "removed in R18.~n"
- "Use 'from_asm' instead.~n"),
+ %% TODO: remove 'asm' in 18.0
+ io:format("compile:file/2 option 'asm' has been deprecated and will be~n"
+ "removed in the 18.0 release. Use 'from_asm' instead.~n"),
pass(from_asm);
pass(from_beam) ->
{".beam",[?pass(read_beam_file)|binary_passes()]};
@@ -623,9 +624,11 @@ core_passes() ->
[{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},
{iff,doldinline,{listing,"oldinline"}},
?pass(core_fold_module),
+ {iff,dcorefold,{listing,"corefold"}},
{core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},
{iff,dinline,{listing,"inline"}},
- {core_fold_after_inlining,fun test_core_inliner/1,fun core_fold_module_after_inlining/1},
+ {core_fold_after_inlining,fun test_any_inliner/1,
+ fun core_fold_module_after_inlining/1},
?pass(core_transforms)]},
{iff,dcopt,{listing,"copt"}},
{iff,'to_core',{done,"core"}}]}
@@ -791,20 +794,59 @@ no_native_compilation(BeamFile, #compile{options=Opts0}) ->
_ -> false
end.
-parse_module(St) ->
- Opts = St#compile.options,
- Cwd = ".",
- IncludePath = [Cwd, St#compile.dir|inc_paths(Opts)],
- R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)),
+parse_module(St0) ->
+ case do_parse_module(utf8, St0) of
+ {ok,_}=Ret ->
+ Ret;
+ {error,_}=Ret ->
+ Ret;
+ {invalid_unicode,File,Line} ->
+ case do_parse_module(latin1, St0) of
+ {ok,St} ->
+ Es = [{File,[{Line,?MODULE,reparsing_invalid_unicode}]}],
+ {ok,St#compile{warnings=Es++St#compile.warnings}};
+ {error,St} ->
+ Es = [{File,[{Line,?MODULE,reparsing_invalid_unicode}]}],
+ {error,St#compile{errors=Es++St#compile.errors}}
+ end
+ end.
+
+do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) ->
+ R = epp:parse_file(File,
+ [{includes,[".",Dir|inc_paths(Opts)]},
+ {macros,pre_defs(Opts)},
+ {default_encoding,DefEncoding},
+ extra]),
case R of
- {ok,Forms} ->
- Encoding = epp:read_encoding(St#compile.ifile),
- {ok,St#compile{code=Forms,encoding=Encoding}};
+ {ok,Forms,Extra} ->
+ Encoding = proplists:get_value(encoding, Extra),
+ case find_invalid_unicode(Forms, File) of
+ none ->
+ {ok,St#compile{code=Forms,encoding=Encoding}};
+ {invalid_unicode,_,_}=Ret ->
+ case Encoding of
+ none ->
+ Ret;
+ _ ->
+ {ok,St#compile{code=Forms,encoding=Encoding}}
+ end
+ end;
{error,E} ->
Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
+find_invalid_unicode([H|T], File0) ->
+ case H of
+ {attribute,_,file,{File,_}} ->
+ find_invalid_unicode(T, File);
+ {error,{Line,file_io_server,invalid_unicode}} ->
+ {invalid_unicode,File0,Line};
+ _Other ->
+ find_invalid_unicode(T, File0)
+ end;
+find_invalid_unicode([], _) -> none.
+
parse_core(St) ->
case file:read_file(St#compile.ifile) of
{ok,Bin} ->
@@ -1171,6 +1213,9 @@ test_core_inliner(#compile{options=Opts}) ->
end, Opts)
end.
+test_any_inliner(St) ->
+ test_old_inliner(St) orelse test_core_inliner(St).
+
core_old_inliner(#compile{code=Code0,options=Opts}=St) ->
{ok,Code} = sys_core_inline:module(Code0, Opts),
{ok,St#compile{code=Code}}.
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index 8775c84698..8f68915f8e 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -67,4 +67,6 @@
]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0",
+ "crypto-3.3"]}]}.
diff --git a/lib/compiler/src/compiler.appup.src b/lib/compiler/src/compiler.appup.src
index 54a63833e6..fe273b269c 100644
--- a/lib/compiler/src/compiler.appup.src
+++ b/lib/compiler/src/compiler.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, compiler}]}],
+ [{<<".*">>,[{restart_application, compiler}]}]
+}.
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index f506901099..93ec3bbad5 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -105,8 +105,8 @@ vu_expr(V, #c_cons{hd=H,tl=T}) ->
vu_expr(V, H) orelse vu_expr(V, T);
vu_expr(V, #c_tuple{es=Es}) ->
vu_expr_list(V, Es);
-vu_expr(V, #c_map{es=Es}) ->
- vu_expr_list(V, Es);
+vu_expr(V, #c_map{arg=M,es=Es}) ->
+ vu_expr(V, M) orelse vu_expr_list(V, Es);
vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
vu_expr_list(V, [Key,Val]);
vu_expr(V, #c_binary{segments=Ss}) ->
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index 36165245a6..25df33a287 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -267,10 +267,21 @@ gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body
{Lvs,St2} = variable_list(Vs, St1),
gbody(B, union(Lvs, Def), Rt, St2);
-gexpr(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{},
- args=As}, Def, 1, St) ->
- gexpr_list(As, Def, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record},
+ args=[Arg,#c_literal{val=Tag},#c_literal{val=Size}]},
+ Def, 1, St) when is_atom(Tag), is_integer(Size) ->
+ gexpr(Arg, Def, 1, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record}},
+ _Def, 1, St) ->
+ add_error({illegal_guard,St#lint.func}, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=Name},args=As},
+ Def, 1, St) when is_atom(Name) ->
+ case is_guard_bif(Name, length(As)) of
+ true ->
+ gexpr_list(As, Def, St);
+ false ->
+ add_error({illegal_guard,St#lint.func}, St)
+ end;
gexpr(#c_primop{name=#c_literal{val=A},args=As}, Def, _Rt, St0) when is_atom(A) ->
gexpr_list(As, Def, St0);
gexpr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
@@ -298,6 +309,14 @@ gbitstr_list(Es, Def, St0) ->
gbitstr(#c_bitstr{val=V,size=S}, Def, St) ->
gexpr_list([V,S], Def, St).
+%% is_guard_bif(Name, Arity) -> Boolean.
+
+is_guard_bif(Name, Arity) ->
+ erl_internal:guard_bif(Name, Arity)
+ orelse erl_internal:arith_op(Name, Arity)
+ orelse erl_internal:bool_op(Name, Arity)
+ orelse erl_internal:comp_op(Name, Arity).
+
%% expr(Expr, Defined, RetCount, State) -> State.
expr(#c_var{name={_,_}=FA}, Def, _Rt, St) ->
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index d54715ef59..4a00535360 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -34,7 +34,7 @@
-record(c_apply, {anno=[], op, % op :: Tree,
args}). % args :: [Tree]
--record(c_binary, {anno=[], segments}). % segments :: [#c_bitstr{}]
+-record(c_binary, {anno=[], segments :: [cerl:c_bitstr()]}).
-record(c_bitstr, {anno=[], val, % val :: Tree,
size, % size :: Tree,
@@ -70,6 +70,15 @@
-record(c_literal, {anno=[], val}). % val :: literal()
+-record(c_map, {anno=[],
+ arg=#c_literal{val=#{}} :: cerl:c_var() | cerl:c_literal(),
+ es :: [cerl:c_map_pair()]}).
+
+-record(c_map_pair, {anno=[],
+ op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
+ key,
+ val}).
+
-record(c_module, {anno=[], name, % name :: Tree,
exports, % exports :: [Tree],
attrs, % attrs :: [#c_def{}],
@@ -96,12 +105,3 @@
-record(c_values, {anno=[], es}). % es :: [Tree]
-record(c_var, {anno=[], name :: cerl:var_name()}).
-
--record(c_map_pair, {anno=[],
- op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
- key,
- val}).
-
--record(c_map, {anno=[],
- var=#c_literal{val=[]} :: #c_var{} | #c_literal{},
- es :: [#c_map_pair{}]}).
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index b8db0f683a..a66ad4235f 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -21,7 +21,7 @@
%% Have explicit productions for annotated phrases named anno_XXX.
%% This just does an XXX and adds the annotation.
-Expect 1.
+Expect 0.
Nonterminals
@@ -285,9 +285,9 @@ tuple -> '{' '}' : c_tuple([]).
tuple -> '{' anno_expressions '}' : c_tuple('$2').
map_expr -> '~' '{' '}' '~' : #c_map{es=[]}.
-map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}.
-map_expr -> variable '~' '{' '}' '~' : #c_map{var='$1',es=[]}.
-map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}.
+map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}.
+map_expr -> '~' '{' map_pairs '|' variable '}' '~' : #c_map{arg='$5',es='$3'}.
+map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : #c_map{arg='$5',es='$3'}.
map_pairs -> map_pair : ['$1'].
map_pairs -> map_pair ',' map_pairs : ['$1' | '$3'].
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index faa26ec6df..a76327457d 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -118,6 +118,12 @@ format_1(#c_literal{val=Tuple}, Ctxt) when is_tuple(Tuple) ->
format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) ->
Segs = segs_from_bitstring(Bitstring),
format_1(#c_binary{anno=A,segments=Segs}, Ctxt);
+format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) ->
+ Pairs = maps:to_list(M),
+ Cpairs = [#c_map_pair{op=#c_literal{val=assoc},
+ key=#c_literal{val=V},
+ val=#c_literal{val=K}} || {K,V} <- Pairs],
+ format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt);
format_1(#c_var{name={I,A}}, _) ->
[core_atom(I),$/,integer_to_list(A)];
format_1(#c_var{name=V}, _) ->
@@ -161,15 +167,15 @@ format_1(#c_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
$}
];
-format_1(#c_map{var=#c_var{}=Var,es=Es}, Ctxt) ->
- [format_1(Var, Ctxt),
- "~{",
+format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 ->
+ ["~{",
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
"}~"
];
-format_1(#c_map{es=Es}, Ctxt) ->
+format_1(#c_map{arg=Var,es=Es}, Ctxt) ->
["~{",
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
+ "|",format(Var, add_indent(Ctxt, 1)),
"}~"
];
format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) ->
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index 3ad3c8c690..6c75538194 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -91,6 +91,7 @@ is_pure(erlang, is_float, 1) -> true;
is_pure(erlang, is_function, 1) -> true;
is_pure(erlang, is_integer, 1) -> true;
is_pure(erlang, is_list, 1) -> true;
+is_pure(erlang, is_map, 1) -> true;
is_pure(erlang, is_number, 1) -> true;
is_pure(erlang, is_pid, 1) -> true;
is_pure(erlang, is_port, 1) -> true;
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 79b467f949..7d6bf56ccb 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -529,10 +529,10 @@ BEAM_FORMAT_NUMBER=0
153: line/1
-# R16
+# R17
154: put_map_assoc/5
155: put_map_exact/5
156: is_map/2
-157: has_map_field/3
-158: get_map_element/4
+157: has_map_fields/3
+158: get_map_elements/3
diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl
index 31a1f8b0b7..555a331bd7 100644
--- a/lib/compiler/src/rec_env.erl
+++ b/lib/compiler/src/rec_env.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -72,6 +72,7 @@ test_1({custom, F} = Type, N, Env) when is_integer(N), N > 0 ->
test_1(_,0, Env) ->
Env.
-endif.
+%%@clear
%% Representation:
@@ -95,7 +96,7 @@ test_1(_,0, Env) ->
%% =====================================================================
%% @type environment(). An abstract environment.
--type mapping() :: {'map', dict()} | {'rec', dict(), dict()}.
+-type mapping() :: {'map', dict:dict()} | {'rec', dict:dict(), dict:dict()}.
-type environment() :: [mapping(),...].
%% =====================================================================
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 1cdbac5693..b7422318b2 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -72,7 +72,7 @@
-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,all/2,any/2,
reverse/1,reverse/2,member/2,nth/2,flatten/1,unzip/1]).
--import(cerl, [ann_c_cons/3,ann_c_tuple/2]).
+-import(cerl, [ann_c_cons/3,ann_c_map/3,ann_c_tuple/2]).
-include("core_parse.hrl").
@@ -246,7 +246,7 @@ expr(#c_tuple{anno=Anno,es=Es0}=Tuple, Ctxt, Sub) ->
value ->
ann_c_tuple(Anno, Es)
end;
-expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) ->
+expr(#c_map{anno=Anno,arg=V0,es=Es0}=Map, Ctxt, Sub) ->
Es = pair_list(Es0, Ctxt, Sub),
case Ctxt of
effect ->
@@ -254,7 +254,7 @@ expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) ->
expr(make_effect_seq(Es, Sub), Ctxt, Sub);
value ->
V = expr(V0, Ctxt, Sub),
- Map#c_map{var=V,es=Es}
+ ann_c_map(Anno,V,Es)
end;
expr(#c_binary{segments=Ss}=Bin0, Ctxt, Sub) ->
%% Warn for useless building, but always build the binary
@@ -305,6 +305,10 @@ expr(#c_let{}=Let, Ctxt, Sub) ->
%% Now recursively re-process the new expression.
expr(Expr, Ctxt, sub_new_preserve_types(Sub))
end;
+expr(#c_letrec{body=#c_var{}}=Letrec, effect, _Sub) ->
+ %% This is named fun in an 'effect' context. Warn and ignore.
+ add_warning(Letrec, useless_building),
+ void();
expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) ->
Fs1 = map(fun ({Name,Fb}) ->
{Name,expr(Fb, {letrec,Ctxt}, Sub)}
@@ -349,7 +353,12 @@ expr(#c_case{}=Case0, Ctxt, Sub) ->
Case = Case1#c_case{arg=Arg2,clauses=Cs2},
warn_no_clause_match(Case1, Case),
Expr = eval_case(Case, Sub),
- bsm_an(Expr);
+ case move_case_into_arg(Case, Sub) of
+ impossible ->
+ bsm_an(Expr);
+ Other ->
+ expr(Other, Ctxt, sub_new_preserve_types(Sub))
+ end;
Other ->
expr(Other, Ctxt, Sub)
end;
@@ -598,6 +607,14 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz},
error:_ ->
throw(impossible)
end;
+eval_binary_1([#c_bitstr{val=#c_literal{},size=#c_literal{},
+ unit=#c_literal{},type=#c_literal{},
+ flags=#c_cons{}=Flags}=Bitstr|Ss], Acc0) ->
+ case cerl:fold_literal(Flags) of
+ #c_literal{} = Flags1 ->
+ eval_binary_1([Bitstr#c_bitstr{flags=Flags1}|Ss], Acc0);
+ _ -> throw(impossible)
+ end;
eval_binary_1([], Acc) -> Acc;
eval_binary_1(_, _) -> throw(impossible).
@@ -1361,6 +1378,7 @@ eval_is_record(Call, _, _, _, _) -> Call.
is_not_integer(#c_literal{val=Val}) when not is_integer(Val) -> true;
is_not_integer(#c_tuple{}) -> true;
is_not_integer(#c_cons{}) -> true;
+is_not_integer(#c_map{}) -> true;
is_not_integer(_) -> false.
%% is_not_tuple(Core) -> true | false.
@@ -1368,6 +1386,7 @@ is_not_integer(_) -> false.
is_not_tuple(#c_literal{val=Val}) when not is_tuple(Val) -> true;
is_not_tuple(#c_cons{}) -> true;
+is_not_tuple(#c_map{}) -> true;
is_not_tuple(_) -> false.
%% eval_setelement(Call, Pos, Tuple, NewVal) -> Core.
@@ -1536,9 +1555,17 @@ map_pair_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0),
{Ps,Osub}.
-map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair, {Isub,Osub0}) ->
- {K,Osub1} = pattern(K0, Isub, Osub0),
- {V,Osub} = pattern(V0, Isub, Osub1),
+map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,Osub0}) ->
+ {K,Osub1} = case cerl:type(K0) of
+ binary ->
+ K1 = eval_binary(K0),
+ case cerl:type(K1) of
+ literal -> {K1,Osub0};
+ _ -> pattern(K0,Isub,Osub0)
+ end;
+ _ -> pattern(K0,Isub,Osub0)
+ end,
+ {V,Osub} = pattern(V0,Isub,Osub1),
{Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
bin_pattern_list(Ps0, Isub, Osub0) ->
@@ -1785,9 +1812,14 @@ opt_bool_clauses([#c_clause{pats=[#c_literal{val=Lit}],
true ->
%% This clause will match.
C = C0#c_clause{body=opt_bool_case(B)},
- case Lit of
- false -> [C|opt_bool_clauses(Cs, SeenT, true)];
- true -> [C|opt_bool_clauses(Cs, true, SeenF)]
+ case {Lit,SeenT,SeenF} of
+ {false,_,false} ->
+ [C|opt_bool_clauses(Cs, SeenT, true)];
+ {true,false,_} ->
+ [C|opt_bool_clauses(Cs, true, SeenF)];
+ _ ->
+ add_warning(C, nomatch_shadow),
+ opt_bool_clauses(Cs, SeenT, SeenF)
end
end;
opt_bool_clauses([#c_clause{pats=Ps,guard=#c_literal{val=true}}=C|Cs], SeenT, SeenF) ->
@@ -1920,14 +1952,45 @@ opt_bool_case_guard(Arg, [#c_clause{pats=[#c_literal{val=false}]}=Fc,Tc]) ->
%% last clause is guaranteed to match so if there is only one clause
%% with a pattern containing only variables then rewrite to a let.
-eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,body=B}]}, Sub) ->
+eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,
+ guard=#c_literal{val=true},
+ body=B}]}=Case, Sub) ->
Es = case cerl:is_c_values(E) of
true -> cerl:values_es(E);
false -> [E]
end,
- {true,Bs} = cerl_clauses:match_list(Ps0, Es),
- {Ps,As} = unzip(Bs),
- expr(#c_let{vars=Ps,arg=core_lib:make_values(As),body=B}, sub_new(Sub));
+ %% Consider:
+ %%
+ %% case SomeSideEffect() of
+ %% X=Y -> ...
+ %% end
+ %%
+ %% We must not rewrite it to:
+ %%
+ %% let <X,Y> = <SomeSideEffect(),SomeSideEffect()> in ...
+ %%
+ %% because SomeSideEffect() would be evaluated twice.
+ %%
+ %% Instead we must evaluate the case expression in an outer let
+ %% like this:
+ %%
+ %% let NewVar = SomeSideEffect() in
+ %% let <X,Y> = <NewVar,NewVar> in ...
+ %%
+ Vs = make_vars([], length(Es)),
+ case cerl_clauses:match_list(Ps0, Vs) of
+ {false,_} ->
+ %% This can only happen if the Core Erlang code is
+ %% handwritten or generated by another code generator
+ %% than v3_core. Assuming that the Core Erlang program
+ %% is correct, the clause will always match at run-time.
+ Case;
+ {true,Bs} ->
+ {Ps,As} = unzip(Bs),
+ InnerLet = cerl:c_let(Ps, core_lib:make_values(As), B),
+ Let = cerl:c_let(Vs, E, InnerLet),
+ expr(Let, sub_new(Sub))
+ end;
eval_case(Case, _) -> Case.
%% case_opt(CaseArg, [Clause]) -> {CaseArg,[Clause]}.
@@ -1968,9 +2031,9 @@ case_opt(Arg, Cs0, Sub) ->
case_opt_args([A0|As0], Cs0, Sub, LitExpr, Acc) ->
case case_opt_arg(A0, Sub, Cs0, LitExpr) of
- error ->
+ {error,Cs1} ->
%% Nothing to be done. Move on to the next argument.
- Cs = [{Ps,C,[P|PsAcc],Bs} || {[P|Ps],C,PsAcc,Bs} <- Cs0],
+ Cs = [{Ps,C,[P|PsAcc],Bs} || {[P|Ps],C,PsAcc,Bs} <- Cs1],
case_opt_args(As0, Cs, Sub, LitExpr, [A0|Acc]);
{ok,As1,Cs} ->
%% The argument was either expanded (from tuple/list) or
@@ -1989,7 +2052,7 @@ case_opt_arg(E0, Sub, Cs, LitExpr) ->
E = maybe_replace_var(E0, Sub),
case cerl:is_data(E) of
false ->
- error;
+ {error,Cs};
true ->
case cerl:data_type(E) of
{atomic,_} ->
@@ -2039,35 +2102,44 @@ maybe_replace_var_1(E, #sub{t=Tdb}) ->
%% pattern matching is tricky, so we will give up in that case.
case_opt_lit(Lit, Cs0, LitExpr) ->
- try case_opt_lit_1(Cs0, Lit, LitExpr) of
+ Cs1 = case_opt_lit_1(Lit, Cs0, LitExpr),
+ try case_opt_lit_2(Lit, Cs1) of
Cs ->
{ok,[],Cs}
catch
throw:impossible ->
- error
+ {error,Cs1}
end.
-case_opt_lit_1([{[P|Ps],C,PsAcc,Bs0}|Cs], E, LitExpr) ->
+case_opt_lit_1(E, [{[P|_],C,_,_}=Current|Cs], LitExpr) ->
+ case cerl_clauses:match(P, E) of
+ none ->
+ %% The pattern will not match the literal. Remove the clause.
+ %% Unless the entire case expression is a literal, also
+ %% emit a warning.
+ case LitExpr of
+ false -> add_warning(C, nomatch_clause_type);
+ true -> ok
+ end,
+ case_opt_lit_1(E, Cs, LitExpr);
+ _ ->
+ [Current|case_opt_lit_1(E, Cs, LitExpr)]
+ end;
+case_opt_lit_1(_, [], _) -> [].
+
+case_opt_lit_2(E, [{[P|Ps],C,PsAcc,Bs0}|Cs]) ->
+ %% Non-matching clauses have already been removed in case_opt_lit_1/3.
case cerl_clauses:match(P, E) of
- none ->
- %% The pattern will not match the literal. Remove the clause.
- %% Unless the entire case expression is a literal, also
- %% emit a warning.
- case LitExpr of
- false -> add_warning(C, nomatch_clause_type);
- true -> ok
- end,
- case_opt_lit_1(Cs, E, LitExpr);
{true,Bs} ->
%% The pattern matches the literal. Remove the pattern
%% and update the bindings.
- [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_1(Cs, E, LitExpr)];
+ [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_2(E, Cs)];
{false,_} ->
%% Binary literal and pattern. We are not sure whether
%% the pattern will match.
throw(impossible)
end;
-case_opt_lit_1([], _, _) -> [].
+case_opt_lit_2(_, []) -> [].
%% case_opt_data(Expr, Clauses0, LitExpr) -> {ok,Exprs,Clauses}
@@ -2290,16 +2362,31 @@ is_safe_bool_expr(Core, Sub) ->
is_safe_bool_expr_1(Core, Sub, gb_sets:empty()).
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{val=is_record},
- args=[_,_]},
- _Sub, _BoolVars) ->
+ name=#c_literal{val=is_record},
+ args=[A,#c_literal{val=Tag},#c_literal{val=Size}]},
+ Sub, _BoolVars) when is_atom(Tag), is_integer(Size) ->
+ is_safe_simple(A, Sub);
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_record}},
+ _Sub, _BoolVars) ->
%% The is_record/2 BIF is NOT allowed in guards.
+ %% The is_record/3 BIF where its second argument is not an atom or its third
+ %% is not an integer is NOT allowed in guards.
%%
%% NOTE: Calls like is_record(Expr, LiteralTag), where LiteralTag
%% is a literal atom referring to a defined record, have already
%% been rewritten to is_record(Expr, LiteralTag, TupleSize).
false;
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[A,#c_literal{val=Arity}]},
+ Sub, _BoolVars) when is_integer(Arity), Arity >= 0 ->
+ is_safe_simple(A, Sub);
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function}},
+ _Sub, _BoolVars) ->
+ false;
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=Name},args=Args},
Sub, BoolVars) ->
NumArgs = length(Args),
@@ -2555,6 +2642,77 @@ opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->
value, Sub)
end.
+move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg,
+ body=InnerArg0}=Outer,
+ clauses=InnerClauses}=Inner, Sub) ->
+ %%
+ %% case let <OuterVars> = <OuterArg> in <InnerArg> of
+ %% <InnerClauses>
+ %% end
+ %%
+ %% ==>
+ %%
+ %% let <OuterVars> = <OuterArg>
+ %% in case <InnerArg> of <InnerClauses> end
+ %%
+ ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}),
+ {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0),
+ InnerArg = body(InnerArg0, ScopeSub),
+ Outer#c_let{vars=OuterVars,arg=OuterArg,
+ body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}};
+move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg,
+ clauses=[OuterCa0,OuterCb]}=Outer,
+ clauses=InnerClauses}=Inner0, Sub) ->
+ case is_failing_clause(OuterCb) of
+ true ->
+ #c_clause{pats=OuterPats0,guard=OuterGuard0,
+ body=InnerArg0} = OuterCa0,
+ %%
+ %% case case <OuterArg> of
+ %% <OuterPats> when <OuterGuard> -> <InnerArg>
+ %% <OuterCb>
+ %% ...
+ %% end of
+ %% <InnerClauses>
+ %% end
+ %%
+ %% ==>
+ %%
+ %% case <OuterArg> of
+ %% <OuterPats> when <OuterGuard> ->
+ %% case <InnerArg> of <InnerClauses> end
+ %% <OuterCb>
+ %% end
+ %%
+ ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}),
+ {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0),
+ OuterGuard = guard(OuterGuard0, ScopeSub),
+ InnerArg = body(InnerArg0, ScopeSub),
+ Inner = Inner0#c_case{arg=InnerArg,clauses=InnerClauses},
+ OuterCa = OuterCa0#c_clause{pats=OuterPats,guard=OuterGuard,
+ body=Inner},
+ Outer#c_case{arg=OuterArg,
+ clauses=[OuterCa,OuterCb]};
+ false ->
+ impossible
+ end;
+move_case_into_arg(#c_case{arg=#c_seq{arg=OuterArg,body=InnerArg}=Outer,
+ clauses=InnerClauses}=Inner, _Sub) ->
+ %%
+ %% case do <OuterArg> <InnerArg> of
+ %% <InnerClauses>
+ %% end
+ %%
+ %% ==>
+ %%
+ %% do <OuterArg>
+ %% case <InnerArg> of <InerClauses> end
+ %%
+ Outer#c_seq{arg=OuterArg,
+ body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}};
+move_case_into_arg(_, _) ->
+ impossible.
+
%% In guards only, rewrite a case in a let argument like
%%
%% let <Var> = case <> of
@@ -3019,9 +3177,6 @@ format_error(result_ignored) ->
"(suppress the warning by assigning the expression to the _ variable)";
format_error(useless_building) ->
"a term is constructed, but never used";
-format_error({map_pair_key_overloaded,K}) ->
- M = io_lib:format("the key ~p is used multiple times in map value association",[K]),
- flatten(M);
format_error(bin_opt_alias) ->
"INFO: the '=' operator will prevent delayed sub binary optimization";
format_error(bin_partition) ->
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 9998043013..91a46a20fe 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -331,9 +331,10 @@ expr({tuple,Line,Es0}, St0) ->
expr({map,Line,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Es1},St1};
-expr({map,Line,Var,Es0}, St0) ->
- {Es1,St1} = expr_list(Es0, St0),
- {{map,Line,Var,Es1},St1};
+expr({map,Line,E0,Es0}, St0) ->
+ {E1,St1} = expr(E0, St0),
+ {Es1,St2} = expr_list(Es0, St1),
+ {{map,Line,E1,Es1},St2};
expr({map_field_assoc,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index c8735a76e8..47a357c23d 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -459,7 +459,7 @@ basic_block([Le|Les], Acc) ->
%% sets that may garbage collect are not allowed in basic blocks.
collect_block({set,_,{binary,_}}) -> no_block;
-collect_block({set,_,{map,_,_}}) -> no_block;
+collect_block({set,_,{map,_,_,_}}) -> no_block;
collect_block({set,_,_}) -> include;
collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};
collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)};
@@ -928,7 +928,7 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->
select_map(Scs, V, Tf, Vf, Bef, St0) ->
Reg = fetch_var(V, Bef),
{Is,Aft,St1} =
- match_fmf(fun(#l{ke={val_clause,{map,Es},B},i=I,vdb=Vdb}, Fail, St1) ->
+ match_fmf(fun(#l{ke={val_clause,{map,_,Es},B},i=I,vdb=Vdb}, Fail, St1) ->
select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1)
end, Vf, St0, Scs),
{[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}.
@@ -938,22 +938,38 @@ select_map_val(V, Es, B, Fail, I, Vdb, Bef, St0) ->
{Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
{Eis++Bis,Aft,St2}.
+select_extract_map(_, [], _, _, _, Bef, St) -> {[],Bef,St};
select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) ->
- F = fun ({map_pair,Key,{var,V}}, Int0) ->
- Rsrc = fetch_var(Src, Int0),
+ %% First split the instruction flow
+ %% We want one set of each
+ %% 1) has_map_fields (no target registers)
+ %% 2) get_map_elements (with target registers)
+ %% Assume keys are term-sorted
+ Rsrc = fetch_var(Src, Bef),
+
+ {{HasKs,GetVs},Aft} = lists:foldr(fun
+ ({map_pair,Key,{var,V}},{{HasKsi,GetVsi},Int0}) ->
case vdb_find(V, Vdb) of
{V,_,L} when L =< I ->
- {[{test,has_map_field,{f,Fail},[Rsrc,Key]}],Int0};
+ {{[Key|HasKsi],GetVsi},Int0};
_Other ->
Reg1 = put_reg(V, Int0#sr.reg),
Int1 = Int0#sr{reg=Reg1},
- {[{get_map_element,{f,Fail},
- Rsrc,Key,fetch_reg(V, Reg1)}],
- Int1}
+ {{HasKsi,[Key,fetch_reg(V, Reg1)|GetVsi]},Int1}
end
- end,
- {Es,Aft} = flatmapfoldl(F, Bef, Vs),
- {Es,Aft,St}.
+ end, {{[],[]},Bef}, Vs),
+
+ Code = case {HasKs,GetVs} of
+ {HasKs,[]} ->
+ [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}];
+ {[],GetVs} ->
+ [{get_map_elements, {f,Fail},Rsrc,{list,GetVs}}];
+ {HasKs,GetVs} ->
+ [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}},
+ {get_map_elements, {f,Fail},Rsrc,{list,GetVs}}]
+ end,
+ {Code, Aft, St}.
+
select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) ->
{Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of
@@ -1488,55 +1504,35 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
%% Now generate the complete code for constructing the binary.
Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
{Sis++Code,Aft,St};
-set_cg([{var,R}], {map,SrcMap,Es0}, Le, Vdb, Bef,
+set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef,
#cg{in_catch=InCatch,bfail=Bfail}=St) ->
+
Fail = {f,Bfail},
{Sis,Int0} =
case InCatch of
true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
false -> {[],Bef}
end,
+ SrcReg = cg_reg_arg(Map,Int0),
Line = line(Le#l.a),
- SrcReg = case SrcMap of
- {var,SrcVar} -> fetch_var(SrcVar, Int0);
- _ -> SrcMap
- end,
- {Assoc,Exact} =
- try
- cg_map_pairs(Es0)
- catch
- throw:badarg ->
- {[],[{{float,0.0},{atom,badarg}},
- {{integer,0},{atom,badarg}}]}
- end,
- F = fun ({K,{var,V}}) -> [K,fetch_var(V, Int0)];
- ({K,E}) -> [K,E]
- end,
- AssocList = flatmap(F, Assoc),
- ExactList = flatmap(F, Exact),
- Live0 = max_reg(Bef#sr.reg),
- Int1 = clear_dead(Int0, Le#l.i, Vdb),
- Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)},
- Target = fetch_reg(R, Aft#sr.reg),
- Code = [Line] ++
- case {AssocList,ExactList} of
- {[_|_],[]} ->
- [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}];
- {[_|_],[_|_]} ->
- Live = case Target of
- {x,TargetX} when TargetX =:= Live0 ->
- Live0 + 1;
- _ ->
- Live0
- end,
- [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}},
- {put_map_exact,Fail,Target,Target,Live,{list,ExactList}}];
- {[],[_|_]} ->
- [{put_map_exact,Fail,SrcReg,Target,Live0,{list,ExactList}}];
- {[],[]} ->
- [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,[]}}]
- end,
- {Sis++Code,Aft,St};
+
+ %% The instruction needs to store keys in term sorted order
+ %% All keys has to be unique here
+ Pairs = map_pair_strip_and_termsort(Es),
+
+ %% fetch registers for values to be put into the map
+ List = flatmap(fun({K,V}) -> [K,cg_reg_arg(V,Int0)] end, Pairs),
+
+ Live = max_reg(Bef#sr.reg),
+ Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
+ Aft = clear_dead(Int1, Le#l.i, Vdb),
+ Target = fetch_reg(R, Int1#sr.reg),
+
+ I = case Op of
+ assoc -> put_map_assoc;
+ exact -> put_map_exact
+ end,
+ {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],Aft,St};
set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
%% Find a place for the return register first.
Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
@@ -1549,70 +1545,15 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
end,
{Ais,clear_dead(Int, Le#l.i, Vdb),St}.
-%% cg_map_pairs(MapPairs) -> {Assoc,Exact}
-%% Assoc = Exact = [{K,V}]
-%%
-%% Remove multiple assignments to the same key, and return
-%% one list key-value list with all keys that may or may not exist
-%% (Assoc), and one with keys that must exist (Exact).
-%%
-
-cg_map_pairs(Es0) ->
- Es = cg_map_pairs_1(Es0, 0),
- R0 = sofs:relation(Es),
- R1 = sofs:relation_to_family(R0),
- R2 = sofs:to_external(R1),
-
- %% R2 is now [{KeyValue,[{Order,Op,OriginalKey,Value}]}]
- R3 = [begin
- %% The value for the last pair determines the value.
- {_,_,_,V} = lists:last(Vs),
- {Op,{_,SortOrder}=K} = map_pair_op_and_key(Vs),
- {Op,{SortOrder,K,V}}
- end || {_,Vs} <- R2],
-
- %% R3 is now [{Op,{Key,Value}}]
- R = termsort(R3),
-
- %% R4 is now sorted with all alloc first in the list, followed by
- %% all exact.
- {Assoc,Exact} = lists:partition(fun({Op,_}) -> Op =:= assoc end, R),
- {[{K,V} || {_,{_,K,V}} <- Assoc],
- [{K,V} || {_,{_,K,V}} <- Exact]}.
-
-cg_map_pairs_1([{map_pair_assoc,{_,Kv}=K,V}|T], Order) ->
- [{Kv,{Order,assoc,K,V}}|cg_map_pairs_1(T, Order+1)];
-cg_map_pairs_1([{map_pair_exact,{_,Kv}=K,V}|T], Order) ->
- [{Kv,{Order,exact,K,V}}|cg_map_pairs_1(T, Order+1)];
-cg_map_pairs_1([], _) -> [].
-
-%% map_pair_op_and_key({_,Op,K,_}) -> {Operator,Key}
-%% Determine the operator and key to use. Throw a 'badarg'
-%% exception if there are contradictory exact updates.
-
-map_pair_op_and_key(L) ->
- case [K || {_,exact,K,_} <- L] of
- [K] ->
- %% There is a single ':=' operator. Use that key.
- {exact,K};
- [K|T] ->
- %% There is more than one ':=' operator. All of them
- %% must have the same key.
- case lists:all(fun(E) -> E =:= K end, T) of
- true ->
- {exact,K};
- false ->
- %% Some keys are different, e.g. 1 and 1.0.
- throw(badarg)
- end;
- [] ->
- %% Only '=>' operators. Use the first key in the list.
- [{_,assoc,K,_}|_] = L,
- {assoc,K}
- end.
-
-termsort(Ls) ->
- lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Ls).
+map_pair_strip_and_termsort(Es) ->
+ %% format in
+ %% [{map_pair,K,V}]
+ %% where K is for example {integer, 1} and we want to sort on 1.
+ Ls = [{K,V}||{_,K,V}<-Es],
+ lists:sort(fun ({{_,A},_}, {{_,B},_}) -> erts_internal:cmp_term(A,B) =< 0;
+ ({nil,_}, {{_,B},_}) -> [] =< B;
+ ({{_,A},_}, {nil,_}) -> A =< []
+ end, Ls).
%%%
%%% Code generation for constructing binaries.
@@ -2085,7 +2026,7 @@ load_vars(Vs, Regs) ->
foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).
%% put_reg(Val, Regs) -> Regs.
-%% find_reg(Val, Regs) -> ok{r{R}} | error.
+%% find_reg(Val, Regs) -> {ok,r{R}} | error.
%% fetch_reg(Val, Regs) -> r{R}.
%% Functions to interface the registers.
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index e30bfa729c..a548ba2f7c 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -74,10 +74,11 @@
-export([module/2,format_error/1]).
-import(lists, [reverse/1,reverse/2,map/2,member/2,foldl/3,foldr/3,mapfoldl/3,
- splitwith/2,keyfind/3,sort/1,foreach/2]).
+ splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]).
-import(ordsets, [add_element/2,del_element/2,is_element/2,
union/1,union/2,intersection/2,subtract/2]).
--import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1]).
+-import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1,
+ ann_c_map/2, ann_c_map/3]).
-include("core_parse.hrl").
@@ -101,6 +102,8 @@
-record(ireceive2, {anno=#a{},clauses,timeout,action}).
-record(iset, {anno=#a{},var,arg}).
-record(itry, {anno=#a{},args,vars,body,evars,handler}).
+-record(ifilter, {anno=#a{},arg}).
+-record(igen, {anno=#a{},acc_pat,acc_guard,skip_pat,tail,tail_pat,arg}).
-type iapply() :: #iapply{}.
-type ibinary() :: #ibinary{}.
@@ -117,10 +120,13 @@
-type ireceive2() :: #ireceive2{}.
-type iset() :: #iset{}.
-type itry() :: #itry{}.
+-type ifilter() :: #ifilter{}.
+-type igen() :: #igen{}.
-type i() :: iapply() | ibinary() | icall() | icase() | icatch()
| iclause() | ifun() | iletrec() | imatch() | iprimop()
- | iprotect() | ireceive1() | ireceive2() | iset() | itry().
+ | iprotect() | ireceive1() | ireceive2() | iset() | itry()
+ | ifilter() | igen().
-type warning() :: {file:filename(), [{integer(), module(), term()}]}.
@@ -226,13 +232,13 @@ guard(Gs0, St0) ->
Gt1 = guard_tests(Gt0),
L = element(2, Gt1),
{op,L,'or',Gt1,Rhs}
- end, guard_tests(last(Gs0)), first(Gs0)),
+ end, guard_tests(last(Gs0)), droplast(Gs0)),
{Gs,St} = gexpr_top(Gs1, St0#core{in_guard=true}),
{Gs,St#core{in_guard=false}}.
guard_tests(Gs) ->
L = element(2, hd(Gs)),
- {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}.
+ {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), droplast(Gs))}.
%% gexpr_top(Expr, State) -> {Cexpr,State}.
%% Generate an internal core expression of a guard test. Explicitly
@@ -269,51 +275,67 @@ gexpr({op,L,'orelse',E1,E2}, Bools, St0) ->
True = {atom,L,true},
E = make_bool_switch_guard(L, E1, V, True, E2),
gexpr(E, Bools, St);
-gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
+gexpr({op,Line,Op,L,R}=E, Bools, St) ->
case erl_internal:bool_op(Op, 2) of
- true ->
- {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
- {Ll,Llps,St2} = force_safe(Le, St1),
- {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
- {Rl,Rlps,St4} = force_safe(Re, St3),
- Anno = lineno_anno(Line, St4),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
- false ->
- gexpr_test(Call, Bools0, St0)
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
-gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
- case Op of
- 'not' ->
- {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
- case Ae0 of
- #icall{module=#c_literal{val=erlang},
- name=#c_literal{val='=:='},
- args=[E,#c_literal{val=true}]}=EqCall ->
- %%
- %% Doing the following transformation
- %% not(Expr =:= true) ==> Expr =:= false
- %% will help eliminating redundant is_boolean/1 tests.
- %%
- Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
- {Al,Alps,St2} = force_safe(Ae, St1),
- {Al,Aps ++ Alps,Bools,St2};
- Ae ->
- {Al,Alps,St2} = force_safe(Ae, St1),
- Anno = lineno_anno(Line, St2),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Al]},Aps ++ Alps,Bools,St2}
- end;
- _ ->
- gexpr_test(Call, Bools0, St0)
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,Op}},[L,R]}=E, Bools, St) ->
+ case erl_internal:bool_op(Op, 2) of
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
+gexpr({op,Line,'not',A}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,'not'}},[A]}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
gexpr(E0, Bools, St0) ->
gexpr_test(E0, Bools, St0).
+%% gexpr_not(L, R, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate a guard for boolean operators
+
+gexpr_bool(Op, L, R, Bools0, St0, Line) ->
+ {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
+ {Ll,Llps,St2} = force_safe(Le, St1),
+ {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
+ {Rl,Rlps,St4} = force_safe(Re, St3),
+ Anno = lineno_anno(Line, St4),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val=Op},
+ args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4}.
+
+%% gexpr_not(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate an erlang:'not'/1 guard test.
+
+gexpr_not(A, Bools0, St0, Line) ->
+ {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
+ case Ae0 of
+ #icall{module=#c_literal{val=erlang},
+ name=#c_literal{val='=:='},
+ args=[E,#c_literal{val=true}]}=EqCall ->
+ %%
+ %% Doing the following transformation
+ %% not(Expr =:= true) ==> Expr =:= false
+ %% will help eliminating redundant is_boolean/1 tests.
+ %%
+ Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ {Al,Aps ++ Alps,Bools,St2};
+ Ae ->
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ Anno = lineno_anno(Line, St2),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val='not'},
+ args=[Al]},Aps ++ Alps,Bools,St2}
+ end.
+
%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
%% Generate a guard test. At this stage we must be sure that we have
%% a proper boolean value here so wrap things with an true test if we
@@ -330,7 +352,8 @@ gexpr_test(E0, Bools0, St0) ->
#icall{anno=Anno,module=#c_literal{val=erlang},name=#c_literal{val=N},args=As} ->
Ar = length(As),
case erl_internal:type_test(N, Ar) orelse
- erl_internal:comp_op(N, Ar) of
+ erl_internal:comp_op(N, Ar) orelse
+ erl_internal:bool_op(N, Ar) of
true -> {E1,Eps0,Bools0,St1};
false ->
Lanno = Anno#a.anno,
@@ -479,8 +502,9 @@ expr({cons,L,H0,T0}, St0) ->
{T1,Tps,St2} = safe(T0, St1),
A = lineno_anno(L, St2),
{ann_c_cons(A, H1, T1),Hps ++ Tps,St2};
-expr({lc,L,E,Qs}, St) ->
- lc_tq(L, E, Qs, #c_literal{anno=lineno_anno(L, St),val=[]}, St);
+expr({lc,L,E,Qs0}, St0) ->
+ {Qs1,St1} = preprocess_quals(L, Qs0, St0),
+ lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
expr({bc,L,E,Qs}, St) ->
bc_tq(L, E, Qs, {nil,L}, St);
expr({tuple,L,Es0}, St0) ->
@@ -490,14 +514,33 @@ expr({tuple,L,Es0}, St0) ->
expr({map,L,Es0}, St0) ->
% erl_lint should make sure only #{ K => V } are allowed
% in map construction.
- {Es1,Eps,St1} = map_pair_list(Es0, St0),
- A = lineno_anno(L, St1),
- {#c_map{anno=A,es=Es1},Eps,St1};
+ try map_pair_list(Es0, St0) of
+ {Es1,Eps,St1} ->
+ A = lineno_anno(L, St1),
+ {ann_c_map(A,Es1),Eps,St1}
+ catch
+ throw:{bad_map,Warning} ->
+ St = add_warning(L, Warning, St0),
+ LineAnno = lineno_anno(L, St),
+ As = [#c_literal{anno=LineAnno,val=badarg}],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_literal{anno=LineAnno,val=erlang},
+ name=#c_literal{anno=LineAnno,val=error},
+ args=As},[],St}
+ end;
expr({map,L,M0,Es0}, St0) ->
- {M1,Mps,St1} = safe(M0, St0),
- {Es1,Eps,St2} = map_pair_list(Es0, St1),
- A = lineno_anno(L, St2),
- {#c_map{anno=A,var=M1,es=Es1},Mps++Eps,St2};
+ try expr_map(M0,Es0,lineno_anno(L, St0),St0) of
+ {_,_,_}=Res -> Res
+ catch
+ throw:{bad_map,Warning} ->
+ St = add_warning(L, Warning, St0),
+ LineAnno = lineno_anno(L, St),
+ As = [#c_literal{anno=LineAnno,val=badarg}],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_literal{anno=LineAnno,val=erlang},
+ name=#c_literal{anno=LineAnno,val=error},
+ args=As},[],St}
+ end;
expr({bin,L,Es0}, St0) ->
try expr_bin(Es0, lineno_anno(L, St0), St0) of
{_,_,_}=Res -> Res
@@ -513,7 +556,7 @@ expr({bin,L,Es0}, St0) ->
end;
expr({block,_,Es0}, St0) ->
%% Inline the block directly.
- {Es1,St1} = exprs(first(Es0), St0),
+ {Es1,St1} = exprs(droplast(Es0), St0),
{E1,Eps,St2} = expr(last(Es0), St1),
{E1,Es1 ++ Eps,St2};
expr({'if',L,Cs0}, St0) ->
@@ -617,7 +660,7 @@ expr({call,Lc,{atom,Lf,F},As0}, St0) ->
Op = #c_var{anno=lineno_anno(Lf, St1),name={F,length(As1)}},
{#iapply{anno=#a{anno=lineno_anno(Lc, St1)},op=Op,args=As1},Aps,St1};
expr({call,L,FunExp,As0}, St0) ->
- {Fun,Fps,St1} = safe(FunExp, St0),
+ {Fun,Fps,St1} = safe_fun(length(As0), FunExp, St0),
{As1,Aps,St2} = safe_list(As0, St1),
Lanno = lineno_anno(L, St2),
{#iapply{anno=#a{anno=Lanno},op=Fun,args=As1},Fps ++ Aps,St2};
@@ -647,7 +690,7 @@ expr({match,L,P0,E0}, St0) ->
Other when not is_atom(Other) ->
{#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps,St4}
end;
-expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) ->
+expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->
%% Optimise '++' here because of the list comprehension algorithm.
%%
%% To avoid achieving quadratic complexity if there is a chain of
@@ -655,7 +698,8 @@ expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) ->
%% evaluation of More now. Evaluating More here could also reduce the
%% number variables in the environment for letrec.
{Mc,Mps,St1} = safe(More, St0),
- {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St1),
+ {Qs,St2} = preprocess_quals(Llc, Qs0, St1),
+ {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2),
{Y,Mps++Yps,St};
expr({op,L,'andalso',E1,E2}, St0) ->
{#c_var{name=V0},St} = new_var(L, St0),
@@ -706,22 +750,58 @@ make_bool_switch_guard(L, E, V, T, F) ->
{clause,NegL,[V],[],[V]}
]}.
+expr_map(M0,Es0,A,St0) ->
+ {M1,Mps,St1} = safe(M0, St0),
+ case is_valid_map_src(M1) of
+ true ->
+ case {M1,Es0} of
+ {#c_var{}, []} ->
+ %% transform M#{} to is_map(M)
+ {Vpat,St2} = new_var(St1),
+ {Fpat,St3} = new_var(St2),
+ Cs = [#iclause{
+ anno=A,
+ pats=[Vpat],
+ guard=[#icall{anno=#a{anno=A},
+ module=#c_literal{anno=A,val=erlang},
+ name=#c_literal{anno=A,val=is_map},
+ args=[Vpat]}],
+ body=[Vpat]}],
+ Fc = fail_clause([Fpat], A, #c_literal{val=badarg}),
+ {#icase{anno=#a{anno=A},args=[M1],clauses=Cs,fc=Fc},Mps,St3};
+ {_,_} ->
+ {Es1,Eps,St2} = map_pair_list(Es0, St1),
+ {ann_c_map(A,M1,Es1),Mps++Eps,St2}
+ end;
+ false -> throw({bad_map,bad_map})
+ end.
+
+is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true;
+is_valid_map_src(#c_map{}) -> true;
+is_valid_map_src(#c_var{}) -> true;
+is_valid_map_src(_) -> false.
+
map_pair_list(Es, St) ->
foldr(fun
({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
+ ok = ensure_valid_map_key(K),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
+ ok = ensure_valid_map_key(K),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
end, {[],[],St}, Es).
+ensure_valid_map_key(#c_literal{}) -> ok;
+ensure_valid_map_key(_) -> throw({bad_map,bad_map_key}).
+
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
try_exception(Ecs0, St0) ->
@@ -889,133 +969,45 @@ fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) ->
%% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
%% This TQ from Simon PJ pp 127-138.
-%% This gets a bit messy as we must transform all directly here. We
-%% recognise guard tests and try to fold them together and join to a
-%% preceding generators, this should give us better and more compact
-%% code.
-lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+lc_tq(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard,
+ skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
+ arg={Pre,Arg}}|Qs], Mc, St0) ->
{Name,St1} = new_fun_name("lc", St0),
- {Head,St2} = new_var(St1),
- {Tname,St3} = new_var_name(St2),
- LA = lineno_anno(Line, St3),
- LAnno = #a{anno=LA},
- Tail = #c_var{anno=LA,name=Tname},
- {Arg,St4} = new_var(St3),
- {Nc,[],St5} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St4),
- {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat!
- {Lc,Lps,St7} = lc_tq(Line, E, Qs1, Nc, St6),
- {Pc,St8} = list_gen_pattern(P, Line, St7),
- {Gc,Gps,St9} = safe(G, St8), %Will be a function argument!
- Fc = function_clause([Arg], LA, {Name,1}),
-
- %% Avoid constructing a default clause if the list comprehension
- %% only has a variable as generator and there are no guard
- %% tests. In other words, if the comprehension is equivalent to
- %% lists:map/2.
- Cs0 = case {Guardc, Pc} of
- {[], #c_var{}} ->
- [#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]}],guard=[],
- body=[Mc]}];
- _ ->
- [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[ann_c_cons(LA, Head, Tail)],
- guard=[],
- body=[Nc]},
- #iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]}],guard=[],
- body=[Mc]}]
- end,
- Cs = case Pc of
- nomatch -> Cs0;
- _ ->
- [#iclause{anno=LAnno,
- pats=[ann_c_cons(LA, Pc, Tail)],
- guard=Guardc,
- body=Lps ++ [Lc]}|Cs0]
- end,
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,1}},
- args=[Gc]}]},
- [],St9};
-lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Name,St1} = new_fun_name("blc", St0),
LA = lineno_anno(Line, St1),
LAnno = #a{anno=LA},
- HeadBinPattern = pattern(P, St1),
- #c_binary{segments=Ps0} = HeadBinPattern,
- {Ps,Tail,St2} = append_tail_segment(Ps0, St1),
- {EPs,St3} = emasculate_segments(Ps, St2),
- Pattern = HeadBinPattern#c_binary{segments=Ps},
- EPattern = HeadBinPattern#c_binary{segments=EPs},
- {Arg,St4} = new_var(St3),
- {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
- Tname = Tail#c_var.name,
- {Nc,[],St6} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St5),
- {Bc,Bps,St7} = lc_tq(Line, E, Qs1, Nc, St6),
- {Gc,Gps,St10} = safe(G, St7), %Will be a function argument!
- Fc = function_clause([Arg], LA, {Name,1}),
- {TailSegList,_,St} = append_tail_segment([], St10),
- Cs = [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[Pattern],
- guard=Guardc,
- body=Bps ++ [Bc]},
- #iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[EPattern],
- guard=[],
- body=[#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,1}},
- args=[Tail]}]},
- #iclause{anno=LAnno,
- pats=[#c_binary{anno=LA,segments=TailSegList}],guard=[],
- body=[Mc]}],
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,1}},
- args=[Gc]}]},
- [],St};
-lc_tq(Line, E, [Fil0|Qs0], Mc, St0) ->
- %% Special case sequences guard tests.
- LA = lineno_anno(element(2, Fil0), St0),
- LAnno = #a{anno=LA},
- case is_guard_test(Fil0) of
- true ->
- {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Lc,Lps,St1} = lc_tq(Line, E, Qs1, Mc, St0),
- {Gs,St2} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat!
- {#icase{anno=LAnno,
- args=[],
- clauses=[#iclause{anno=LAnno,pats=[],
- guard=Gs,body=Lps ++ [Lc]}],
- fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[],guard=[],body=[Mc]}},
- [],St2};
- false ->
- {Lc,Lps,St1} = lc_tq(Line, E, Qs0, Mc, St0),
- {Fpat,St2} = new_var(St1),
- Fc = fail_clause([Fpat], LA,
- c_tuple([#c_literal{val=case_clause},Fpat])),
- %% Do a novars little optimisation here.
- {Filc,Fps,St3} = novars(Fil0, St2),
- {#icase{anno=LAnno,
- args=[Filc],
- clauses=[#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=true}],
- guard=[],
- body=Lps ++ [Lc]},
- #iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[#c_literal{anno=LA,val=false}],
- guard=[],
- body=[Mc]}],
- fc=Fc},
- Fps,St3}
- end;
+ F = #c_var{anno=LA,name={Name,1}},
+ Nc = #iapply{anno=GAnno,op=F,args=[Tail]},
+ {Var,St2} = new_var(St1),
+ Fc = function_clause([Var], LA, {Name,1}),
+ TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]},
+ Cs0 = case {AccPat,AccGuard} of
+ {SkipPat,[]} ->
+ %% Skip and accumulator patterns are the same and there is
+ %% no guard, no need to generate a skip clause.
+ [TailClause];
+ _ ->
+ [#iclause{anno=#a{anno=[compiler_generated|LA]},
+ pats=[SkipPat],guard=[],body=[Nc]},
+ TailClause]
+ end,
+ {Cs,St4} = case AccPat of
+ nomatch ->
+ %% The accumulator pattern never matches, no need
+ %% for an accumulator clause.
+ {Cs0,St2};
+ _ ->
+ {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2),
+ {[#iclause{anno=LAnno,pats=[AccPat],guard=AccGuard,
+ body=Lps ++ [Lc]}|Cs0],
+ St3}
+ end,
+ Fun = #ifun{anno=LAnno,id=[],vars=[Var],clauses=Cs,fc=Fc},
+ {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,1},Fun}],
+ body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg]}]},
+ [],St4};
+lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
+ filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5);
lc_tq(Line, E0, [], Mc0, St0) ->
{H1,Hps,St1} = safe(E0, St0),
{T1,Tps,St} = force_safe(Mc0, St1),
@@ -1025,143 +1017,60 @@ lc_tq(Line, E0, [], Mc0, St0) ->
%% bc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.
%% This TQ from Gustafsson ERLANG'05.
-%% This gets a bit messy as we must transform all directly here. We
-%% recognise guard tests and try to fold them together and join to a
-%% preceding generators, this should give us better and more compact
-%% code.
%% More could be transformed before calling bc_tq.
-bc_tq(Line, Exp, Qualifiers, _, St0) ->
+bc_tq(Line, Exp, Qs0, _, St0) ->
{BinVar,St1} = new_var(St0),
- {Sz,SzPre,St2} = bc_initial_size(Exp, Qualifiers, St1),
- {E,BcPre,St} = bc_tq1(Line, Exp, Qualifiers, BinVar, St2),
+ {Sz,SzPre,St2} = bc_initial_size(Exp, Qs0, St1),
+ {Qs,St3} = preprocess_quals(Line, Qs0, St2),
+ {E,BcPre,St} = bc_tq1(Line, Exp, Qs, BinVar, St3),
Pre = SzPre ++
[#iset{var=BinVar,
arg=#iprimop{name=#c_literal{val=bs_init_writable},
args=[Sz]}}] ++ BcPre,
{E,Pre,St}.
-bc_tq1(Line, E, [{generate,Lg,P,G}|Qs0], AccExpr, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Name,St1} = new_fun_name("lbc", St0),
- LA = lineno_anno(Line, St1),
- {[Head,Tail,AccVar],St2} = new_vars(LA, 3, St1),
- LAnno = #a{anno=LA},
- {Arg,St3} = new_var(St2),
- NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name},
- {var,Lg,AccVar#c_var.name}]},
- {Guardc,St4} = lc_guard_tests(Gs, St3), %These are always flat!
- {Lc,Lps,St5} = bc_tq1(Line, E, Qs1, AccVar, St4),
- {Nc,Nps,St6} = expr(NewMore, St5),
- {Pc,St7} = list_gen_pattern(P, Line, St6),
- {Gc,Gps,St8} = safe(G, St7), %Will be a function argument!
- Fc = function_clause([Arg,AccVar], LA, {Name,2}),
- Cs0 = case {Guardc, Pc} of
- {[], #c_var{}} ->
- [#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[],
- body=[AccVar]}];
- _ ->
- [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[ann_c_cons(LA, Head, Tail),AccVar],
- guard=[],
- body=Nps ++ [Nc]},
- #iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[],
- body=[AccVar]}]
- end,
- Cs = case Pc of
- nomatch -> Cs0;
- _ ->
- Body = Lps ++ Nps ++ [#iset{var=AccVar,arg=Lc},Nc],
- [#iclause{anno=LAnno,
- pats=[ann_c_cons(LA,Pc,Tail),AccVar],
- guard=Guardc,
- body=Body}|Cs0]
- end,
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,2}},
- args=[Gc,AccExpr]}]},
- [],St8};
-bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+bc_tq1(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard,
+ skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
+ arg={Pre,Arg}}|Qs], Mc, St0) ->
{Name,St1} = new_fun_name("lbc", St0),
LA = lineno_anno(Line, St1),
- {AccVar,St2} = new_var(LA, St1),
- LAnno = #a{anno=LA},
- HeadBinPattern = pattern(P, St2),
- #c_binary{segments=Ps0} = HeadBinPattern,
- {Ps,Tail,St3} = append_tail_segment(Ps0, St2),
- {EPs,St4} = emasculate_segments(Ps, St3),
- Pattern = HeadBinPattern#c_binary{segments=Ps},
- EPattern = HeadBinPattern#c_binary{segments=EPs},
- {Arg,St5} = new_var(St4),
- NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name},
- {var,Lg,AccVar#c_var.name}]},
- {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat!
- {Bc,Bps,St7} = bc_tq1(Line, E, Qs1, AccVar, St6),
- {Nc,Nps,St8} = expr(NewMore, St7),
- {Gc,Gps,St9} = safe(G, St8), %Will be a function argument!
- Fc = function_clause([Arg,AccVar], LA, {Name,2}),
- Body = Bps ++ Nps ++ [#iset{var=AccVar,arg=Bc},Nc],
- {TailSegList,_,St} = append_tail_segment([], St9),
- Cs = [#iclause{anno=LAnno,
- pats=[Pattern,AccVar],
- guard=Guardc,
- body=Body},
- #iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[EPattern,AccVar],
- guard=[],
- body=Nps ++ [Nc]},
- #iclause{anno=LAnno,
- pats=[#c_binary{anno=LA,segments=TailSegList},AccVar],
- guard=[],
- body=[AccVar]}],
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_var{anno=LA,name={Name,2}},
- args=[Gc,AccExpr]}]},
- [],St};
-bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) ->
- %% Special case sequences guard tests.
- LA = lineno_anno(element(2, Fil0), St0),
LAnno = #a{anno=LA},
- case is_guard_test(Fil0) of
- true ->
- {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Bc,Bps,St1} = bc_tq1(Line, E, Qs1, AccVar, St0),
- {Gs,St} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat!
- {#icase{anno=LAnno,
- args=[],
- clauses=[#iclause{anno=LAnno,
- pats=[],
- guard=Gs,body=Bps ++ [Bc]}],
- fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[],guard=[],body=[AccVar]}},
- [],St};
- false ->
- {Bc,Bps,St1} = bc_tq1(Line, E, Qs0, AccVar, St0),
- {Fpat,St2} = new_var(St1),
- Fc = fail_clause([Fpat], LA,
- c_tuple([#c_literal{val=case_clause},Fpat])),
- %% Do a novars little optimisation here.
- {Filc,Fps,St} = novars(Fil0, St2),
- {#icase{anno=LAnno,
- args=[Filc],
- clauses=[#iclause{anno=LAnno,
- pats=[#c_literal{anno=LA,val=true}],
- guard=[],
- body=Bps ++ [Bc]},
- #iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
- pats=[#c_literal{anno=LA,val=false}],
- guard=[],
- body=[AccVar]}],
- fc=Fc},
- Fps,St}
- end;
+ {Vars=[_,AccVar],St2} = new_vars(LA, 2, St1),
+ F = #c_var{anno=LA,name={Name,2}},
+ Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]},
+ Fc = function_clause(Vars, LA, {Name,2}),
+ TailClause = #iclause{anno=LAnno,pats=[TailPat,AccVar],guard=[],
+ body=[AccVar]},
+ Cs0 = case {AccPat,AccGuard} of
+ {SkipPat,[]} ->
+ %% Skip and accumulator patterns are the same and there is
+ %% no guard, no need to generate a skip clause.
+ [TailClause];
+ _ ->
+ [#iclause{anno=#a{anno=[compiler_generated|LA]},
+ pats=[SkipPat,AccVar],guard=[],body=[Nc]},
+ TailClause]
+ end,
+ {Cs,St4} = case AccPat of
+ nomatch ->
+ %% The accumulator pattern never matches, no need
+ %% for an accumulator clause.
+ {Cs0,St2};
+ _ ->
+ {Bc,Bps,St3} = bc_tq1(Line, E, Qs, AccVar, St2),
+ Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc],
+ {[#iclause{anno=LAnno,
+ pats=[AccPat,AccVar],guard=AccGuard,
+ body=Body}|Cs0],
+ St3}
+ end,
+ Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc},
+ {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,2},Fun}],
+ body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]},
+ [],St4};
+bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
+ filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5);
bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
{E,Pre,St} = expr({bin,Bl,[{bin_element,Bl,
{var,Bl,AccVar#c_var.name},
@@ -1169,16 +1078,154 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
[binary,{unit,1}]}|Elements]}, St0),
#a{anno=A} = Anno0 = get_anno(E),
Anno = Anno0#a{anno=[compiler_generated,single_use|A]},
- %%Anno = Anno0#a{anno=[compiler_generated|A]},
{set_anno(E, Anno),Pre,St}.
+%% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) ->
+%% {Case,[PreExpr],State}.
+%% Transform an intermediate comprehension filter to its intermediate case
+%% representation.
+
+filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg={Pre,Arg}},
+ Mc, St0, Qs, TqFun) ->
+ %% The filter is an expression, it is compiled to a case of degree 1 with
+ %% 3 clauses, one accumulating, one skipping and the final one throwing
+ %% {case_clause,Value} where Value is the result of the filter and is not a
+ %% boolean.
+ {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0),
+ {FailPat,St2} = new_var(St1),
+ Fc = fail_clause([FailPat], LA,
+ c_tuple([#c_literal{val=case_clause},FailPat])),
+ {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[Arg],
+ clauses=[#iclause{anno=LAnno,
+ pats=[#c_literal{val=true}],guard=[],
+ body=Lps ++ [Lc]},
+ #iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
+ pats=[#c_literal{val=false}],guard=[],
+ body=[Mc]}],
+ fc=Fc},
+ Pre,St2};
+filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg=Guard},
+ Mc, St0, Qs, TqFun) when is_list(Guard) ->
+ %% Otherwise it is a guard, compiled to a case of degree 0 with 2 clauses,
+ %% the first matches if the guard succeeds and the comprehension continues
+ %% or the second one is selected and the current element is skipped.
+ {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0),
+ {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[],
+ clauses=[#iclause{anno=LAnno,pats=[],guard=Guard,body=Lps ++ [Lc]}],
+ fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]},
+ pats=[],guard=[],body=[Mc]}},
+ [],St1}.
+
+%% preprocess_quals(Line, [Qualifier], State) -> {[Qualifier'],State}.
+%% Preprocess a list of Erlang qualifiers into its intermediate representation,
+%% represented as a list of #igen{} and #ifilter{} records. We recognise guard
+%% tests and try to fold them together and join to a preceding generators, this
+%% should give us better and more compact code.
+
+preprocess_quals(Line, Qs, St) ->
+ preprocess_quals(Line, Qs, St, []).
+
+preprocess_quals(Line, [Q|Qs0], St0, Acc) ->
+ case is_generator(Q) of
+ true ->
+ {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0),
+ {Gen,St} = generator(Line, Q, Gs, St0),
+ preprocess_quals(Line, Qs, St, [Gen|Acc]);
+ false ->
+ LAnno = #a{anno=lineno_anno(get_anno(Q), St0)},
+ case is_guard_test(Q) of
+ true ->
+ %% When a filter is a guard test, its argument in the
+ %% #ifilter{} record is a list as returned by
+ %% lc_guard_tests/2.
+ {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0),
+ {Cg,St} = lc_guard_tests([Q|Gs], St0),
+ Filter = #ifilter{anno=LAnno,arg=Cg},
+ preprocess_quals(Line, Qs, St, [Filter|Acc]);
+ false ->
+ %% Otherwise, it is a pair {Pre,Arg} as in a generator
+ %% input.
+ {Ce,Pre,St} = novars(Q, St0),
+ Filter = #ifilter{anno=LAnno,arg={Pre,Ce}},
+ preprocess_quals(Line, Qs0, St, [Filter|Acc])
+ end
+ end;
+preprocess_quals(_, [], St, Acc) ->
+ {reverse(Acc),St}.
+
+is_generator({generate,_,_,_}) -> true;
+is_generator({b_generate,_,_,_}) -> true;
+is_generator(_) -> false.
+
+%%
+%% Generators are abstracted as sextuplets:
+%% - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr.
+%% - acc_guard is the list of guards immediately following the current
+%% generator in the qualifier list input.
+%% - skip_pat is the skip pattern, e.g. <<X,_:X,Tail/bitstring>> for
+%% <<X,1:X>> <= Expr.
+%% - tail is the variable used in AccPat and SkipPat bound to the rest of the
+%% generator input.
+%% - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list
+%% and bit string generators.
+%% - arg is a pair {Pre,Arg} where Pre is the list of expressions to be
+%% inserted before the comprehension function and Arg is the expression
+%% that it should be passed.
+%%
+
+%% generator(Line, Generator, Guard, State) -> {Generator',State}.
+%% Transform a given generator into its #igen{} representation.
+
+generator(Line, {generate,Lg,P0,E}, Gs, St0) ->
+ LA = lineno_anno(Line, St0),
+ GA = lineno_anno(Lg, St0),
+ {Head,St1} = list_gen_pattern(P0, Line, St0),
+ {[Tail,Skip],St2} = new_vars(2, St1),
+ {Cg,St3} = lc_guard_tests(Gs, St2),
+ {AccPat,SkipPat} = case Head of
+ #c_var{} ->
+ %% If the generator pattern is a variable, the
+ %% pattern from the accumulator clause can be
+ %% reused in the skip one. lc_tq and bc_tq1 takes
+ %% care of dismissing the latter in that case.
+ Cons = ann_c_cons(LA, Head, Tail),
+ {Cons,Cons};
+ nomatch ->
+ %% If it never matches, there is no need for
+ %% an accumulator clause.
+ {nomatch,ann_c_cons(LA, Skip, Tail)};
+ _ ->
+ {ann_c_cons(LA, Head, Tail),
+ ann_c_cons(LA, Skip, Tail)}
+ end,
+ {Ce,Pre,St4} = safe(E, St3),
+ Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
+ tail=Tail,tail_pat=#c_literal{anno=LA,val=[]},arg={Pre,Ce}},
+ {Gen,St4};
+generator(Line, {b_generate,Lg,P,E}, Gs, St0) ->
+ LA = lineno_anno(Line, St0),
+ GA = lineno_anno(Lg, St0),
+ Cp = #c_binary{segments=Segs} = pattern(P, St0),
+ %% The function append_tail_segment/2 keeps variable patterns as-is, making
+ %% it possible to have the same skip clause removal as with list generators.
+ {AccSegs,Tail,TailSeg,St1} = append_tail_segment(Segs, St0),
+ AccPat = Cp#c_binary{segments=AccSegs},
+ {Cg,St2} = lc_guard_tests(Gs, St1),
+ {SkipSegs,St3} = emasculate_segments(AccSegs, St2),
+ SkipPat = Cp#c_binary{segments=SkipSegs},
+ {Ce,Pre,St4} = safe(E, St3),
+ Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
+ tail=Tail,tail_pat=#c_binary{anno=LA,segments=[TailSeg]},
+ arg={Pre,Ce}},
+ {Gen,St4}.
+
append_tail_segment(Segs, St0) ->
{Var,St} = new_var(St0),
Tail = #c_bitstr{val=Var,size=#c_literal{val=all},
unit=#c_literal{val=1},
type=#c_literal{val=binary},
flags=#c_literal{val=[unsigned,big]}},
- {Segs++[Tail],Var,St}.
+ {Segs++[Tail],Var,Tail,St}.
emasculate_segments(Segs, St) ->
emasculate_segments(Segs, St, []).
@@ -1189,7 +1236,7 @@ emasculate_segments([B|Rest], St0, Acc) ->
{Var,St1} = new_var(St0),
emasculate_segments(Rest, St1, [B#c_bitstr{val=Var}|Acc]);
emasculate_segments([], St, Acc) ->
- {lists:reverse(Acc),St}.
+ {reverse(Acc),St}.
lc_guard_tests([], St) -> {[],St};
lc_guard_tests(Gs0, St0) ->
@@ -1434,6 +1481,15 @@ safe(E0, St0) ->
{Se,Sps,St2} = force_safe(E1, St1),
{Se,Eps ++ Sps,St2}.
+safe_fun(A0, E0, St0) ->
+ case safe(E0, St0) of
+ {#c_var{name={_,A1}}=E1,Eps,St1} when A1 =/= A0 ->
+ {V,St2} = new_var(St1),
+ {V,Eps ++ [#iset{var=V,arg=E1}],St2};
+ Result ->
+ Result
+ end.
+
safe_list(Es, St) ->
foldr(fun (E, {Ces,Esp,St0}) ->
{Ce,Ep,St1} = safe(E, St0),
@@ -1505,15 +1561,59 @@ pattern({cons,L,H,T}, St) ->
pattern({tuple,L,Ps}, St) ->
ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St));
pattern({map,L,Ps}, St) ->
- #c_map{anno=lineno_anno(L, St), es=sort(pattern_list(Ps, St))};
-pattern({map_field_exact,L,K,V}, St) ->
+ #c_map{anno=lineno_anno(L, St), es=pattern_map_pairs(Ps, St)};
+pattern({bin,L,Ps}, St) ->
+ %% We don't create a #ibinary record here, since there is
+ %% no need to hold any used/new annotations in a pattern.
+ #c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)};
+pattern({match,_,P1,P2}, St) ->
+ pat_alias(pattern(P1, St), pattern(P2, St)).
+
+%% pattern_map_pairs([MapFieldExact],State) -> [#c_map_pairs{}]
+pattern_map_pairs(Ps, St) ->
+ %% check literal key uniqueness (dict is needed)
+ %% pattern all pairs
+ {CMapPairs, Kdb} = lists:mapfoldl(fun
+ (P,Kdbi) ->
+ #c_map_pair{key=Ck,val=Cv} = CMapPair = pattern_map_pair(P,St),
+ K = core_lib:literal_value(Ck),
+ case dict:find(K,Kdbi) of
+ {ok, Vs} ->
+ {CMapPair, dict:store(K,[Cv|Vs],Kdbi)};
+ _ ->
+ {CMapPair, dict:store(K,[Cv],Kdbi)}
+ end
+ end, dict:new(), Ps),
+ pattern_alias_map_pairs(CMapPairs,Kdb,dict:new(),St).
+
+pattern_alias_map_pairs([],_,_,_) -> [];
+pattern_alias_map_pairs([#c_map_pair{key=Ck}=Pair|Pairs],Kdb,Kset,St) ->
+ %% alias same keys if needed
+ K = core_lib:literal_value(Ck),
+ case dict:find(K,Kset) of
+ {ok,processed} ->
+ pattern_alias_map_pairs(Pairs,Kdb,Kset,St);
+ _ ->
+ Cvs = dict:fetch(K,Kdb),
+ Cv = pattern_alias_map_pair_patterns(Cvs),
+ Kset1 = dict:store(K, processed, Kset),
+ [Pair#c_map_pair{val=Cv}|pattern_alias_map_pairs(Pairs,Kdb,Kset1,St)]
+ end.
+
+pattern_alias_map_pair_patterns([Cv]) -> Cv;
+pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) ->
+ pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]).
+
+pattern_map_pair({map_field_exact,L,K,V}, St) ->
%% FIXME: Better way to construct literals? or missing case
%% {Key,_,_} = expr(K, St),
Key = case K of
{bin,L,Es0} ->
case constant_bin(Es0) of
error ->
- throw(badmatch);
+ %% this will throw a cryptic error message
+ %% but it is better than nothing
+ throw(nomatch);
Bin ->
#c_literal{anno=lineno_anno(L,St),val=Bin}
end;
@@ -1523,13 +1623,8 @@ pattern({map_field_exact,L,K,V}, St) ->
#c_map_pair{anno=lineno_anno(L, St),
op=#c_literal{val=exact},
key=Key,
- val=pattern(V, St)};
-pattern({bin,L,Ps}, St) ->
- %% We don't create a #ibinary record here, since there is
- %% no need to hold any used/new annotations in a pattern.
- #c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)};
-pattern({match,_,P1,P2}, St) ->
- pat_alias(pattern(P1, St), pattern(P2, St)).
+ val=pattern(V, St)}.
+
%% pat_bin([BinElement], State) -> [BinSeg].
@@ -1588,15 +1683,6 @@ pat_alias_list(_, _) -> throw(nomatch).
pattern_list(Ps, St) -> [pattern(P, St) || P <- Ps].
-%% first([A]) -> [A].
-%% last([A]) -> A.
-
-first([_]) -> [];
-first([H|T]) -> [H|first(T)].
-
-last([L]) -> L;
-last([_|T]) -> last(T).
-
%% make_vars([Name]) -> [{Var,Name}].
make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ].
@@ -1679,13 +1765,13 @@ uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) ->
uguard([], [], _, St) -> {[],St};
uguard(Pg, [], Ks, St) ->
%% No guard, so fold together equality tests.
- uguard(first(Pg), [last(Pg)], Ks, St);
+ uguard(droplast(Pg), [last(Pg)], Ks, St);
uguard(Pg, Gs0, Ks, St0) ->
%% Gs0 must contain at least one element here.
{Gs3,St5} = foldr(fun (T, {Gs1,St1}) ->
{L,St2} = new_var(St1),
{R,St3} = new_var(St2),
- {[#iset{var=L,arg=T}] ++ first(Gs1) ++
+ {[#iset{var=L,arg=T}] ++ droplast(Gs1) ++
[#iset{var=R,arg=last(Gs1)},
#icall{anno=#a{}, %Must have an #a{}
module=#c_literal{val=erlang},
@@ -1754,13 +1840,16 @@ uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
{B1,St2} = uexprs(B0, Ks, St1),
Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
{#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
-uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
+uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
%% As0 will never generate new variables.
{As1,St1} = uexpr_list(As0, Ks, St0),
{Cs1,St2} = uclauses(Cs0, Ks, St1),
{Fc1,St3} = uclause(Fc0, Ks, St2),
Used = union(used_in_any(As1), used_in_any(Cs1)),
- New = new_in_all(Cs1),
+ New = case member(list_comprehension, Anno) of
+ true -> [];
+ false -> new_in_all(Cs1)
+ end,
{#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}, Ks0, St0) ->
Avs = lit_list_vars(As),
@@ -2088,7 +2177,8 @@ cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0,
RecVar = #c_var{name={Name,length(Ps)}},
Let = #c_let{vars=[#c_var{name=Name}],arg=RecVar,body=Body},
CFun1 = CFun0#c_fun{body=Let},
- Letrec = #c_letrec{defs=[{RecVar,CFun1}],
+ Letrec = #c_letrec{anno=A0#a.anno,
+ defs=[{RecVar,CFun1}],
body=RecVar},
{Letrec,[],Us1,St1}
end;
@@ -2134,6 +2224,8 @@ lit_vars(Lit) -> lit_vars(Lit, []).
lit_vars(#c_cons{hd=H,tl=T}, Vs) -> lit_vars(H, lit_vars(T, Vs));
lit_vars(#c_tuple{es=Es}, Vs) -> lit_list_vars(Es, Vs);
+lit_vars(#c_map{arg=V,es=Es}, Vs) -> lit_vars(V, lit_list_vars(Es, Vs));
+lit_vars(#c_map_pair{key=K,val=V}, Vs) -> lit_vars(K, lit_vars(V, Vs));
lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs);
lit_vars(_, Vs) -> Vs. %These are atomic
@@ -2224,7 +2316,11 @@ is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
format_error(nomatch) ->
"pattern cannot possibly match";
format_error(bad_binary) ->
- "binary construction will fail because of a type mismatch".
+ "binary construction will fail because of a type mismatch";
+format_error(bad_map_key) ->
+ "map construction will fail because of none literal key (large binaries are not literals)";
+format_error(bad_map) ->
+ "map construction will fail because of a type mismatch".
add_warning(Line, Term, #core{ws=Ws,file=[{file,File}]}=St) when Line >= 0 ->
St#core{ws=[{File,[{location(Line),?MODULE,Term}]}|Ws]};
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 9a2b1605ad..40d2f72b4c 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -81,7 +81,7 @@
-export([module/2,format_error/1]).
-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2,
- keymember/3,keyfind/3,partition/2]).
+ keymember/3,keyfind/3,partition/2,droplast/1,last/1]).
-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
-import(cerl, [c_tuple/1]).
@@ -272,10 +272,18 @@ expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
{Kes,Ep,St1} = atomic_list(Ces, Sub, St0),
{#k_tuple{anno=A,es=Kes},Ep,St1};
-expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) ->
- {Var,[],St1} = expr(Var0, Sub, St0),
- {Kes,Ep,St2} = map_pairs(Ces, Sub, St1),
- {#k_map{anno=A,var=Var,es=Kes},Ep,St2};
+expr(#c_map{anno=A,arg=Var,es=Ces}, Sub, St0) ->
+ try expr_map(A,Var,Ces,Sub,St0) of
+ {_,_,_}=Res -> Res
+ catch
+ throw:bad_map ->
+ St1 = add_warning(get_line(A), bad_map, A, St0),
+ Erl = #c_literal{val=erlang},
+ Name = #c_literal{val=error},
+ Args = [#c_literal{val=badarg}],
+ Error = #c_call{anno=A,module=Erl,name=Name,args=Args},
+ expr(Error, Sub, St1)
+ end;
expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
try atomic_bin(Cv, Sub, St0) of
{Kv,Ep,St1} ->
@@ -351,7 +359,7 @@ expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->
{Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here!
{Km,St3} = kmatch(Kvs, Ccs, Sub, St2),
Match = flatten_seq(build_match(Kvs, Km)),
- {last(Match),Pa ++ Pv ++ first(Match),St3};
+ {last(Match),Pa ++ Pv ++ droplast(Match),St3};
expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->
{Ke,Pe,St1} = atomic(Ce, Sub, St0), %Force this to be atomic!
{Rvar,St2} = new_var(St1),
@@ -497,15 +505,84 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
translate_fc(Args) ->
[#c_literal{val=function_clause},make_list(Args)].
-%% FIXME: Not completed
-map_pairs(Es, Sub, St) ->
- foldr(fun
- (#c_map_pair{op=#c_literal{val=Op},key=K0,val=V0}, {Kes,Esp,St0}) when
- Op =:= assoc; Op =:= exact -> %% assert Op
- {K,[],St1} = expr(K0, Sub, St0),
- {V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{op=Op,key=K,val=V}|Kes],Ep ++ Esp,St2}
- end, {[],[],St}, Es).
+expr_map(A,Var0,Ces,Sub,St0) ->
+ %% An extra pass of validation of Map src because of inlining
+ {Var,Mps,St1} = expr(Var0, Sub, St0),
+ case is_valid_map_src(Var) of
+ true ->
+ {Km,Eps,St2} = map_split_pairs(A, Var, Ces, Sub, St1),
+ {Km,Eps++Mps,St2};
+ false -> throw(bad_map)
+ end.
+
+is_valid_map_src(#k_map{}) -> true;
+is_valid_map_src(#k_literal{val=M}) when is_map(M) -> true;
+is_valid_map_src(#k_var{}) -> true;
+is_valid_map_src(_) -> false.
+
+map_split_pairs(A, Var, Ces, Sub, St0) ->
+ %% two steps
+ %% 1. force variables
+ %% 2. remove multiples
+ Pairs0 = [{Op,K,V} || #c_map_pair{op=#c_literal{val=Op},key=K,val=V} <- Ces],
+ {Pairs,Esp,St1} = foldr(fun
+ ({Op,K0,V0}, {Ops,Espi,Sti0}) when Op =:= assoc; Op =:= exact ->
+ {K,[],Sti1} = expr(K0, Sub, Sti0),
+ {V,Ep,Sti2} = atomic(V0, Sub, Sti1),
+ {[{Op,K,V}|Ops],Ep ++ Espi,Sti2}
+ end, {[],[],St0}, Pairs0),
+
+ case map_group_pairs(Pairs) of
+ {Assoc,[]} ->
+ Kes = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc],
+ {#k_map{anno=A,op=assoc,var=Var,es=Kes},Esp,St1};
+ {[],Exact} ->
+ Kes = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact],
+ {#k_map{anno=A,op=exact,var=Var,es=Kes},Esp,St1};
+ {Assoc,Exact} ->
+ Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc],
+ {Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1),
+ Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact],
+ {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Esp ++ Em,St2}
+
+ end.
+
+%% Group map by Assoc operations and Exact operations
+
+map_group_pairs(Es) ->
+ Groups = dict:to_list(map_group_pairs(Es,dict:new())),
+ partition(fun({_,{Op,_,_}}) -> Op =:= assoc end, Groups).
+
+map_group_pairs([{assoc,K,V}|Es0],Used0) ->
+ Used1 = case map_key_is_used(K,Used0) of
+ {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0);
+ {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0);
+ _ -> map_key_set_used(K,{assoc,K,V},Used0)
+ end,
+ map_group_pairs(Es0,Used1);
+map_group_pairs([{exact,K,V}|Es0],Used0) ->
+ Used1 = case map_key_is_used(K,Used0) of
+ {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0);
+ {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0);
+ _ -> map_key_set_used(K,{exact,K,V},Used0)
+ end,
+ map_group_pairs(Es0,Used1);
+map_group_pairs([],Used) ->
+ Used.
+
+map_key_set_used(K,How,Used) ->
+ dict:store(map_key_clean(K),How,Used).
+
+map_key_is_used(K,Used) ->
+ dict:find(map_key_clean(K),Used).
+
+%% Be explicit instead of using set_kanno(K,[])
+map_key_clean(#k_literal{val=V}) -> {k_literal,V};
+map_key_clean(#k_int{val=V}) -> {k_int,V};
+map_key_clean(#k_float{val=V}) -> {k_float,V};
+map_key_clean(#k_atom{val=V}) -> {k_atom,V};
+map_key_clean(#k_nil{}) -> k_nil.
+
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
%% Classify the call.
@@ -663,12 +740,8 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_tuple{anno=A,es=Kes},Osub1,St1};
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
- {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
- {#k_map{anno=A,es=Kes},Osub1,St1};
-pattern(#c_map_pair{op=#c_literal{val=exact},anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
- {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
- {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
- {#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2};
+ {Kes,Osub1,St1} = pattern_map_pairs(Ces, Isub, Osub0, St0),
+ {#k_map{anno=A,op=exact,es=Kes},Osub1,St1};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -683,6 +756,25 @@ flatten_alias(#c_alias{var=V,pat=P}) ->
{[V|Vs],Pat};
flatten_alias(Pat) -> {[],Pat}.
+pattern_map_pairs(Ces0, Isub, Osub0, St0) ->
+ %% It is assumed that all core keys are literals
+ %% It is later assumed that these keys are term sorted
+ %% so we need to sort them here
+ Ces1 = lists:sort(fun
+ (#c_map_pair{key=CkA},#c_map_pair{key=CkB}) ->
+ A = core_lib:literal_value(CkA),
+ B = core_lib:literal_value(CkB),
+ erts_internal:cmp_term(A,B) < 0
+ end, Ces0),
+ %% pattern the pair keys and values as normal
+ {Kes,{Osub1,St1}} = lists:mapfoldl(fun
+ (#c_map_pair{anno=A,key=Ck,val=Cv},{Osubi0,Sti0}) ->
+ {Kk,Osubi1,Sti1} = pattern(Ck, Isub, Osubi0, Sti0),
+ {Kv,Osubi2,Sti2} = pattern(Cv, Isub, Osubi1, Sti1),
+ {#k_map_pair{anno=A,key=Kk,val=Kv},{Osubi2,Sti2}}
+ end, {Osub0, St0}, Ces1),
+ {Kes,Osub1,St1}.
+
pattern_bin(Es, Isub, Osub0, St0) ->
{Kbin,{_,Osub},St} = pattern_bin_1(Es, Isub, Osub0, St0),
{Kbin,Osub,St}.
@@ -847,15 +939,6 @@ foldr2(Fun, Acc0, [E1|L1], [E2|L2]) ->
foldr2(Fun, Acc1, L1, L2);
foldr2(_, Acc, [], []) -> Acc.
-%% first([A]) -> [A].
-%% last([A]) -> A.
-
-last([L]) -> L;
-last([_|T]) -> last(T).
-
-first([_]) -> [];
-first([H|T]) -> [H|first(T)].
-
%% This code implements the algorithm for an optimizing compiler for
%% pattern matching given "The Implementation of Functional
%% Programming Languages" by Simon Peyton Jones. The code is much
@@ -1342,13 +1425,13 @@ get_match(#k_bin_int{}=BinInt, St0) ->
get_match(#k_tuple{es=Es}, St0) ->
{Mes,St1} = new_vars(length(Es), St0),
{#k_tuple{es=Mes},Mes,St1};
-get_match(#k_map{es=Es0}, St0) ->
+get_match(#k_map{op=exact,es=Es0}, St0) ->
{Mes,St1} = new_vars(length(Es0), St0),
{Es,_} = mapfoldl(fun
(#k_map_pair{}=Pair, [V|Vs]) ->
{Pair#k_map_pair{val=V},Vs}
end, Mes, Es0),
- {#k_map{es=Es},Mes,St1};
+ {#k_map{op=exact,es=Es},Mes,St1};
get_match(M, St) ->
{M,[],St}.
@@ -1365,9 +1448,8 @@ new_clauses(Cs0, U, St) ->
[S,N|As];
#k_bin_int{next=N} ->
[N|As];
- #k_map{es=Es} ->
- Vals = [V ||
- #k_map_pair{op=exact,val=V} <- Es],
+ #k_map{op=exact,es=Es} ->
+ Vals = [V || #k_map_pair{val=V} <- Es],
Vals ++ As;
_Other ->
As
@@ -1467,14 +1549,14 @@ arg_val(Arg, C) ->
_ ->
{set_kanno(S, []),U,T,Fs}
end;
- #k_map{es=Es} ->
+ #k_map{op=exact,es=Es} ->
Keys = [begin
- #k_map_pair{op=exact,key=#k_literal{val=Key}} = Pair,
+ #k_map_pair{key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
%% multiple keys may have the same name
%% do not use ordsets
- lists:sort(Keys)
+ lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Keys)
end.
%% ubody_used_vars(Expr, State) -> [UsedVar]
@@ -1885,7 +1967,7 @@ pat_vars(#k_tuple{es=Es}) ->
pat_list_vars(Es);
pat_vars(#k_map{es=Es}) ->
pat_list_vars(Es);
-pat_vars(#k_map_pair{op=exact,val=V}) ->
+pat_vars(#k_map_pair{val=V}) ->
pat_vars(V).
pat_list_vars(Ps) ->
@@ -1927,7 +2009,9 @@ format_error(nomatch_shadow) ->
format_error(bad_call) ->
"invalid module and/or function name; this call will always fail";
format_error(bad_segment_size) ->
- "binary construction will fail because of a type mismatch".
+ "binary construction will fail because of a type mismatch";
+format_error(bad_map) ->
+ "map construction will fail because of a type mismatch".
add_warning(none, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index c7886a070d..ab66445f73 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -38,8 +38,8 @@
-record(k_nil, {anno=[]}).
-record(k_tuple, {anno=[],es}).
--record(k_map, {anno=[],var,es}).
--record(k_map_pair, {anno=[],op,key,val}).
+-record(k_map, {anno=[],var,op,es}).
+-record(k_map_pair, {anno=[],key,val}).
-record(k_cons, {anno=[],hd,tl}).
-record(k_binary, {anno=[],segs}).
-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index edbd3f74f8..b33eba50eb 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -104,21 +104,30 @@ format_1(#k_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
$}
];
-format_1(#k_map{var=#k_var{}=Var,es=Es}, Ctxt) ->
- [$~,${,
+format_1(#k_map{var=#k_literal{val=M},op=assoc,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 ->
+ ["~{",
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ "}~"
+ ];
+format_1(#k_map{var=#k_literal{val=M},op=exact,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 ->
+ ["::{",
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ "}::"
+ ];
+format_1(#k_map{var=Var,op=assoc,es=Es}, Ctxt) ->
+ ["~{",
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
" | ",format_1(Var, Ctxt),
- $},$~
+ "}~"
];
-format_1(#k_map{es=Es}, Ctxt) ->
- [$~,${,
+format_1(#k_map{var=Var,op=exact,es=Es}, Ctxt) ->
+ ["::{",
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- $},$~
+ " | ",format_1(Var, Ctxt),
+ "}::"
];
-format_1(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) ->
- ["~<",format(K, Ctxt),",",format(V, Ctxt),">"];
-format_1(#k_map_pair{op=exact,key=K,val=V}, Ctxt) ->
- ["::<",format(K, Ctxt),",",format(V, Ctxt),">"];
+format_1(#k_map_pair{key=K,val=V}, Ctxt) ->
+ ["<",format(K, Ctxt),",",format(V, Ctxt),">"];
format_1(#k_binary{segs=S}, Ctxt) ->
["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
format_1(#k_bin_seg{next=Next}=S, Ctxt) ->
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index ae928e955c..cd4b5fd674 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -324,8 +324,7 @@ type(k_binary) -> binary;
type(k_bin_seg) -> bin_seg;
type(k_bin_int) -> bin_int;
type(k_bin_end) -> bin_end;
-type(k_map) -> map;
-type(k_map_pair) -> map_pair.
+type(k_map) -> map.
%% variable(Klit) -> Lit.
%% var_list([Klit]) -> [Lit].
@@ -367,12 +366,10 @@ literal(#k_bin_end{}, Ctxt) ->
{bin_end,Ctxt};
literal(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list(Es, Ctxt)};
-literal(#k_map{var=Var,es=Es}, Ctxt) ->
- {map,literal(Var, Ctxt),literal_list(Es, Ctxt)};
-literal(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) ->
- {map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)};
-literal(#k_map_pair{op=exact,key=K,val=V}, Ctxt) ->
- {map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)};
+literal(#k_map{op=Op,var=Var,es=Es}, Ctxt) ->
+ {map,Op,literal(Var, Ctxt),literal_list(Es, Ctxt)};
+literal(#k_map_pair{key=K,val=V}, Ctxt) ->
+ {map_pair,literal(K, Ctxt),literal(V, Ctxt)};
literal(#k_literal{val=V}, _Ctxt) ->
{literal,V}.
@@ -402,8 +399,8 @@ literal2(#k_bin_end{}, Ctxt) ->
{bin_end,Ctxt};
literal2(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list2(Es, Ctxt)};
-literal2(#k_map{es=Es}, Ctxt) ->
- {map,literal_list2(Es, Ctxt)};
+literal2(#k_map{op=Op,es=Es}, Ctxt) ->
+ {map,Op,literal_list2(Es, Ctxt)};
literal2(#k_map_pair{key=K,val=V}, Ctxt) ->
{map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}.
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 51b3064589..0b56a49cd6 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -26,6 +26,7 @@ MODULES= \
guard_SUITE \
inline_SUITE \
lc_SUITE \
+ map_SUITE \
match_SUITE \
misc_SUITE \
num_bif_SUITE \
@@ -47,6 +48,7 @@ NO_OPT= \
fun \
guard \
lc \
+ map \
match \
misc \
num_bif \
@@ -67,6 +69,7 @@ INLINE= \
fun \
guard \
lc \
+ map \
match \
misc \
num_bif \
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index 7bef0aa27c..b5408ecd8f 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -129,6 +129,10 @@ t_case_y(X, Y, Z) ->
Y =:= 100
end.
+-define(GUARD(E), if E -> true;
+ true -> false
+ end).
+
t_and_or(Config) when is_list(Config) ->
?line true = true and true,
?line false = true and false,
@@ -160,11 +164,16 @@ t_and_or(Config) when is_list(Config) ->
?line true = false or id(true),
?line false = false or id(false),
- ok.
+ True = id(true),
--define(GUARD(E), if E -> true;
- true -> false
- end).
+ false = ?GUARD(erlang:'and'(bar, True)),
+ false = ?GUARD(erlang:'or'(bar, True)),
+ false = ?GUARD(erlang:'not'(erlang:'and'(bar, True))),
+ false = ?GUARD(erlang:'not'(erlang:'not'(erlang:'and'(bar, True)))),
+
+ true = (fun (X = true) when X or true or X -> true end)(True),
+
+ ok.
t_andalso(Config) when is_list(Config) ->
Bs = [true,false],
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 9f15845d33..149b9bbb8f 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -34,7 +34,7 @@
otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1,
match_string/1,zero_width/1,bad_size/1,haystack/1,
cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
- no_partition/1]).
+ no_partition/1,calling_a_binary/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -59,7 +59,7 @@ groups() ->
matching_and_andalso,otp_7188,otp_7233,otp_7240,
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
- no_partition]}].
+ no_partition,calling_a_binary]}].
init_per_suite(Config) ->
@@ -1178,6 +1178,17 @@ no_partition_2([], a5) ->
no_partition_2(42.0, a6) ->
six.
+calling_a_binary(Config) when is_list(Config) ->
+ [] = call_binary(<<>>, []),
+ {'EXIT',{badarg,_}} = (catch call_binary(<<1>>, [])),
+ {'EXIT',{badarg,_}} = (catch call_binary(<<1,2,3>>, [])),
+ ok.
+
+call_binary(<<>>, Acc) ->
+ Acc;
+call_binary(<<H,T/bits>>, Acc) ->
+ T(<<Acc/binary,H>>).
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index de35ebc7bd..8cb7d1b55b 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -24,7 +24,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
- app_test/1,
+ app_test/1,appup_test/1,
file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1,
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, encrypted_abstr/1,
@@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [app_test, file_1, forms_2, module_mismatch, big_file, outdir,
+ [app_test, appup_test, file_1, forms_2, module_mismatch, big_file, outdir,
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, encrypted_abstr,
{group, bad_record_use}, strict_record,
@@ -71,6 +71,10 @@ end_per_group(_GroupName, Config) ->
app_test(Config) when is_list(Config) ->
?line ?t:app_test(compiler).
+%% Test that the Application upgrade file has no `basic' errors.";
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(compiler).
+
%% Tests that we can compile and run a simple Erlang program,
%% using compile:file/1.
@@ -286,57 +290,67 @@ cond_and_ifdef(Config) when is_list(Config) ->
ok.
listings(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(8)),
- ?line DataDir = ?config(data_dir, Config),
- ?line PrivDir = ?config(priv_dir, Config),
- ?line Simple = filename:join(DataDir, simple),
- ?line TargetDir = filename:join(PrivDir, listings),
- ?line ok = file:make_dir(TargetDir),
+ Dog = test_server:timetrap(test_server:minutes(8)),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ok = do_file_listings(DataDir, PrivDir, [
+ "simple",
+ "small",
+ "small_maps"
+ ]),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+do_file_listings(_, _, []) -> ok;
+do_file_listings(DataDir, PrivDir, [File|Files]) ->
+ Simple = filename:join(DataDir, File),
+ TargetDir = filename:join(PrivDir, listings),
+ ok = file:make_dir(TargetDir),
%% Test all dedicated listing options.
- ?line do_listing(Simple, TargetDir, 'S'),
- ?line do_listing(Simple, TargetDir, 'E'),
- ?line do_listing(Simple, TargetDir, 'P'),
- ?line do_listing(Simple, TargetDir, dpp, ".pp"),
- ?line do_listing(Simple, TargetDir, dabstr, ".abstr"),
- ?line do_listing(Simple, TargetDir, dexp, ".expand"),
- ?line do_listing(Simple, TargetDir, dcore, ".core"),
- ?line do_listing(Simple, TargetDir, doldinline, ".oldinline"),
- ?line do_listing(Simple, TargetDir, dinline, ".inline"),
- ?line do_listing(Simple, TargetDir, dcore, ".core"),
- ?line do_listing(Simple, TargetDir, dcopt, ".copt"),
- ?line do_listing(Simple, TargetDir, dsetel, ".dsetel"),
- ?line do_listing(Simple, TargetDir, dkern, ".kernel"),
- ?line do_listing(Simple, TargetDir, dlife, ".life"),
- ?line do_listing(Simple, TargetDir, dcg, ".codegen"),
- ?line do_listing(Simple, TargetDir, dblk, ".block"),
- ?line do_listing(Simple, TargetDir, dbool, ".bool"),
- ?line do_listing(Simple, TargetDir, dtype, ".type"),
- ?line do_listing(Simple, TargetDir, ddead, ".dead"),
- ?line do_listing(Simple, TargetDir, djmp, ".jump"),
- ?line do_listing(Simple, TargetDir, dclean, ".clean"),
- ?line do_listing(Simple, TargetDir, dpeep, ".peep"),
- ?line do_listing(Simple, TargetDir, dopt, ".optimize"),
+ do_listing(Simple, TargetDir, 'S'),
+ do_listing(Simple, TargetDir, 'E'),
+ do_listing(Simple, TargetDir, 'P'),
+ do_listing(Simple, TargetDir, dpp, ".pp"),
+ do_listing(Simple, TargetDir, dabstr, ".abstr"),
+ do_listing(Simple, TargetDir, dexp, ".expand"),
+ do_listing(Simple, TargetDir, dcore, ".core"),
+ do_listing(Simple, TargetDir, doldinline, ".oldinline"),
+ do_listing(Simple, TargetDir, dinline, ".inline"),
+ do_listing(Simple, TargetDir, dcore, ".core"),
+ do_listing(Simple, TargetDir, dcopt, ".copt"),
+ do_listing(Simple, TargetDir, dsetel, ".dsetel"),
+ do_listing(Simple, TargetDir, dkern, ".kernel"),
+ do_listing(Simple, TargetDir, dlife, ".life"),
+ do_listing(Simple, TargetDir, dcg, ".codegen"),
+ do_listing(Simple, TargetDir, dblk, ".block"),
+ do_listing(Simple, TargetDir, dbool, ".bool"),
+ do_listing(Simple, TargetDir, dtype, ".type"),
+ do_listing(Simple, TargetDir, ddead, ".dead"),
+ do_listing(Simple, TargetDir, djmp, ".jump"),
+ do_listing(Simple, TargetDir, dclean, ".clean"),
+ do_listing(Simple, TargetDir, dpeep, ".peep"),
+ do_listing(Simple, TargetDir, dopt, ".optimize"),
%% First clean up.
- ?line Listings = filename:join(PrivDir, listings),
- ?line lists:foreach(fun(F) -> ok = file:delete(F) end,
- filelib:wildcard(filename:join(Listings, "*"))),
+ Listings = filename:join(PrivDir, listings),
+ lists:foreach(fun(F) -> ok = file:delete(F) end,
+ filelib:wildcard(filename:join(Listings, "*"))),
%% Test options that produce a listing file if 'binary' is not given.
- ?line do_listing(Simple, TargetDir, to_pp, ".P"),
- ?line do_listing(Simple, TargetDir, to_exp, ".E"),
- ?line do_listing(Simple, TargetDir, to_core0, ".core"),
- ?line ok = file:delete(filename:join(Listings, "simple.core")),
- ?line do_listing(Simple, TargetDir, to_core, ".core"),
- ?line do_listing(Simple, TargetDir, to_kernel, ".kernel"),
+ do_listing(Simple, TargetDir, to_pp, ".P"),
+ do_listing(Simple, TargetDir, to_exp, ".E"),
+ do_listing(Simple, TargetDir, to_core0, ".core"),
+ ok = file:delete(filename:join(Listings, File ++ ".core")),
+ do_listing(Simple, TargetDir, to_core, ".core"),
+ do_listing(Simple, TargetDir, to_kernel, ".kernel"),
%% Final clean up.
- ?line lists:foreach(fun(F) -> ok = file:delete(F) end,
- filelib:wildcard(filename:join(Listings, "*"))),
- ?line ok = file:del_dir(Listings),
- ?line test_server:timetrap_cancel(Dog),
- ok.
+ lists:foreach(fun(F) -> ok = file:delete(F) end,
+ filelib:wildcard(filename:join(Listings, "*"))),
+ ok = file:del_dir(Listings),
+
+ do_file_listings(DataDir,PrivDir,Files).
listings_big(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:minutes(10)),
diff --git a/lib/compiler/test/compile_SUITE_data/small.erl b/lib/compiler/test/compile_SUITE_data/small.erl
new file mode 100644
index 0000000000..37cd270e50
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/small.erl
@@ -0,0 +1,48 @@
+-module(small).
+
+-export([go/0,go/2]).
+
+
+-small_attribute({value,3}).
+
+go() -> go(3, 3.0).
+go(A,B) ->
+ V1 = A + B,
+ V2 = A * B,
+ V3 = V1 / V2,
+ V4 = V3 / 0.3,
+ V5 = V1 + V2 + V3 + V4,
+ try
+ R = call(<<"wazzup">>, A),
+ {A,B,V5,R,t(),recv()}
+ catch
+ C:E ->
+ {error, C, E}
+ end.
+
+-spec call(binary(), term()) -> binary().
+
+call(<<"wa", B/binary>>,V) when is_integer(V) -> B;
+call(B,_) -> B.
+
+t() ->
+ <<23:32, V:14, _:2, B/binary>> = id(<<"wazzup world">>),
+ {V,B}.
+
+recv() ->
+ F = fun() ->
+ receive
+ 1 -> ok;
+ 2 -> ok;
+ 3 -> ok;
+ a -> ok;
+ _ -> none
+ after 0 -> tmo
+ end
+ end,
+ tmo = F(),
+ ok.
+
+
+id(I) -> I.
+
diff --git a/lib/compiler/test/compile_SUITE_data/small_maps.erl b/lib/compiler/test/compile_SUITE_data/small_maps.erl
new file mode 100644
index 0000000000..a17a136a7d
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/small_maps.erl
@@ -0,0 +1,16 @@
+-module(small_maps).
+
+-export([go/0,go/1]).
+
+go() ->
+ go(1337).
+
+go(V0) ->
+ M0 = #{ a => 1, val => V0},
+ V1 = get_val(M0),
+ M1 = M0#{ val := [V0,V1] },
+ {some_val,[1337,{some_val,1337}]} = get_val(M1),
+ ok.
+
+get_val(#{ "wazzup" := _, val := V}) -> V;
+get_val(#{ val := V }) -> {some_val, V}.
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 1a521c3591..428ad65364 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -24,7 +24,7 @@
dehydrated_itracer/1,nested_tries/1,
seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
unsafe_case/1,nomatch_shadow/1,reversed_annos/1,
- map_core_test/1]).
+ map_core_test/1,eval_case/1,bad_boolean_guard/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -50,7 +50,7 @@ groups() ->
[{p,test_lib:parallel(),
[dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos,
- map_core_test
+ map_core_test,eval_case,bad_boolean_guard
]}].
@@ -76,6 +76,8 @@ end_per_group(_GroupName, Config) ->
?comp(nomatch_shadow).
?comp(reversed_annos).
?comp(map_core_test).
+?comp(eval_case).
+?comp(bad_boolean_guard).
try_it(Mod, Conf) ->
Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)),
diff --git a/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core b/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core
new file mode 100644
index 0000000000..318f8e3dc7
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core
@@ -0,0 +1,32 @@
+module 'bad_boolean_guard' ['bad_boolean_guard'/0,
+ 'module_info'/0,
+ 'module_info'/1]
+ attributes []
+'bad_boolean_guard'/0 =
+ fun () ->
+ apply 'f'/1
+ ('true')
+'f'/1 =
+ fun (_X_cor0) ->
+ case _X_cor0 of
+ <X>
+ when try
+ call 'erlang':'and'
+ ('bad', _X_cor0)
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false' ->
+ 'not_ok'
+ <_X_cor3> when 'true' ->
+ 'ok'
+ end
+'module_info'/0 =
+ fun () ->
+ call 'erlang':'get_module_info'
+ ('bad_boolean_guard')
+'module_info'/1 =
+ fun (_X_cor0) ->
+ call 'erlang':'get_module_info'
+ ('bad_boolean_guard', _X_cor0)
+end \ No newline at end of file
diff --git a/lib/compiler/test/core_SUITE_data/eval_case.core b/lib/compiler/test/core_SUITE_data/eval_case.core
new file mode 100644
index 0000000000..f2776e2b1f
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/eval_case.core
@@ -0,0 +1,34 @@
+module 'eval_case' ['eval_case'/0]
+ attributes []
+'eval_case'/0 =
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ case apply 'do_case'/0() of
+ <'ok'> when 'true' ->
+ 'ok'
+ ( <_cor0> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_cor0})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ ( primop 'match_fail'
+ ({'function_clause'})
+ -| [{'function_name',{'eval_case',0}}] )
+ -| ['compiler_generated'] )
+ end
+'do_case'/0 =
+ fun () ->
+ case let <_cor0> =
+ apply 'id'/1(42)
+ in let <_cor1> =
+ call 'erlang':'+'
+ (_cor0, 7)
+ in {'x',_cor1} of
+ <{'x',49}> when 'true' ->
+ 'ok'
+ end
+'id'/1 =
+ fun (_cor0) -> _cor0
+end
diff --git a/lib/compiler/test/core_SUITE_data/map_core_test.core b/lib/compiler/test/core_SUITE_data/map_core_test.core
index 7ece8a8bbd..2aa853d450 100644
--- a/lib/compiler/test/core_SUITE_data/map_core_test.core
+++ b/lib/compiler/test/core_SUITE_data/map_core_test.core
@@ -67,7 +67,7 @@ module 'map_core_test' ['map_core_test'/0,
(Val, V)
in let <_cor5> =
%% Line 21
- M~{~<1337,_cor4>,~<'val',_cor2>}~
+ ~{~<1337,_cor4>,~<'val',_cor2>|M}~
in %% Line 21
apply 'call'/2
(_cor5, Vs)
@@ -92,4 +92,4 @@ module 'map_core_test' ['map_core_test'/0,
fun (_cor0) ->
call 'erlang':'get_module_info'
('map_core_test', _cor0)
-end \ No newline at end of file
+end
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 69f61a046f..6a7036d728 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -22,7 +22,8 @@
init_per_group/2,end_per_group/2,
t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1,
eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1,
- unused_multiple_values_error/1,unused_multiple_values/1]).
+ unused_multiple_values_error/1,unused_multiple_values/1,
+ multiple_aliases/1,redundant_boolean_clauses/1,mixed_matching_clauses/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -38,7 +39,8 @@ groups() ->
[{p,test_lib:parallel(),
[t_element,setelement,t_length,append,t_apply,bifs,
eq,nested_call_in_case,guard_try_catch,coverage,
- unused_multiple_values_error,unused_multiple_values]}].
+ unused_multiple_values_error,unused_multiple_values,
+ multiple_aliases,redundant_boolean_clauses,mixed_matching_clauses]}].
init_per_suite(Config) ->
@@ -299,8 +301,6 @@ cover_is_safe_bool_expr(X) ->
bsm_an_inlined(<<_:8>>, _) -> ok;
bsm_an_inlined(_, _) -> error.
-id(I) -> I.
-
unused_multiple_values_error(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
Dir = filename:dirname(code:which(?MODULE)),
@@ -338,3 +338,50 @@ do_something(I) ->
put(unused_multiple_values,
[I|get(unused_multiple_values)]),
I.
+
+
+%% Make sure that multiple aliases does not cause
+%% the case expression to be evaluated twice.
+multiple_aliases(Config) when is_list(Config) ->
+ do_ma(fun() ->
+ X = Y = run_once(),
+ {X,Y}
+ end, {ok,ok}),
+ do_ma(fun() ->
+ case {true,run_once()} of
+ {true=A=B,ok=X=Y} ->
+ {A,B,X,Y}
+ end
+ end, {true,true,ok,ok}),
+ ok.
+
+do_ma(Fun, Expected) when is_function(Fun, 0) ->
+ Expected = Fun(),
+ ran_once = erase(run_once),
+ ok.
+
+run_once() ->
+ undefined = put(run_once, ran_once),
+ ok.
+
+
+redundant_boolean_clauses(Config) when is_list(Config) ->
+ X = id(0),
+ yes = case X == 0 of
+ false -> no;
+ false -> no;
+ true -> yes
+ end.
+
+mixed_matching_clauses(Config) when is_list(Config) ->
+ 0 = case #{} of
+ #{} -> 0;
+ a -> 1
+ end,
+ 0 = case <<>> of
+ <<>> -> 0;
+ a -> 1
+ end,
+ ok.
+
+id(I) -> I.
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index 859c4571ea..bd877bb528 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -23,7 +23,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1,
- transforms/1]).
+ transforms/1,forbidden_maps/1,bad_utf8/1]).
%% Used by transforms/1 test case.
-export([parse_transform/2]).
@@ -36,7 +36,8 @@ all() ->
groups() ->
[{p,test_lib:parallel(),
- [head_mismatch_line,warnings_as_errors,bif_clashes,transforms]}].
+ [head_mismatch_line,warnings_as_errors,bif_clashes,
+ transforms,forbidden_maps,bad_utf8]}].
init_per_suite(Config) ->
Config.
@@ -240,6 +241,38 @@ parse_transform(_, _) ->
error(too_bad).
+forbidden_maps(Config) when is_list(Config) ->
+ Ts1 = [{map_illegal_use_of_pattern,
+ <<"
+ -export([t/0]).
+ t() ->
+ V = 32,
+ #{<<\"hi\",V,\"all\">> := 1} = id(#{<<\"hi all\">> => 1}).
+ id(I) -> I.
+ ">>,
+ [return],
+ {error,[{5,erl_lint,{illegal_map_key_variable,'V'}}], []}}],
+ [] = run2(Config, Ts1),
+ ok.
+
+bad_utf8(Config) ->
+ Ts = [{bad_utf8,
+ %% If coding is specified explicitly as utf-8, there should be
+ %% a compilation error; we must not fallback to parsing the
+ %% file in latin-1 mode.
+ <<"%% coding: utf-8
+ %% Bj",246,"rn
+ t() -> \"",246,"\".
+ ">>,
+ [],
+ {error,[{2,epp,cannot_parse},
+ {2,file_io_server,invalid_unicode}],
+ []}
+ }],
+ [] = run2(Config, Ts),
+ ok.
+
+
run(Config, Tests) ->
?line File = test_filename(Config),
run(Tests, File, dont_write_beam).
@@ -303,6 +336,7 @@ run_test(Test0, File, Warnings, WriteBeam) ->
?line compile:file(File, [binary,report|Warnings]),
%% Test result of compilation.
+ io:format("~p\n", [Opts]),
?line Res = case compile:file(File, Opts) of
{ok,Mod,_,[{_File,Ws}]} ->
%io:format("compile:file(~s,~p) ->~n~p~n",
@@ -320,6 +354,11 @@ run_test(Test0, File, Warnings, WriteBeam) ->
%io:format("compile:file(~s,~p) ->~n~p~n",
% [File,Opts,_ZZ]),
{error,Es,Ws};
+ {error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ
+ when is_list(XFile) ->
+ %io:format("compile:file(~s,~p) ->~n~p~n",
+ % [File,Opts,_ZZ]),
+ {error,Es1++Es2,Ws};
{error,Es,[{_File,Ws}]} = _ZZ->
%io:format("compile:file(~s,~p) ->~n~p~n",
% [File,Opts,_ZZ]),
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index e35692efd1..25b7f677b5 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1,
- external/1,eep37/1]).
+ external/1,eep37/1,badarity/1]).
%% Internal export.
-export([call_me/1]).
@@ -32,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [test1,overwritten_fun,otp_7202,bif_fun,external,eep37].
+ [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,badarity].
groups() ->
[].
@@ -206,5 +206,9 @@ eep37(Config) when is_list(Config) ->
50 = UnusedName(8),
ok.
+badarity(Config) when is_list(Config) ->
+ {'EXIT',{{badarity,{_,[]}},_}} = (catch (fun badarity/1)()),
+ ok.
+
id(I) ->
I.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index a0a9bb7ddd..eb205d09a7 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -33,7 +33,7 @@
tricky/1,rel_ops/1,literal_type_tests/1,
basic_andalso_orelse/1,traverse_dcd/1,
check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1,
- bad_constants/1]).
+ bad_constants/1,bad_guards/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -50,7 +50,7 @@ groups() ->
t_is_boolean,is_function_2,tricky,rel_ops,
literal_type_tests,basic_andalso_orelse,traverse_dcd,
check_qlc_hrl,andalso_semi,t_tuple_size,binary_part,
- bad_constants]}].
+ bad_constants,bad_guards]}].
init_per_suite(Config) ->
Config.
@@ -1023,6 +1023,10 @@ is_function_2(Config) when is_list(Config) ->
true = is_function(id(fun() -> ok end), 0),
false = is_function(id(fun ?MODULE:all/1), 0),
false = is_function(id(fun() -> ok end), 1),
+ {'EXIT',{badarg,_}} =
+ (catch is_function(id(fun() -> ok end), -1) orelse error),
+ {'EXIT',{badarg,_}} =
+ (catch is_function(id(fun() -> ok end), '') orelse error),
F = fun(_) -> ok end,
if
@@ -1550,6 +1554,10 @@ bad_constants(Config) when is_list(Config) ->
?line ?FAILING(3.14),
ok.
+bad_guards(Config) when is_list(Config) ->
+ if erlang:float(self()); true -> ok end,
+ ok.
+
%% Call this function to turn off constant propagation.
id(I) -> I.
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index f5948504b3..398398a397 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
basic/1,deeply_nested/1,no_generator/1,
- empty_generator/1]).
+ empty_generator/1,no_export/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -31,7 +31,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [basic, deeply_nested, no_generator, empty_generator].
+ [basic, deeply_nested, no_generator, empty_generator, no_export].
groups() ->
[].
@@ -177,6 +177,10 @@ empty_generator(Config) when is_list(Config) ->
?line [] = [X || {X} <- [], (false or (X/0 > 3))],
ok.
+no_export(Config) when is_list(Config) ->
+ [] = [ _X = a || false ] ++ [ _X = a || false ],
+ ok.
+
id(I) -> I.
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok;
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
new file mode 100644
index 0000000000..cc018e4305
--- /dev/null
+++ b/lib/compiler/test/map_SUITE.erl
@@ -0,0 +1,600 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. 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(map_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2
+ ]).
+
+-export([
+ t_build_and_match_literals/1,
+ t_update_literals/1,t_match_and_update_literals/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1,t_update_exact/1,
+ t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
+ t_guard_receive/1, t_guard_fun/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ t_map_size/1,
+ t_build_and_match_aliasing/1,
+
+ %% warnings
+ t_warn_useless_build/1,
+ t_warn_pair_key_overloaded/1,
+
+ %% not covered in 17.0-rc1
+ t_build_and_match_over_alloc/1,
+ t_build_and_match_empty_val/1,
+ t_build_and_match_val/1,
+ t_build_and_match_nil/1,
+ t_build_and_match_structure/1,
+
+ %% errors in 17.0-rc1
+ t_update_values/1,
+ t_expand_map_update/1,
+ t_export/1
+ ]).
+
+suite() -> [].
+
+all() -> [
+ t_build_and_match_literals,
+ t_update_literals, t_match_and_update_literals,
+ t_update_map_expressions,
+ t_update_assoc,t_update_exact,
+ t_guard_bifs, t_guard_sequence, t_guard_update,
+ t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_sort_literals,
+ t_map_size,
+ t_build_and_match_aliasing,
+
+ %% warnings
+ t_warn_useless_build,
+ t_warn_pair_key_overloaded,
+
+ %% not covered in 17.0-rc1
+ t_build_and_match_over_alloc,
+ t_build_and_match_empty_val,
+ t_build_and_match_val,
+ t_build_and_match_nil,
+ t_build_and_match_structure,
+
+ %% errors in 17.0-rc1
+ t_update_values,
+ t_expand_map_update,
+ t_export
+ ].
+
+groups() -> [].
+
+init_per_suite(Config) -> Config.
+end_per_suite(_Config) -> ok.
+
+init_per_group(_GroupName, Config) -> Config.
+end_per_group(_GroupName, Config) -> Config.
+
+%% tests
+
+t_build_and_match_literals(Config) when is_list(Config) ->
+ #{} = id(#{}),
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% nil key
+ #{[]:=ok,1:=2} = id(#{[]=>ok,1=>2}),
+
+ %% error case
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ {'EXIT',{badarg,_}} = (catch id(#{<<0:258>> =>"three"})),
+ ok.
+
+t_build_and_match_aliasing(Config) when is_list(Config) ->
+ M1 = id(#{a=>1,b=>2,c=>3,d=>4}),
+ #{c:=C1=_=_=C2} = M1,
+ true = C1 =:= C2,
+ #{a:=A,a:=A,a:=A,b:=B,b:=B} = M1,
+ #{a:=A,a:=A,a:=A,b:=B,b:=B,b:=2} = M1,
+ #{a:=A=1,a:=A,a:=A,b:=B=2,b:=B,b:=2} = M1,
+ #{c:=C1, c:=_, c:=3, c:=_, c:=C2} = M1,
+ #{c:=C=_=3=_=C} = M1,
+
+ M2 = id(#{"a"=>1,"b"=>2,"c"=>3,"d"=>4}),
+ #{"a":=A2,"a":=A2,"a":=A2,"b":=B2,"b":=B2,"b":=2} = M2,
+ #{"a":=_,"a":=_,"a":=_,"b":=_,"b":=_,"b":=2} = M2,
+ ok.
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 1),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "c" => 2}, 2),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+% test map updates without matching
+t_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
+
+% test map updates with matching
+t_match_and_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+
+t_update_map_expressions(Config) when is_list(Config) ->
+ M = maps:new(),
+ X = id(fondue),
+ M1 = #{ a := 1 } = M#{a => 1},
+ #{ b := {X} } = M1#{ a := 1, b => {X} },
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Test need to be in a fun.
+ %% This tests that let expr optimisation in sys_core_fold
+ %% covers maps correctly.
+ F = fun() ->
+ M0 = id(#{ "a" => [1,2,3] }),
+ #{ "a" := _ } = M0,
+ M0#{ "a" := b }
+ end,
+
+ #{ "a" := b } = F(),
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+
+t_update_assoc(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+ {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting=>val}),
+ {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> => val}), %% limitation
+ ok.
+
+t_update_exact(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ true = M2 =/= M0#{3=>right,3.0:=new},
+ #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new},
+
+ M3 = id(#{ 1 => val}),
+ #{1 := update2,1.0 := new_val4} = M3#{
+ 1.0 => new_val1, 1 := update, 1=> update3,
+ 1 := update2, 1.0 := new_val2, 1.0 => new_val3,
+ 1.0 => new_val4 },
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })),
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+ {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation
+ ok.
+
+t_update_values(Config) when is_list(Config) ->
+ V0 = id(1337),
+ M0 = #{ a => 1, val => V0},
+ V1 = get_val(M0),
+ M1 = M0#{ val := [V0,V1], "wazzup" => 42 },
+ [1337, {some_val, 1337}] = get_val(M1),
+
+ N = 110,
+ List = [{[I,1,2,3,I],{1,2,3,"wat",I}}|| I <- lists:seq(1,N)],
+
+ {_,_,#{val2 := {1,2,3,"wat",N}, val1 := [N,1,2,3,N]}} = lists:foldl(fun
+ ({V2,V3},{Old2,Old3,Mi}) ->
+ ok = check_val(Mi,Old2,Old3),
+ #{ val1 := Old2, val2 := Old3 } = Mi,
+ {V2,V3, Mi#{ val1 := id(V2), val2 := V1, val2 => id(V3)}}
+ end, {none, none, #{val1=>none,val2=>none}},List),
+ ok.
+
+t_expand_map_update(Config) when is_list(Config) ->
+ M = #{<<"hello">> => <<"world">>}#{<<"hello">> := <<"les gens">>},
+ #{<<"hello">> := <<"les gens">>} = M,
+ ok.
+
+t_export(Config) when is_list(Config) ->
+ Raclette = id(#{}),
+ case brie of brie -> Fromage = Raclette end,
+ Raclette = Fromage#{},
+ ok.
+
+check_val(#{val1:=V1, val2:=V2},V1,V2) -> ok.
+
+get_val(#{ "wazzup" := _, val := V}) -> V;
+get_val(#{ val := V }) -> {some_val, V}.
+
+t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_empty(),
+ true = map_guard_empty_2(),
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ true = map_guard_tautology(),
+ true = map_guard_ill_map_size(),
+ ok.
+
+map_guard_empty() when is_map(#{}); false -> true.
+
+map_guard_empty_2() when true; #{} andalso false -> true.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+map_guard_tautology() when #{} =:= #{}; true -> true.
+
+map_guard_ill_map_size() when true; map_size(0) -> true.
+
+t_guard_sequence(Config) when is_list(Config) ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+
+t_guard_update(Config) when is_list(Config) ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third;
+map_guard_update(_, _) -> error.
+
+t_guard_receive(Config) when is_list(Config) ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <<B1/binary, B2/binary>>,
+
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
+
+
+t_list_comprehension(Config) when is_list(Config) ->
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
+
+t_guard_fun(Config) when is_list(Config) ->
+ F1 = fun
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ case (catch F1(#{s=>none,v=>none})) of
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} -> ok;
+ {'EXIT', {{case_clause,_},_}} -> {comment,inlined};
+ Other ->
+ test_server:fail({no_match, Other})
+ end.
+
+
+t_map_sort_literals(Config) when is_list(Config) ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = id(#{ a => 1 }) < id(#{ b => 1}),
+ false = id(#{ b => 1 }) < id(#{ a => 1}),
+ true = id(#{ a => 1, b => 1, c => 1 }) < id(#{ b => 1, c => 1, d => 1}),
+ true = id(#{ b => 1, c => 1, d => 1 }) > id(#{ a => 1, b => 1, c => 1}),
+ true = id(#{ c => 1, b => 1, a => 1 }) < id(#{ b => 1, c => 1, d => 1}),
+ true = id(#{ "a" => 1 }) < id(#{ <<"a">> => 1}),
+ false = id(#{ <<"a">> => 1 }) < id(#{ "a" => 1}),
+ false = id(#{ 1 => 1 }) < id(#{ 1.0 => 1}),
+ false = id(#{ 1.0 => 1 }) < id(#{ 1 => 1}),
+
+ %% value order
+ true = id(#{ a => 1 }) < id(#{ a => 2}),
+ false = id(#{ a => 2 }) < id(#{ a => 1}),
+ false = id(#{ a => 2, b => 1 }) < id(#{ a => 1, b => 3}),
+ true = id(#{ a => 1, b => 1 }) < id(#{ a => 1, b => 3}),
+
+ true = id(#{ "a" => "hi", b => 134 }) == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+t_warn_pair_key_overloaded(Config) when is_list(Config) ->
+ #{ "hi1" := 42 } = id(#{ "hi1" => 1, "hi1" => 42 }),
+
+ #{ "hi1" := 1337, "hi2" := [2], "hi3" := 3 } = id(#{
+ "hi1" => erlang:atom_to_binary(?MODULE,utf8),
+ "hi1" => erlang:binary_to_atom(<<"wazzup">>,utf8),
+ "hi1" => erlang:binary_to_float(<<"3.1416">>),
+ "hi1" => erlang:float_to_binary(3.1416),
+ "hi2" => erlang:pid_to_list(self()),
+ "hi3" => erlang:float_to_binary(3.1416),
+ "hi2" => lists:subtract([1,2],[1]),
+ "hi3" => +3,
+ "hi1" => erlang:min(1,2),
+ "hi1" => erlang:hash({1,2},35),
+ "hi1" => erlang:phash({1,2},33),
+ "hi1" => erlang:phash2({1,2},34),
+ "hi1" => erlang:integer_to_binary(1337),
+ "hi1" => erlang:binary_to_integer(<<"1337">>),
+ "hi4" => erlang:float_to_binary(3.1416)
+ }),
+ ok.
+
+t_warn_useless_build(Config) when is_list(Config) ->
+ [#{ a => id(I)} || I <- [1,2,3]],
+ ok.
+
+t_build_and_match_over_alloc(Config) when is_list(Config) ->
+ Ls = id([1,2,3]),
+ V0 = [a|Ls],
+ M0 = id(#{ "a" => V0 }),
+ #{ "a" := V1 } = M0,
+ V2 = id([c|Ls]),
+ M2 = id(#{ "a" => V2 }),
+ #{ "a" := V3 } = M2,
+ {[a,1,2,3],[c,1,2,3]} = id({V1,V3}),
+ ok.
+
+t_build_and_match_empty_val(Config) when is_list(Config) ->
+ F = fun(#{ "hi":=_,{1,2}:=_,1337:=_}) -> ok end,
+ ok = F(id(#{"hi"=>ok,{1,2}=>ok,1337=>ok})),
+
+ %% error case
+ case (catch (F(id(#{"hi"=>ok})))) of
+ {'EXIT',{function_clause,_}} -> ok;
+ {'EXIT', {{case_clause,_},_}} -> {comment,inlined};
+ Other ->
+ test_server:fail({no_match, Other})
+ end.
+
+t_build_and_match_val(Config) when is_list(Config) ->
+ F = fun
+ (#{ "hi" := first, v := V}) -> {1,V};
+ (#{ "hi" := second, v := V}) -> {2,V}
+ end,
+
+
+ {1,"hello"} = F(id(#{"hi"=>first,v=>"hello"})),
+ {2,"second"} = F(id(#{"hi"=>second,v=>"second"})),
+
+ %% error case
+ case (catch (F(id(#{"hi"=>ok})))) of
+ {'EXIT',{function_clause,_}} -> ok;
+ {'EXIT', {{case_clause,_},_}} -> {comment,inlined};
+ Other ->
+ test_server:fail({no_match, Other})
+ end.
+
+t_build_and_match_nil(Config) when is_list(Config) ->
+ %% literals removed the coverage
+ V1 = id(cookie),
+ V2 = id(cake),
+ V3 = id(crisps),
+
+ #{ [] := V1, "treat" := V2, {your,treat} := V3 } = id(#{
+ {your,treat} => V3,
+ "treat" => V2,
+ [] => V1 }),
+ #{ [] := V3, [] := V3 } = id(#{ [] => V1, [] => V3 }),
+ ok.
+
+t_build_and_match_structure(Config) when is_list(Config) ->
+ V2 = id("it"),
+ S = id([42,{"hi", "=)", #{ "a" => 42, any => any, val => "get_" ++ V2}}]),
+
+ %% match deep map values
+ V2 = case S of
+ [42,{"hi",_, #{ "a" := 42, val := "get_" ++ V1, any := _ }}] -> V1
+ end,
+ %% match deep map
+ ok = case S of
+ [42,{"hi",_, #{ }}] -> ok
+ end,
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index ec49267ded..00a6e900d4 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -257,6 +257,7 @@ wait(Config) when is_list(Config) ->
self() ! <<42>>,
<<42>> = wait_1(r, 1, 2),
{1,2,3} = wait_1(1, 2, 3),
+ {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
ok.
wait_1(r, _, _) ->
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index c9f5a2053e..f736e14bf6 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -369,6 +369,14 @@ record_test_3(Config) when is_list(Config) ->
?line false = is_record(id(#barf{}), id(barf), id(42)),
?line false = is_record(id(#barf{}), id(foo), id(6)),
+ Rec = id(#barf{}),
+ Good = id(barf),
+ Bad = id(foo),
+ Size = id(6),
+
+ true = is_record(Rec, Good, Size) orelse error,
+ error = is_record(Rec, Bad, Size) orelse error,
+
ok.
record_access_in_guards(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 7186956603..ad4ad91f74 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -37,7 +37,9 @@
-export([pattern/1,pattern2/1,pattern3/1,pattern4/1,
guard/1,bad_arith/1,bool_cases/1,bad_apply/1,
- files/1,effect/1,bin_opt_info/1,bin_construction/1, comprehensions/1]).
+ files/1,effect/1,bin_opt_info/1,bin_construction/1,
+ comprehensions/1,maps/1,redundant_boolean_clauses/1,
+ latin1_fallback/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(2)).
@@ -61,7 +63,8 @@ groups() ->
[{p,test_lib:parallel(),
[pattern,pattern2,pattern3,pattern4,guard,
bad_arith,bool_cases,bad_apply,files,effect,
- bin_opt_info,bin_construction,comprehensions]}].
+ bin_opt_info,bin_construction,comprehensions,maps,
+ redundant_boolean_clauses,latin1_fallback]}].
init_per_suite(Config) ->
Config.
@@ -200,6 +203,8 @@ pattern4(Config) when is_list(Config) ->
[nowarn_unused_vars],
{warnings,
[{9,sys_core_fold,no_clause_match},
+ {11,sys_core_fold,nomatch_shadow},
+ {15,sys_core_fold,nomatch_shadow},
{18,sys_core_fold,no_clause_match},
{23,sys_core_fold,no_clause_match},
{33,sys_core_fold,no_clause_match}
@@ -390,6 +395,10 @@ effect(Config) when is_list(Config) ->
<<X:8>>;
unused_fun ->
fun() -> {ok,X} end;
+ unused_named_fun ->
+ fun F(0) -> 1;
+ F(N) -> N*F(N-1)
+ end;
unused_atom ->
ignore; %no warning
unused_nil ->
@@ -484,8 +493,9 @@ effect(Config) when is_list(Config) ->
{22,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
{24,sys_core_fold,useless_building},
{26,sys_core_fold,useless_building},
- {32,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
- {34,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
+ {28,sys_core_fold,useless_building},
+ {36,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
+ {38,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
?line [] = run(Config, Ts),
ok.
@@ -547,6 +557,113 @@ comprehensions(Config) when is_list(Config) ->
run(Config, Ts),
ok.
+maps(Config) when is_list(Config) ->
+ Ts = [{bad_map,
+ <<"
+ t() ->
+ case maybe_map of
+ #{} -> ok;
+ not_map -> error
+ end.
+ x() ->
+ case true of
+ #{} -> error;
+ true -> ok
+ end.
+ ">>,
+ [],
+ {warnings,[{3,sys_core_fold,no_clause_match},
+ {9,sys_core_fold,nomatch_clause_type}]}},
+ {bad_map_src1,
+ <<"
+ t() ->
+ M = {a,[]},
+ {'EXIT',{badarg,_}} = (catch(M#{ a => 1})),
+ ok.
+ ">>,
+ [],
+ {warnings,[{4,v3_kernel,bad_map}]}},
+ {bad_map_src2,
+ <<"
+ t() ->
+ M = id({a,[]}),
+ {'EXIT',{badarg,_}} = (catch(M#{ a => 1})),
+ ok.
+ id(I) -> I.
+ ">>,
+ [inline],
+ {warnings,[{4,v3_kernel,bad_map}]}},
+ {bad_map_src3,
+ <<"
+ t() ->
+ {'EXIT',{badarg,_}} = (catch <<>>#{ a := 1}),
+ ok.
+ ">>,
+ [],
+ {warnings,[{3,v3_core,bad_map}]}},
+ {bad_map_literal_key,
+ <<"
+ t() ->
+ V = id(1),
+ M = id(#{ <<$h,$i>> => V }),
+ V = case M of
+ #{ <<0:257>> := Val } -> Val;
+ #{ <<$h,$i>> := Val } -> Val
+ end,
+ ok.
+ id(I) -> I.
+ ">>,
+ [],
+ {warnings,[{6,v3_core,nomatch}]}}],
+ run(Config, Ts),
+ ok.
+
+redundant_boolean_clauses(Config) when is_list(Config) ->
+ Ts = [{redundant_boolean_clauses,
+ <<"
+ t(X) ->
+ case X == 0 of
+ false -> no;
+ false -> no;
+ true -> yes
+ end.
+ ">>,
+ [],
+ {warnings,[{5,sys_core_fold,nomatch_shadow}]}}],
+ run(Config, Ts),
+ ok.
+
+latin1_fallback(Conf) when is_list(Conf) ->
+ DataDir = ?privdir,
+ IncFile = filename:join(DataDir, "include_me.hrl"),
+ file:write_file(IncFile, <<"%% ",246," in include file\n">>),
+ Ts1 = [{latin1_fallback1,
+ %% Test that the compiler fall backs to latin-1 with
+ %% a warning if a file has no encoding and does not
+ %% contain correct UTF-8 sequences.
+ <<"%% Bj",246,"rn
+ t(_) -> \"",246,"\";
+ t(x) -> ok.
+ ">>,
+ [],
+ {warnings,[{1,compile,reparsing_invalid_unicode},
+ {3,sys_core_fold,{nomatch_shadow,2}}]}}],
+ [] = run(Conf, Ts1),
+
+ Ts2 = [{latin1_fallback2,
+ %% Test that the compiler fall backs to latin-1 with
+ %% a warning if a file has no encoding and does not
+ %% contain correct UTF-8 sequences.
+ <<"
+
+ -include(\"include_me.hrl\").
+ ">>,
+ [],
+ {warnings,[{1,compile,reparsing_invalid_unicode}]}
+ }],
+ [] = run(Conf, Ts2),
+ ok.
+
%%%
%%% End of test cases.
%%%
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index cbdf57f177..c0c3d56472 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 4.9.4
+COMPILER_VSN = 5.0
diff --git a/lib/cosEvent/src/cosEvent.app.src b/lib/cosEvent/src/cosEvent.app.src
index c1cb9e0cc9..66b0d2e168 100644
--- a/lib/cosEvent/src/cosEvent.app.src
+++ b/lib/cosEvent/src/cosEvent.app.src
@@ -38,7 +38,8 @@
{registered, []},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosEventApp, []}}
+ {mod, {cosEventApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/cosEvent/vsn.mk b/lib/cosEvent/vsn.mk
index 6745bee079..40bf1ba49d 100644
--- a/lib/cosEvent/vsn.mk
+++ b/lib/cosEvent/vsn.mk
@@ -1,3 +1,3 @@
-COSEVENT_VSN = 2.1.14
+COSEVENT_VSN = 2.1.15
diff --git a/lib/cosEventDomain/src/cosEventDomain.app.src b/lib/cosEventDomain/src/cosEventDomain.app.src
index e4307e1f99..60114b6a91 100644
--- a/lib/cosEventDomain/src/cosEventDomain.app.src
+++ b/lib/cosEventDomain/src/cosEventDomain.app.src
@@ -27,5 +27,7 @@
{registered, []},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosEventDomainApp, []}}
+ {mod, {cosEventDomainApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0",
+ "cosNotification-1.1.21"]}
]}.
diff --git a/lib/cosEventDomain/vsn.mk b/lib/cosEventDomain/vsn.mk
index e9cf92395a..6317ed3c22 100644
--- a/lib/cosEventDomain/vsn.mk
+++ b/lib/cosEventDomain/vsn.mk
@@ -1,3 +1,3 @@
-COSEVENTDOMAIN_VSN = 1.1.13
+COSEVENTDOMAIN_VSN = 1.1.14
diff --git a/lib/cosFileTransfer/src/cosFileTransfer.app.src b/lib/cosFileTransfer/src/cosFileTransfer.app.src
index 31d94b6f0d..21226b0c6b 100644
--- a/lib/cosFileTransfer/src/cosFileTransfer.app.src
+++ b/lib/cosFileTransfer/src/cosFileTransfer.app.src
@@ -36,6 +36,8 @@
{registered, []},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosFileTransferApp, []}}
+ {mod, {cosFileTransferApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","orber-3.6.27","kernel-3.0",
+ "inets-5.10","erts-6.0","cosProperty-1.1.17"]}
]}.
diff --git a/lib/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk
index cf33926334..f52a1bd800 100644
--- a/lib/cosFileTransfer/vsn.mk
+++ b/lib/cosFileTransfer/vsn.mk
@@ -1 +1 @@
-COSFILETRANSFER_VSN = 1.1.15
+COSFILETRANSFER_VSN = 1.1.16
diff --git a/lib/cosNotification/src/cosNotification.app.src b/lib/cosNotification/src/cosNotification.app.src
index 04beac36e8..ad02eb4421 100644
--- a/lib/cosNotification/src/cosNotification.app.src
+++ b/lib/cosNotification/src/cosNotification.app.src
@@ -116,5 +116,7 @@
{registered, [cosNotificationSup, oe_cosNotificationFactory]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosNotificationApp, []}}
+ {mod, {cosNotificationApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0",
+ "cosTime-1.1.14","cosEvent-2.1.15"]}
]}.
diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk
index ea59800164..28d6ec71bf 100644
--- a/lib/cosNotification/vsn.mk
+++ b/lib/cosNotification/vsn.mk
@@ -1,2 +1,2 @@
-COSNOTIFICATION_VSN = 1.1.20
+COSNOTIFICATION_VSN = 1.1.21
diff --git a/lib/cosProperty/src/cosProperty.app.src b/lib/cosProperty/src/cosProperty.app.src
index 3099e904f7..b977bb5984 100644
--- a/lib/cosProperty/src/cosProperty.app.src
+++ b/lib/cosProperty/src/cosProperty.app.src
@@ -41,5 +41,7 @@
{registered, [oe_cosPropertySup]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosProperty, []}}
+ {mod, {cosProperty, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","mnesia-4.12",
+ "kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/cosProperty/vsn.mk b/lib/cosProperty/vsn.mk
index ac7820216e..0f546a2da8 100644
--- a/lib/cosProperty/vsn.mk
+++ b/lib/cosProperty/vsn.mk
@@ -1,2 +1,2 @@
-COSPROPERTY_VSN = 1.1.16
+COSPROPERTY_VSN = 1.1.17
diff --git a/lib/cosTime/src/cosTime.app.src b/lib/cosTime/src/cosTime.app.src
index 191ee5f3db..cd01de35cb 100644
--- a/lib/cosTime/src/cosTime.app.src
+++ b/lib/cosTime/src/cosTime.app.src
@@ -26,5 +26,7 @@
{registered, [oe_cosTimeSup, oe_cosTimerEventService]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosTime, []}}
+ {mod, {cosTime, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0",
+ "cosEvent-2.1.15"]}
]}.
diff --git a/lib/cosTime/vsn.mk b/lib/cosTime/vsn.mk
index 02cd669222..9e9e5c0250 100644
--- a/lib/cosTime/vsn.mk
+++ b/lib/cosTime/vsn.mk
@@ -1,2 +1,3 @@
-COSTIME_VSN = 1.1.13
+COSTIME_VSN = 1.1.14
+
diff --git a/lib/cosTransactions/src/cosTransactions.app.src b/lib/cosTransactions/src/cosTransactions.app.src
index 52769b1711..6b99915ad6 100644
--- a/lib/cosTransactions/src/cosTransactions.app.src
+++ b/lib/cosTransactions/src/cosTransactions.app.src
@@ -39,5 +39,6 @@
{registered, [cosTransactions_sup, oe_cosTransactionsFactory]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosTransactions, []}}
+ {mod, {cosTransactions, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/cosTransactions/vsn.mk b/lib/cosTransactions/vsn.mk
index 5414270a3d..7aed212523 100644
--- a/lib/cosTransactions/vsn.mk
+++ b/lib/cosTransactions/vsn.mk
@@ -1 +1 @@
-COSTRANSACTIONS_VSN = 1.2.13
+COSTRANSACTIONS_VSN = 1.2.14
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index 2adcfd7f31..e893c914e6 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -23,7 +23,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Macros
#
+ifneq ($(findstring ose,$(TARGET)),ose)
SUB_DIRECTORIES = src c_src doc/src
+else
+SUB_DIRECTORIES = src doc/src
+endif
static_lib: SUB_DIRECTORIES = c_src
include vsn.mk
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index 124d088056..8c92b5ba1b 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -126,7 +126,12 @@ ALL_STATIC_CFLAGS = $(DED_STATIC_CFLAGS) $(INCLUDES)
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
+ifneq ($(findstring ose,$(TARGET)),ose)
debug opt valgrind: $(NIF_LIB) $(CALLBACK_LIB)
+else
+# Do not build dynamic files on OSE
+debug opt valgrind:
+endif
static_lib: $(NIF_ARCHIVE)
@@ -197,12 +202,14 @@ release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/obj"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/lib"
$(INSTALL_DATA) $(NIF_MAKEFILE) "$(RELSYSDIR)/priv/obj"
+ifneq ($(findstring ose,$(TARGET)),ose)
$(INSTALL_PROGRAM) $(CRYPTO_OBJS) "$(RELSYSDIR)/priv/obj"
$(INSTALL_PROGRAM) $(NIF_LIB) "$(RELSYSDIR)/priv/lib"
ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
$(INSTALL_PROGRAM) $(CALLBACK_OBJS) "$(RELSYSDIR)/priv/obj"
$(INSTALL_PROGRAM) $(CALLBACK_LIB) "$(RELSYSDIR)/priv/lib"
endif
+endif
release_docs_spec:
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 925ad0c091..fca08c4eed 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2014. 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
@@ -254,6 +254,8 @@ static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
/* helpers */
static void init_algorithms_types(ErlNifEnv*);
@@ -381,7 +383,9 @@ static ErlNifFunc nif_funcs[] = {
{"ec_key_generate", 1, ec_key_generate},
{"ecdsa_sign_nif", 4, ecdsa_sign_nif},
{"ecdsa_verify_nif", 5, ecdsa_verify_nif},
- {"ecdh_compute_key_nif", 3, ecdh_compute_key_nif}
+ {"ecdh_compute_key_nif", 3, ecdh_compute_key_nif},
+
+ {"rand_seed_nif", 1, rand_seed_nif}
};
ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
@@ -444,6 +448,15 @@ static ERL_NIF_TERM atom_ppbasis;
static ERL_NIF_TERM atom_onbasis;
#endif
+static ErlNifResourceType* hmac_context_rtype;
+struct hmac_context
+{
+ ErlNifMutex* mtx;
+ int alive;
+ HMAC_CTX ctx;
+};
+static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*);
+
/*
#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
@@ -451,6 +464,46 @@ static ERL_NIF_TERM atom_onbasis;
#define PRINTF_ERR0(FMT)
#define PRINTF_ERR1(FMT,A1)
+#ifdef __OSE__
+
+/* For crypto on OSE we have to initialize the crypto library on each
+ process that uses it. So since we do not know which scheduler is going
+ to execute the nif we have to check before each nif call that we have
+ initialized crypto in that process. */
+
+#include "ose.h"
+#include "openssl/osessl.h"
+
+static ErlNifTSDKey crypto_init_key;
+static int check_ose_crypto(void);
+static int init_ose_crypto(void);
+
+static int check_ose_crypto() {
+ int key = (int)enif_tsd_get(crypto_init_key);
+ if (!key) {
+ if (!CRYPTO_OSE5_init()) {
+ PRINTF_ERR0("CRYPTO: Call to CRYPTO_OSE5_init failed");
+ return 0;
+ }
+ enif_tsd_set(crypto_init_key,1);
+ }
+ return 1;
+}
+
+static int init_ose_crypto() {
+ /* Crypto nif upgrade does not work on OSE so no need to
+ destroy this key */
+ enif_tsd_key_create("crypto_init_key", &crypto_init_key);
+ return check_ose_crypto();
+}
+
+#define INIT_OSE_CRYPTO() init_ose_crypto()
+#define CHECK_OSE_CRYPTO() check_ose_crypto()
+#else
+#define INIT_OSE_CRYPTO() 1
+#define CHECK_OSE_CRYPTO()
+#endif
+
#ifdef HAVE_DYNAMIC_CRYPTO_LIB
static int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile)
{
@@ -487,6 +540,9 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
ErlNifBinary lib_bin;
char lib_buf[1000];
+ if (!INIT_OSE_CRYPTO())
+ return 0;
+
/* load_info: {301, <<"/full/path/of/this/library">>} */
if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)
|| tpl_arity != 2
@@ -498,6 +554,15 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
return 0;
}
+ hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context",
+ (ErlNifResourceDtor*) hmac_context_dtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
+ if (!hmac_context_rtype) {
+ PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'");
+ return 0;
+ }
+
if (library_refc > 0) {
/* Repeated loading of this library (module upgrade).
* Atoms and callbacks are already set, we are done.
@@ -710,7 +775,7 @@ static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -722,6 +787,7 @@ static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* () */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
MD5_Init((MD5_CTX *) enif_make_new_binary(env, MD5_CTX_LEN, &ret));
return ret;
}
@@ -730,6 +796,7 @@ static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
MD5_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin)
|| ctx_bin.size != MD5_CTX_LEN
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
@@ -746,6 +813,7 @@ static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifBinary ctx_bin;
MD5_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != MD5_CTX_LEN) {
return enif_make_badarg(env);
}
@@ -758,7 +826,7 @@ static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
{/* (Data) */
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -770,6 +838,7 @@ static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
static ERL_NIF_TERM ripemd160_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* () */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
RIPEMD160_Init((RIPEMD160_CTX *) enif_make_new_binary(env, RIPEMD160_CTX_LEN, &ret));
return ret;
}
@@ -778,6 +847,7 @@ static ERL_NIF_TERM ripemd160_update(ErlNifEnv* env, int argc, const ERL_NIF_TER
RIPEMD160_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin)
|| ctx_bin.size != RIPEMD160_CTX_LEN
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
@@ -794,6 +864,7 @@ static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ErlNifBinary ctx_bin;
RIPEMD160_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != RIPEMD160_CTX_LEN) {
return enif_make_badarg(env);
}
@@ -807,7 +878,7 @@ static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -819,6 +890,7 @@ static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* () */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
SHA1_Init((SHA_CTX *) enif_make_new_binary(env, SHA_CTX_LEN, &ret));
return ret;
}
@@ -827,6 +899,7 @@ static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
SHA_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != SHA_CTX_LEN
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
return enif_make_badarg(env);
@@ -842,6 +915,7 @@ static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifBinary ctx_bin;
SHA_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != SHA_CTX_LEN) {
return enif_make_badarg(env);
}
@@ -855,7 +929,7 @@ static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
#ifdef HAVE_SHA224
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -871,6 +945,7 @@ static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
{/* () */
#ifdef HAVE_SHA224
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
SHA224_Init((SHA256_CTX *) enif_make_new_binary(env, sizeof(SHA256_CTX), &ret));
return ret;
#else
@@ -883,6 +958,7 @@ static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
SHA256_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
return enif_make_badarg(env);
@@ -902,6 +978,7 @@ static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
ErlNifBinary ctx_bin;
SHA256_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)) {
return enif_make_badarg(env);
}
@@ -918,7 +995,7 @@ static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
#ifdef HAVE_SHA256
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -934,6 +1011,7 @@ static ERL_NIF_TERM sha256_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
{/* () */
#ifdef HAVE_SHA256
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
SHA256_Init((SHA256_CTX *) enif_make_new_binary(env, sizeof(SHA256_CTX), &ret));
return ret;
#else
@@ -946,6 +1024,7 @@ static ERL_NIF_TERM sha256_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
SHA256_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
return enif_make_badarg(env);
@@ -965,6 +1044,7 @@ static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
ErlNifBinary ctx_bin;
SHA256_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)) {
return enif_make_badarg(env);
}
@@ -981,7 +1061,7 @@ static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
#ifdef HAVE_SHA384
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -997,6 +1077,7 @@ static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
{/* () */
#ifdef HAVE_SHA384
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
SHA384_Init((SHA512_CTX *) enif_make_new_binary(env, sizeof(SHA512_CTX), &ret));
return ret;
#else
@@ -1009,6 +1090,7 @@ static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
SHA512_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
return enif_make_badarg(env);
@@ -1028,6 +1110,7 @@ static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
ErlNifBinary ctx_bin;
SHA512_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)) {
return enif_make_badarg(env);
}
@@ -1044,7 +1127,7 @@ static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
#ifdef HAVE_SHA512
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -1072,6 +1155,7 @@ static ERL_NIF_TERM sha512_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
SHA512_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
return enif_make_badarg(env);
@@ -1091,6 +1175,7 @@ static ERL_NIF_TERM sha512_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
ErlNifBinary ctx_bin;
SHA512_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)) {
return enif_make_badarg(env);
}
@@ -1107,7 +1192,7 @@ static ERL_NIF_TERM md4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
ErlNifBinary ibin;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
return enif_make_badarg(env);
}
@@ -1119,6 +1204,7 @@ static ERL_NIF_TERM md4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM md4_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* () */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
MD4_Init((MD4_CTX *) enif_make_new_binary(env, MD4_CTX_LEN, &ret));
return ret;
}
@@ -1127,6 +1213,7 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
MD4_CTX* new_ctx;
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != MD4_CTX_LEN
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
return enif_make_badarg(env);
@@ -1142,6 +1229,7 @@ static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifBinary ctx_bin;
MD4_CTX ctx_clone;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != MD4_CTX_LEN) {
return enif_make_badarg(env);
}
@@ -1156,7 +1244,7 @@ static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifBinary key, data;
unsigned mac_sz;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)
|| !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > MD5_LEN) {
@@ -1174,7 +1262,7 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifBinary key, data;
unsigned mac_sz;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)
|| !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA_LEN) {
@@ -1194,7 +1282,7 @@ static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ErlNifBinary key, data;
unsigned mac_sz;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)
|| !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA224_DIGEST_LENGTH) {
@@ -1217,7 +1305,7 @@ static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ErlNifBinary key, data;
unsigned mac_sz;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)
|| !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA256_DIGEST_LENGTH) {
@@ -1240,7 +1328,7 @@ static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ErlNifBinary key, data;
unsigned mac_sz;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)
|| !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA384_DIGEST_LENGTH) {
@@ -1264,7 +1352,7 @@ static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ErlNifBinary key, data;
unsigned mac_sz;
ERL_NIF_TERM ret;
-
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)
|| !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA512_DIGEST_LENGTH) {
@@ -1280,13 +1368,23 @@ static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
#endif
}
+static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj)
+{
+ if (obj->alive) {
+ HMAC_CTX_cleanup(&obj->ctx);
+ obj->alive = 0;
+ }
+ enif_mutex_destroy(obj->mtx);
+}
+
static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Key) */
ErlNifBinary key;
- ERL_NIF_TERM ret;
- unsigned char * ctx_buf;
+ struct hmac_context* obj;
const EVP_MD *md;
+ CHECK_OSE_CRYPTO();
+
if (argv[0] == atom_sha) md = EVP_sha1();
#ifdef HAVE_SHA224
else if (argv[0] == atom_sha224) md = EVP_sha224();
@@ -1309,57 +1407,64 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return enif_make_badarg(env);
}
- ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret);
- HMAC_CTX_init((HMAC_CTX *) ctx_buf);
- HMAC_Init((HMAC_CTX *) ctx_buf, key.data, key.size, md);
+ obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context));
+ obj->mtx = enif_mutex_create("crypto.hmac");
+ obj->alive = 1;
+ HMAC_CTX_init(&obj->ctx);
+ HMAC_Init(&obj->ctx, key.data, key.size, md);
- return ret;
+ return enif_make_resource(env, obj);
}
static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context, Data) */
- ErlNifBinary context, data;
- ERL_NIF_TERM ret;
- unsigned char * ctx_buf;
+ ErlNifBinary data;
+ struct hmac_context* obj;
- if (!enif_inspect_binary(env, argv[0], &context)
- || !enif_inspect_iolist_as_binary(env, argv[1], &data)
- || context.size != sizeof(HMAC_CTX)) {
+ CHECK_OSE_CRYPTO();
+
+ if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)) {
+ return enif_make_badarg(env);
+ }
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ enif_mutex_unlock(obj->mtx);
return enif_make_badarg(env);
}
+ HMAC_Update(&obj->ctx, data.data, data.size);
+ enif_mutex_unlock(obj->mtx);
- ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret);
- memcpy(ctx_buf, context.data, context.size);
- HMAC_Update((HMAC_CTX *)ctx_buf, data.data, data.size);
CONSUME_REDS(env,data);
-
- return ret;
+ return argv[0];
}
static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context) or (Context, HashLen) */
- ErlNifBinary context;
ERL_NIF_TERM ret;
- HMAC_CTX ctx;
+ struct hmac_context* obj;
unsigned char mac_buf[EVP_MAX_MD_SIZE];
unsigned char * mac_bin;
unsigned int req_len = 0;
unsigned int mac_len;
+
+ CHECK_OSE_CRYPTO();
- if (!enif_inspect_binary(env, argv[0], &context)) {
- return enif_make_badarg(env);
- }
- if (argc == 2 && !enif_get_uint(env, argv[1], &req_len)) {
+ if (!enif_get_resource(env,argv[0],hmac_context_rtype, (void**)&obj)
+ || (argc == 2 && !enif_get_uint(env, argv[1], &req_len))) {
return enif_make_badarg(env);
}
- if (context.size != sizeof(ctx)) {
- return enif_make_badarg(env);
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ enif_mutex_unlock(obj->mtx);
+ return enif_make_badarg(env);
}
- memcpy(&ctx, context.data, context.size);
- HMAC_Final(&ctx, mac_buf, &mac_len);
- HMAC_CTX_cleanup(&ctx);
+ HMAC_Final(&obj->ctx, mac_buf, &mac_len);
+ HMAC_CTX_cleanup(&obj->ctx);
+ obj->alive = 0;
+ enif_mutex_unlock(obj->mtx);
if (argc == 2 && req_len < mac_len) {
/* Only truncate to req_len bytes if asked. */
@@ -1378,6 +1483,8 @@ static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
DES_cblock ivec_clone; /* writable copy */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 8
|| !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 8
|| !enif_inspect_iolist_as_binary(env, argv[2], &text)
@@ -1399,6 +1506,8 @@ static ERL_NIF_TERM des_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
DES_cblock ivec_clone; /* writable copy */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 8
|| !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 8
|| !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
@@ -1417,6 +1526,7 @@ static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
ErlNifBinary key, text;
DES_key_schedule schedule;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 8 ||
!enif_inspect_iolist_as_binary(env, argv[1], &text) || text.size != 8) {
return enif_make_badarg(env);
@@ -1436,6 +1546,8 @@ static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T
DES_cblock ivec_clone; /* writable copy */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key1) || key1.size != 8
|| !enif_inspect_iolist_as_binary(env, argv[1], &key2) || key2.size != 8
|| !enif_inspect_iolist_as_binary(env, argv[2], &key3) || key3.size != 8
@@ -1464,6 +1576,8 @@ static ERL_NIF_TERM des_ede3_cfb_crypt_nif(ErlNifEnv* env, int argc, const ERL_N
DES_cblock ivec_clone; /* writable copy */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key1) || key1.size != 8
|| !enif_inspect_iolist_as_binary(env, argv[1], &key2) || key2.size != 8
|| !enif_inspect_iolist_as_binary(env, argv[2], &key3) || key3.size != 8
@@ -1494,6 +1608,8 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
int new_ivlen = 0;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 16
|| !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
|| !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
@@ -1521,6 +1637,8 @@ static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM
unsigned int num = 0;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
|| AES_set_encrypt_key(key.data, key.size*8, &aes_key) != 0
|| !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
@@ -1552,6 +1670,8 @@ static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_N
unsigned char * ivec2_buf;
unsigned char * ecount2_buf;
+ CHECK_OSE_CRYPTO();
+
if (!enif_get_tuple(env, argv[0], &state_arity, &state_term)
|| state_arity != 4
|| !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin)
@@ -1585,6 +1705,7 @@ static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
unsigned bytes;
unsigned char* data;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_get_uint(env, argv[0], &bytes)) {
return enif_make_badarg(env);
}
@@ -1598,6 +1719,7 @@ static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NI
unsigned bytes;
unsigned char* data;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_get_uint(env, argv[0], &bytes)) {
return enif_make_badarg(env);
}
@@ -1615,6 +1737,7 @@ static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
unsigned char* data;
unsigned top_mask, bot_mask;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
if (!enif_get_uint(env, argv[0], &bytes)
|| !enif_get_uint(env, argv[1], &top_mask)
|| !enif_get_uint(env, argv[2], &bot_mask)) {
@@ -1637,6 +1760,9 @@ static ERL_NIF_TERM strong_rand_mpint_nif(ErlNifEnv* env, int argc, const ERL_NI
unsigned char* data;
unsigned dlen;
ERL_NIF_TERM ret;
+
+ CHECK_OSE_CRYPTO();
+
if (!enif_get_uint(env, argv[0], &bits)
|| !enif_get_int(env, argv[1], &top)
|| !enif_get_int(env, argv[2], &bottom)) {
@@ -1704,6 +1830,9 @@ static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
unsigned char* data;
unsigned dlen;
ERL_NIF_TERM ret;
+
+ CHECK_OSE_CRYPTO();
+
if (!get_bn_from_mpint(env, argv[0], &bn_from)
|| !get_bn_from_mpint(env, argv[1], &bn_rand)) {
if (bn_from) BN_free(bn_from);
@@ -1735,6 +1864,8 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
unsigned extra_byte;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!get_bn_from_bin(env, argv[0], &bn_base)
|| !get_bn_from_bin(env, argv[1], &bn_exponent)
|| !get_bn_from_bin(env, argv[2], &bn_modulo)
@@ -1777,6 +1908,8 @@ static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
DSA *dsa;
int i;
+ CHECK_OSE_CRYPTO();
+
if (argv[0] == atom_sha) {
if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
if (tpl_arity != 2 || tpl_terms[0] != atom_digest
@@ -1944,6 +2077,8 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
struct digest_type_t* digp = NULL;
unsigned char* digest = NULL;
+ CHECK_OSE_CRYPTO();
+
digp = get_digest_type(type);
if (!digp) {
return enif_make_badarg(env);
@@ -2003,6 +2138,8 @@ static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
unsigned char* ret_ptr;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| (key_bin.size != 16 && key_bin.size != 32)
|| !enif_inspect_binary(env, argv[1], &ivec_bin)
@@ -2039,6 +2176,8 @@ static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
unsigned char* ret_ptr;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| (key_bin.size != 16 && key_bin.size != 32)
|| !enif_inspect_binary(env, argv[1], &ivec_bin)
@@ -2075,6 +2214,8 @@ static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
int i;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env,argv[0], &d1)
|| !enif_inspect_iolist_as_binary(env,argv[1], &d2)
|| d1.size != d2.size) {
@@ -2095,6 +2236,8 @@ static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
RC4_KEY rc4_key;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env,argv[0], &key)
|| !enif_inspect_iolist_as_binary(env,argv[1], &data)) {
return enif_make_badarg(env);
@@ -2111,6 +2254,8 @@ static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
ErlNifBinary key;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env,argv[0], &key)) {
return enif_make_badarg(env);
}
@@ -2126,6 +2271,8 @@ static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_N
RC4_KEY* rc4_key;
ERL_NIF_TERM new_state, new_data;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env,argv[0], &state)
|| state.size != sizeof(RC4_KEY)
|| !enif_inspect_iolist_as_binary(env,argv[1], &data)) {
@@ -2145,6 +2292,8 @@ static ERL_NIF_TERM rc2_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
RC2_KEY rc2_key;
ERL_NIF_TERM ret;
unsigned char iv_copy[8];
+
+ CHECK_OSE_CRYPTO();
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| (key_bin.size != 5 && key_bin.size != 8 && key_bin.size != 16)
@@ -2206,6 +2355,8 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
struct digest_type_t *digp;
unsigned char* digest;
+ CHECK_OSE_CRYPTO();
+
digp = get_digest_type(argv[0]);
if (!digp) {
return enif_make_badarg(env);
@@ -2272,6 +2423,8 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
DSA* dsa;
int i;
+ CHECK_OSE_CRYPTO();
+
if (argv[0] == atom_sha) {
if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
if (tpl_arity != 2 || tpl_terms[0] != atom_digest
@@ -2354,7 +2507,11 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER
ErlNifBinary data_bin, ret_bin;
ERL_NIF_TERM head, tail;
int padding, i;
- RSA* rsa = RSA_new();
+ RSA* rsa;
+
+ CHECK_OSE_CRYPTO();
+
+ rsa = RSA_new();
if (!enif_inspect_binary(env, argv[0], &data_bin)
|| !enif_get_list_cell(env, argv[1], &head, &tail)
@@ -2400,7 +2557,11 @@ static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
{/* (Data, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Padding, IsEncrypt) */
ErlNifBinary data_bin, ret_bin;
int padding, i;
- RSA* rsa = RSA_new();
+ RSA* rsa;
+
+ CHECK_OSE_CRYPTO();
+
+ rsa = RSA_new();
if (!enif_inspect_binary(env, argv[0], &data_bin)
|| !get_rsa_private_key(env, argv[1], rsa)
@@ -2446,6 +2607,8 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
unsigned char *p_ptr, *g_ptr;
ERL_NIF_TERM ret_p, ret_g;
+ CHECK_OSE_CRYPTO();
+
if (!enif_get_int(env, argv[0], &prime_len)
|| !enif_get_int(env, argv[1], &generator)) {
@@ -2469,10 +2632,14 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* ([PrimeLen, Generator]) */
- DH* dh_params = DH_new();
+ DH* dh_params;
int i;
ERL_NIF_TERM ret, head, tail;
+ CHECK_OSE_CRYPTO();
+
+ dh_params = DH_new();
+
if (!enif_get_list_cell(env, argv[0], &head, &tail)
|| !get_bn_from_bin(env, head, &dh_params->p)
|| !enif_get_list_cell(env, tail, &head, &tail)
@@ -2499,12 +2666,16 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (PrivKey, DHParams=[P,G], Mpint) */
- DH* dh_params = DH_new();
+ DH* dh_params;
int pub_len, prv_len;
unsigned char *pub_ptr, *prv_ptr;
ERL_NIF_TERM ret, ret_pub, ret_prv, head, tail;
int mpint; /* 0 or 4 */
+ CHECK_OSE_CRYPTO();
+
+ dh_params = DH_new();
+
if (!(get_bn_from_bin(env, argv[0], &dh_params->priv_key)
|| argv[0] == atom_undefined)
|| !enif_get_list_cell(env, argv[1], &head, &tail)
@@ -2541,12 +2712,16 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_
static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */
- DH* dh_params = DH_new();
+ DH* dh_params;
BIGNUM* pubkey = NULL;
int i;
ErlNifBinary ret_bin;
ERL_NIF_TERM ret, head, tail;
+ CHECK_OSE_CRYPTO();
+
+ dh_params = DH_new();
+
if (!get_bn_from_bin(env, argv[0], &pubkey)
|| !get_bn_from_bin(env, argv[1], &dh_params->priv_key)
|| !enif_get_list_cell(env, argv[2], &head, &tail)
@@ -2584,6 +2759,8 @@ static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
unsigned dlen;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!get_bn_from_bin(env, argv[0], &bn_multiplier)
|| !get_bn_from_bin(env, argv[1], &bn_verifier)
|| !get_bn_from_bin(env, argv[2], &bn_generator)
@@ -2644,6 +2821,8 @@ static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_
unsigned dlen;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!get_bn_from_bin(env, argv[0], &bn_a)
|| !get_bn_from_bin(env, argv[1], &bn_u)
|| !get_bn_from_bin(env, argv[2], &bn_B)
@@ -2723,6 +2902,8 @@ static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_
unsigned dlen;
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!get_bn_from_bin(env, argv[0], &bn_verifier)
|| !get_bn_from_bin(env, argv[1], &bn_b)
|| !get_bn_from_bin(env, argv[2], &bn_u)
@@ -2783,6 +2964,8 @@ static ERL_NIF_TERM bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM
int bf_n = 0; /* blowfish ivec pos */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| !enif_inspect_binary(env, argv[1], &ivec_bin)
|| ivec_bin.size != 8
@@ -2806,6 +2989,8 @@ static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
unsigned char bf_tkey[8]; /* blowfish ivec */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| !enif_inspect_binary(env, argv[1], &ivec_bin)
|| ivec_bin.size != 8
@@ -2829,6 +3014,8 @@ static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
BF_KEY bf_key; /* blowfish key 8 */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)
|| data_bin.size < 8) {
@@ -2849,6 +3036,8 @@ static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_N
int bf_n = 0; /* blowfish ivec pos */
ERL_NIF_TERM ret;
+ CHECK_OSE_CRYPTO();
+
if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)
|| !enif_inspect_binary(env, argv[1], &ivec_bin)
|| ivec_bin.size != 8
@@ -3167,6 +3356,8 @@ static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM
#if defined(HAVE_EC)
EC_KEY *key = ec_key_new(env, argv[0]);
+ CHECK_OSE_CRYPTO();
+
if (key && EC_KEY_generate_key(key)) {
const EC_GROUP *group;
const EC_POINT *public_key;
@@ -3204,6 +3395,8 @@ static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
struct digest_type_t *digp;
unsigned char* digest;
+ CHECK_OSE_CRYPTO();
+
digp = get_digest_type(argv[0]);
if (!digp) {
return enif_make_badarg(env);
@@ -3271,6 +3464,8 @@ static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
struct digest_type_t* digp = NULL;
unsigned char* digest = NULL;
+ CHECK_OSE_CRYPTO();
+
digp = get_digest_type(type);
if (!digp) {
return enif_make_badarg(env);
@@ -3334,6 +3529,8 @@ static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF
EC_POINT *my_ecpoint;
EC_KEY *other_ecdh = NULL;
+ CHECK_OSE_CRYPTO();
+
if (!get_ec_key(env, argv[1], argv[2], atom_undefined, &key))
return enif_make_badarg(env);
@@ -3374,6 +3571,16 @@ out_err:
#endif
}
+static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary seed_bin;
+ CHECK_OSE_CRYPTO();
+ if (!enif_inspect_binary(env, argv[0], &seed_bin))
+ return enif_make_badarg(env);
+ RAND_seed(seed_bin.data,seed_bin.size);
+ return atom_ok;
+}
+
/* HMAC */
diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c
index 81106b4cc2..a08dcec463 100644
--- a/lib/crypto/c_src/crypto_callback.c
+++ b/lib/crypto/c_src/crypto_callback.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012. All Rights Reserved.
+ * Copyright Ericsson AB 2014. 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
@@ -17,6 +17,7 @@
* %CopyrightEnd%
*/
+#include <stdio.h>
#include <string.h>
#include <openssl/opensslconf.h>
@@ -51,13 +52,28 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks);
static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */
+static void nomem(size_t size, const char* op)
+{
+ fprintf(stderr, "Out of memory abort. Crypto failed to %s %zu bytes.\r\n",
+ op, size);
+ abort();
+}
+
static void* crypto_alloc(size_t size)
{
- return enif_alloc(size);
+ void *ret = enif_alloc(size);
+
+ if (!ret && size)
+ nomem(size, "allocate");
+ return ret;
}
static void* crypto_realloc(void* ptr, size_t size)
{
- return enif_realloc(ptr, size);
+ void* ret = enif_realloc(ptr, size);
+
+ if (!ret && size)
+ nomem(size, "reallocate");
+ return ret;
}
static void crypto_free(void* ptr)
{
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 40f829e704..e88bf01491 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -366,7 +366,11 @@
or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and
<seealso marker="#hmac_final_n-2">hmac_final_n</seealso>
</p>
-
+ <warning><p>Do not use a <c>Context</c> as argument in more than one
+ call to hmac_update or hmac_final. The semantics of reusing old contexts
+ in any way is undefined and could even crash the VM in earlier releases.
+ The reason for this limitation is a lack of support in the underlying
+ OpenSSL API.</p></warning>
</desc>
</func>
@@ -552,6 +556,21 @@
</desc>
</func>
+ <func>
+ <name>rand_seed(Seed) -> ok</name>
+ <fsummary>Set the seed for random bytes generation</fsummary>
+ <type>
+ <v>Seed = binary()</v>
+ </type>
+ <desc>
+ <p>Set the seed for PRNG to the given binary. This calls the
+ RAND_seed function from openssl. Only use this if the system
+ you are running on does not have enough "randomness" built in.
+ Normally this is when <seealso marker="#strong_rand_bytes/1">
+ stong_rand_bytes/1</seealso> returns <c>low_entropy</c></p>
+ </desc>
+ </func>
+
<func>
<name>rand_uniform(Lo, Hi) -> N</name>
<fsummary>Generate a random number</fsummary>
diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml
index 6d26076c04..1d10773401 100644
--- a/lib/crypto/doc/src/crypto_app.xml
+++ b/lib/crypto/doc/src/crypto_app.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE appref SYSTEM "appref.dtd">
<appref>
<header>
<copyright>
<year>1999</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/crypto/src/crypto.app.src b/lib/crypto/src/crypto.app.src
index d3084ff336..823a27ee39 100644
--- a/lib/crypto/src/crypto.app.src
+++ b/lib/crypto/src/crypto.app.src
@@ -23,6 +23,7 @@
crypto_ec_curves]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["erts-6.0","stdlib-2.0","kernel-3.0"]}]}.
diff --git a/lib/crypto/src/crypto.appup.src b/lib/crypto/src/crypto.appup.src
index 5b4ce5acee..42816773c1 100644
--- a/lib/crypto/src/crypto.appup.src
+++ b/lib/crypto/src/crypto.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -16,13 +16,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
{"%VSN%",
- [
- {<<"2\\.*">>, [{restart_application, crypto}]}
- {<<"1\\.*">>, [{restart_application, crypto}]}
- ],
- [
- {<<"2\\.*">>, [{restart_application, crypto}]}
- {<<"1\\.*">>, [{restart_application, crypto}]}
- ]}.
+ [{<<".*">>,[{restart_application, crypto}]}],
+ [{<<".*">>,[{restart_application, crypto}]}]
+}.
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index d953bd3bca..5bf52fc8a4 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -35,6 +35,7 @@
-export([private_encrypt/4, public_decrypt/4]).
-export([dh_generate_parameters/2, dh_check/1]). %% Testing see
-export([ec_curve/1, ec_curves/0]).
+-export([rand_seed/1]).
%% DEPRECATED
%% Replaced by hash_*
@@ -437,6 +438,11 @@ rand_uniform_pos(_,_) ->
rand_uniform_nif(_From,_To) -> ?nif_stub.
+-spec rand_seed(binary()) -> ok.
+rand_seed(Seed) ->
+ rand_seed_nif(Seed).
+
+rand_seed_nif(_Seed) -> ?nif_stub.
-spec mod_pow(binary()|integer(), binary()|integer(), binary()|integer()) -> binary() | error.
mod_pow(Base, Exponent, Prime) ->
@@ -1774,6 +1780,7 @@ mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub.
rand_bytes,
strong_rand_bytes,
rand_uniform,
+ rand_seed,
mod_pow,
exor,
%% deprecated
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index d1be7cea68..63552d2e70 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -30,6 +30,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[app,
+ appup,
{group, md4},
{group, md5},
{group, ripemd160},
@@ -104,7 +105,21 @@ groups() ->
init_per_suite(Config) ->
try crypto:start() of
ok ->
- Config
+ try crypto:strong_rand_bytes(1) of
+ _ ->
+ Config
+ catch error:low_entropy ->
+ %% Make sure we are on OSE, otherwise we want to crash
+ {ose,_} = os:type(),
+
+ %% This is NOT how you want to seed this, it is just here
+ %% to make the tests pass. Check your OS manual for how you
+ %% really want to seed.
+ {H,M,L} = erlang:now(),
+ Bin = <<H:24,M:20,L:20>>,
+ crypto:rand_seed(<< <<Bin/binary>> || _ <- lists:seq(1,16) >>),
+ Config
+ end
catch _:_ ->
{skip, "Crypto did not start"}
end.
@@ -142,6 +157,11 @@ app() ->
app(Config) when is_list(Config) ->
ok = ?t:app_test(crypto).
%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the crypto appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(crypto).
+%%--------------------------------------------------------------------
hash() ->
[{doc, "Test all different hash functions"}].
hash(Config) when is_list(Config) ->
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 98c071cf87..a2bd6f851a 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.2
+CRYPTO_VSN = 3.3
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 14a17fe304..0653ce4c00 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -505,12 +505,12 @@ lambda(Fun, As) when is_function(Fun) ->
%% ... and the fun was defined in interpreted code
{module, ?MODULE} ->
- {Mod,Name,Bs} =
+ {Mod,Name,Bs, Cs} =
case erlang:fun_info(Fun, env) of
- {env,[{{M,F},Bs0,Cs}]} ->
- {M,F,Bs0};
- {env,[{{M,F},Bs0,Cs,FName}]} ->
- {M,F,add_binding(FName, Fun, Bs0)}
+ {env,[{{M,F},Bs0,Cs0}]} ->
+ {M,F,Bs0, Cs0};
+ {env,[{{M,F},Bs0,Cs0,FName}]} ->
+ {M,F,add_binding(FName, Fun, Bs0), Cs0}
end,
{arity, Arity} = erlang:fun_info(Fun, arity),
if
@@ -654,6 +654,25 @@ expr({tuple,Line,Es0}, Bs0, Ieval) ->
{Vs,Bs} = eval_list(Es0, Bs0, Ieval#ieval{line=Line}),
{value,list_to_tuple(Vs),Bs};
+%% Map
+expr({map,Line,Fs0}, Bs0, Ieval) ->
+ {Fs,Bs} = eval_map_fields(Fs0, Bs0, Ieval#ieval{line=Line,top=false}),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) end,
+ #{}, Fs),
+ {value,Value,Bs};
+expr({map,Line,E0,Fs0}, Bs0, Ieval0) ->
+ Ieval = Ieval0#ieval{line=Line,top=false},
+ {value,E,Bs1} = expr(E0, Bs0, Ieval),
+ case E of
+ #{} ->
+ {Fs,Bs2} = eval_map_fields(Fs0, Bs1, Ieval),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi)
+ end, E, Fs),
+ {value,Value,Bs2};
+ _ ->
+ exception(error, {badarg,E}, Bs1, Ieval)
+ end;
%% A block of statements
expr({block,Line,Es},Bs,Ieval) ->
seq(Es, Bs, Ieval#ieval{line=Line});
@@ -1127,7 +1146,7 @@ eval_generate([V|Rest], P, Bs0, CompFun, Ieval) ->
case catch match1(P, V, erl_eval:new_bindings(), Bs0) of
{match,Bsn} ->
Bs2 = add_bindings(Bsn, Bs0),
- CompFun(Bs2) ++ eval_generate(Rest, P, Bs2, CompFun, Ieval);
+ CompFun(Bs2) ++ eval_generate(Rest, P, Bs0, CompFun, Ieval);
nomatch ->
eval_generate(Rest, P, Bs0, CompFun, Ieval)
end;
@@ -1468,6 +1487,19 @@ guard_expr({cons,_,H0,T0}, Bs) ->
guard_expr({tuple,_,Es0}, Bs) ->
{values,Es} = guard_exprs(Es0, Bs),
{value,list_to_tuple(Es)};
+guard_expr({map,_,Fs0}, Bs) ->
+ Fs = eval_map_fields_guard(Fs0, Bs),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) end,
+ #{}, Fs),
+ {value,Value};
+guard_expr({map,_,E0,Fs0}, Bs) ->
+ {value,E} = guard_expr(E0, Bs),
+ Fs = eval_map_fields_guard(Fs0, Bs),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end,
+ E, Fs),
+ io:format("~p~n", [{E,Value}]),
+ {value,Value};
guard_expr({bin,_,Flds}, Bs) ->
{value,V,_Bs} =
eval_bits:expr_grp(Flds, Bs,
@@ -1477,6 +1509,37 @@ guard_expr({bin,_,Flds}, Bs) ->
end, [], false),
{value,V}.
+
+%% eval_map_fields([Field], Bindings, IEvalState) ->
+%% {[{map_assoc | map_exact,Key,Value}],Bindings}
+
+eval_map_fields(Fs, Bs, Ieval) ->
+ eval_map_fields(Fs, Bs, Ieval, fun expr/3).
+
+eval_map_fields_guard(Fs0, Bs) ->
+ {Fs,_} = eval_map_fields(Fs0, Bs, #ieval{},
+ fun (G0, Bs0, _) ->
+ {value,G} = guard_expr(G0, Bs0),
+ {value,G,Bs0}
+ end),
+ Fs.
+
+eval_map_fields(Fs, Bs, Ieval, F) ->
+ eval_map_fields(Fs, Bs, Ieval, F, []).
+
+eval_map_fields([{map_field_assoc,Line,K0,V0}|Fs], Bs0, Ieval0, F, Acc) ->
+ Ieval = Ieval0#ieval{line=Line},
+ {value,K,Bs1} = F(K0, Bs0, Ieval),
+ {value,V,Bs2} = F(V0, Bs1, Ieval),
+ eval_map_fields(Fs, Bs2, Ieval0, F, [{map_assoc,K,V}|Acc]);
+eval_map_fields([{map_field_exact,Line,K0,V0}|Fs], Bs0, Ieval0, F, Acc) ->
+ Ieval = Ieval0#ieval{line=Line},
+ {value,K,Bs1} = F(K0, Bs0, Ieval),
+ {value,V,Bs2} = F(V0, Bs1, Ieval),
+ eval_map_fields(Fs, Bs2, Ieval0, F, [{map_exact,K,V}|Acc]);
+eval_map_fields([], Bs, _Ieval, _F, Acc) ->
+ {lists:reverse(Acc),Bs}.
+
%% match(Pattern,Term,Bs) -> {match,Bs} | nomatch
match(Pat, Term, Bs) ->
try match1(Pat, Term, Bs, Bs)
@@ -1506,6 +1569,8 @@ match1({cons,_,H,T}, [H1|T1], Bs0, BBs) ->
match1({tuple,_,Elts}, Tuple, Bs, BBs)
when length(Elts) =:= tuple_size(Tuple) ->
match_tuple(Elts, Tuple, 1, Bs, BBs);
+match1({map,_,Fields}, Map, Bs, BBs) when is_map(Map) ->
+ match_map(Fields, Map, Bs, BBs);
match1({bin,_,Fs}, B, Bs0, BBs) when is_bitstring(B) ->
try eval_bits:match_bits(Fs, B, Bs0, BBs,
match_fun(BBs),
@@ -1529,6 +1594,17 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) ->
match_tuple([], _, _, Bs, _BBs) ->
{match,Bs}.
+match_map([{map_field_exact,_,K0,Pat}|Fs], Map, Bs0, BBs) ->
+ {value,K,BBs} = expr(K0, BBs, #ieval{}),
+ case maps:find(K, Map) of
+ {ok,Value} ->
+ {match,Bs} = match1(Pat, Value, Bs0, BBs),
+ match_map(Fs, Map, Bs, BBs);
+ error -> throw(nomatch)
+ end;
+match_map([], _, Bs, _BBs) ->
+ {match,Bs}.
+
head_match([Par|Pars], [Arg|Args], Bs0, BBs) ->
try match1(Par, Arg, Bs0, BBs) of
{match,Bs} -> head_match(Pars, Args, Bs, BBs)
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 9806692afc..ad05a7c529 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -42,18 +42,21 @@
load_mod(Mod, File, Binary, Db) ->
Flag = process_flag(trap_exit, true),
- Pid = spawn_link(fun () -> load_mod1(Mod, File, Binary, Db) end),
+ Pid = spawn_link(load_mod1(Mod, File, Binary, Db)),
receive
{'EXIT', Pid, What} ->
process_flag(trap_exit, Flag),
What
end.
--spec load_mod1(atom(), file:filename(), binary(), ets:tid()) -> no_return().
+-spec load_mod1(atom(), file:filename(), binary(), ets:tid()) ->
+ fun(() -> no_return()).
load_mod1(Mod, File, Binary, Db) ->
- store_module(Mod, File, Binary, Db),
- exit({ok, Mod}).
+ fun() ->
+ store_module(Mod, File, Binary, Db),
+ exit({ok, Mod})
+ end.
%%====================================================================
%% Internal functions
@@ -194,6 +197,11 @@ pattern({cons,Line,H0,T0}) ->
pattern({tuple,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{tuple,Line,Ps1};
+pattern({map,Line,Fs0}) ->
+ Fs1 = lists:map(fun ({map_field_exact,L,K,V}) ->
+ {map_field_exact,L,expr(K, false),pattern(V)}
+ end, Fs0),
+ {map,Line,Fs1};
pattern({op,_,'-',{integer,Line,I}}) ->
{value,Line,-I};
pattern({op,_,'+',{integer,Line,I}}) ->
@@ -262,6 +270,8 @@ guard_test({string,Line,_}) -> {value,Line,false};
guard_test({nil,Line}) -> {value,Line,false};
guard_test({cons,Line,_,_}) -> {value,Line,false};
guard_test({tuple,Line,_}) -> {value,Line,false};
+guard_test({map,Line,_}) -> {value,Line,false};
+guard_test({map,Line,_,_}) -> {value,Line,false};
guard_test({bin,Line,_}) -> {value,Line,false}.
gexpr({var,Line,V}) -> {var,Line,V};
@@ -279,6 +289,13 @@ gexpr({cons,Line,H0,T0}) ->
gexpr({tuple,Line,Es0}) ->
Es1 = gexpr_list(Es0),
{tuple,Line,Es1};
+gexpr({map,Line,Fs0}) ->
+ Fs1 = map_fields(Fs0, fun gexpr/1),
+ {map,Line,Fs1};
+gexpr({map,Line,E0,Fs0}) ->
+ E1 = gexpr(E0),
+ Fs1 = map_fields(Fs0, fun gexpr/1),
+ {map,Line,E1,Fs1};
gexpr({bin,Line,Flds0}) ->
Flds = gexpr_list(Flds0),
{bin,Line,Flds};
@@ -341,6 +358,13 @@ expr({cons,Line,H0,T0}, _Lc) ->
expr({tuple,Line,Es0}, _Lc) ->
Es1 = expr_list(Es0),
{tuple,Line,Es1};
+expr({map,Line,Fs0}, _Lc) ->
+ Fs1 = map_fields(Fs0),
+ {map,Line,Fs1};
+expr({map,Line,E0,Fs0}, _Lc) ->
+ E1 = expr(E0, false),
+ Fs1 = map_fields(Fs0),
+ {map,Line,E1,Fs1};
expr({block,Line,Es0}, Lc) ->
%% Unfold block into a sequence.
Es1 = exprs(Es0, Lc),
@@ -436,7 +460,7 @@ expr({lc,Line,E0,Gs0}, _Lc) -> %R8.
({b_generate,L,P0,Qs}) -> %R12.
{b_generate,L,expr(P0, false),expr(Qs, false)};
(Expr) ->
- case is_guard(Expr) of
+ case erl_lint:is_guard_test(Expr) of
true -> {guard,guard([[Expr]])};
false -> expr(Expr, false)
end
@@ -448,7 +472,7 @@ expr({bc,Line,E0,Gs0}, _Lc) -> %R12.
({b_generate,L,P0,Qs}) -> %R12.
{b_generate,L,expr(P0, false),expr(Qs, false)};
(Expr) ->
- case is_guard(Expr) of
+ case erl_lint:is_guard_test(Expr) of
true -> {guard,guard([[Expr]])};
false -> expr(Expr, false)
end
@@ -491,42 +515,6 @@ expr({bin_element,Line,Expr,Size,Type}, _Lc) ->
expr(Other, _Lc) ->
exit({?MODULE,{unknown_expr,Other}}).
-%% is_guard(Expression) -> true | false.
-%% Test if a general expression is a guard test or guard BIF.
-%% Cannot use erl_lint here as sys_pre_expand has transformed source.
-
-is_guard({op,_,Op,L,R}) ->
- erl_internal:comp_op(Op, 2) andalso is_gexpr_list([L,R]);
-is_guard({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) ->
- Arity = length(As),
- (erl_internal:guard_bif(Test, Arity) orelse
- erl_internal:old_type_test(Test, Arity)) andalso is_gexpr_list(As);
-is_guard({atom,_,true}) -> true;
-is_guard(_) -> false.
-
-is_gexpr({var,_,_}) -> true;
-is_gexpr({atom,_,_}) -> true;
-is_gexpr({integer,_,_}) -> true;
-is_gexpr({char,_,_}) -> true;
-is_gexpr({float,_,_}) -> true;
-is_gexpr({string,_,_}) -> true;
-is_gexpr({nil,_}) -> true;
-is_gexpr({cons,_,H,T}) -> is_gexpr_list([H,T]);
-is_gexpr({tuple,_,Es}) -> is_gexpr_list(Es);
-is_gexpr({call,_,{remote,_,{atom,_,erlang},{atom,_,F}},As}) ->
- Ar = length(As),
- case erl_internal:guard_bif(F, Ar) of
- true -> is_gexpr_list(As);
- false -> erl_internal:arith_op(F, Ar) andalso is_gexpr_list(As)
- end;
-is_gexpr({op,_,Op,A}) ->
- erl_internal:arith_op(Op, 1) andalso is_gexpr(A);
-is_gexpr({op,_,Op,A1,A2}) ->
- erl_internal:arith_op(Op, 2) andalso is_gexpr_list([A1,A2]);
-is_gexpr(_) -> false.
-
-is_gexpr_list(Es) -> lists:all(fun (E) -> is_gexpr(E) end, Es).
-
consify([A|As]) ->
{cons,0,A,consify(As)};
consify([]) -> {value,0,[]}.
@@ -550,6 +538,15 @@ fun_clauses([{clause,L,H,G,B}|Cs]) ->
[{clause,L,head(H),guard(G),exprs(B, true)}|fun_clauses(Cs)];
fun_clauses([]) -> [].
+map_fields(Fs) ->
+ map_fields(Fs, fun (E) -> expr(E, false) end).
+
+map_fields([{map_field_assoc,L,N,V}|Fs], F) ->
+ [{map_field_assoc,L,F(N),F(V)}|map_fields(Fs)];
+map_fields([{map_field_exact,L,N,V}|Fs], F) ->
+ [{map_field_exact,L,F(N),F(V)}|map_fields(Fs)];
+map_fields([], _) -> [].
+
%% new_var_name() -> VarName.
new_var_name() ->
diff --git a/lib/debugger/src/dbg_wx_filedialog_win.erl b/lib/debugger/src/dbg_wx_filedialog_win.erl
index c8ecb7b5d4..ea34295067 100644
--- a/lib/debugger/src/dbg_wx_filedialog_win.erl
+++ b/lib/debugger/src/dbg_wx_filedialog_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. 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
@@ -242,16 +242,13 @@ handle_event(#wx{event=#wxList{itemIndex=Index}},
{noreply, State0}
end;
-handle_event(#wx{event=#wxCommand{type=command_text_updated, cmdString=Wanted}},
+handle_event(#wx{event=#wxCommand{type=command_text_updated, cmdString=Wanted}},
State = #state{ptext=Previous, completion=Comp}) ->
case Previous =:= undefined orelse lists:prefix(Wanted, Previous) of
- true ->
- case Comp of
- {Temp,_} -> wxWindow:destroy(Temp);
- undefined -> ok
- end,
+ true ->
+ destroy_completion(Comp),
{noreply, State#state{ptext=Wanted,completion=undefined}};
- false ->
+ false ->
{noreply, show_completion(Wanted, State)}
end;
@@ -310,8 +307,7 @@ handle_event(#wx{event=#wxSize{size={Width,_}}}, State = #state{list=LC}) ->
end),
{noreply, State};
-handle_event(Event,State) ->
- io:format("~p Got ~p ~n",[self(), Event]),
+handle_event(_Event,State) ->
{noreply, State}.
handle_info(_Msg, State) ->
@@ -419,8 +415,9 @@ show_completion(Wanted, State = #state{text=TC, win=Win, list=LC, completion=Com
end.
destroy_completion(undefined) -> ok;
-destroy_completion({Window, _}) ->
+destroy_completion({Window, _LB}) ->
Parent = wxWindow:getParent(Window),
+ wxWindow:hide(Window),
wxWindow:destroy(Window),
wxWindow:refresh(Parent).
diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl
index 4a01492cf5..4ab03985d3 100644
--- a/lib/debugger/src/dbg_wx_mon.erl
+++ b/lib/debugger/src/dbg_wx_mon.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -148,7 +148,7 @@ init2(CallingPid, Mode, SFile, GS) ->
win = Win,
focus = undefined,
- coords = {20,20},
+ coords = {-1,-1},
intdir = element(2, file:get_cwd()),
pinfos = [],
diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl
index 0071b27027..d94eb14937 100644
--- a/lib/debugger/src/dbg_wx_mon_win.erl
+++ b/lib/debugger/src/dbg_wx_mon_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -617,8 +617,6 @@ handle_event(#wx{userData=Data,
_WinInfo) ->
Data;
handle_event(_Event, _WinInfo) ->
-%% FIXME
- io:format("Ev: ~p~n",[_Event]),
ignore.
%%====================================================================
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 1ac796bb4c..4438466bb0 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -125,7 +125,7 @@ init(Pid, Parent, Meta, TraceWin, BackTrace, Strings) ->
dbg_wx_winman:insert(Title, Window),
%% Initial process state
- State1 = #state{win=Win, coords={0,0}, pid=Pid, meta=Meta,
+ State1 = #state{win=Win, coords={-1,-1}, pid=Pid, meta=Meta,
status={idle,null,null},
stack={1,1}, strings=[str_on]},
diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl
index 6242b9d0e0..fc7ffc0d56 100644
--- a/lib/debugger/src/dbg_wx_view.erl
+++ b/lib/debugger/src/dbg_wx_view.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -72,7 +72,7 @@ init(GS, Env, Mod, Title) ->
Win2,
int:all_breaks(Mod)),
- try loop(#state{gs=GS, win=Win3, coords={0,0}, mod=Mod})
+ try loop(#state{gs=GS, win=Win3, coords={-1,-1}, mod=Mod})
catch _E:normal ->
exit(normal);
_E:_R ->
diff --git a/lib/debugger/src/debugger.app.src b/lib/debugger/src/debugger.app.src
index 84fb98c94e..f102385d39 100644
--- a/lib/debugger/src/debugger.app.src
+++ b/lib/debugger/src/debugger.app.src
@@ -46,4 +46,6 @@
int
]},
{registered, [dbg_iserver, dbg_wx_mon, dbg_wx_winman]},
- {applications, [kernel, stdlib]}]}.
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","kernel-3.0","erts-6.0",
+ "compiler-5.0"]}]}.
diff --git a/lib/debugger/src/debugger.appup.src b/lib/debugger/src/debugger.appup.src
index 7a435e9b22..81d2fab05a 100644
--- a/lib/debugger/src/debugger.appup.src
+++ b/lib/debugger/src/debugger.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -15,5 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-{"%VSN%",[],[]}.
+{"%VSN%",
+ [{<<".*">>,[{restart_application, debugger}]}],
+ [{<<".*">>,[{restart_application, debugger}]}]
+}.
diff --git a/lib/debugger/src/debugger.erl b/lib/debugger/src/debugger.erl
index 8a2ac28df5..77fd0acb70 100644
--- a/lib/debugger/src/debugger.erl
+++ b/lib/debugger/src/debugger.erl
@@ -51,12 +51,6 @@
%% ------------------------------
%% Help window for creating new breakpoints.
%%
-%% dbg_wx_edit, dbg_wx_edit_win
-%% --------------------------------------
-%% Help window for editing terms, used for setting backtrace size
-%% (i.e. how many stack frames to display in the attach process window)
-%% and changing variable values.
-%%
%% dbg_wx_interpret, dbg_wx_filedialog_win
%% --------------------------------------
%% Help window for selecting modules to interpret.
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index bdd671cff1..2755db64b8 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -630,14 +630,13 @@ find_beam(Mod, Src) ->
File = filename:join(SrcDir, BeamFile),
case is_file(File) of
true -> File;
- false -> find_beam_1(Mod, BeamFile, SrcDir)
+ false -> find_beam_1(BeamFile, SrcDir)
end.
-find_beam_1(Mod, BeamFile, SrcDir) ->
+find_beam_1(BeamFile, SrcDir) ->
RootDir = filename:dirname(SrcDir),
EbinDir = filename:join(RootDir, "ebin"),
CodePath = [EbinDir | code:get_path()],
- BeamFile = atom_to_list(Mod) ++ code:objfile_extension(),
lists:foldl(fun(_, Beam) when is_list(Beam) -> Beam;
(Dir, error) ->
File = filename:join(Dir, BeamFile),
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index 5aa290c61b..9a0ce14ecd 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -44,6 +44,7 @@ MODULES= \
fun_SUITE \
lc_SUITE \
line_number_SUITE \
+ map_SUITE \
record_SUITE \
trycatch_SUITE \
test_lib \
diff --git a/lib/debugger/test/bs_construct_SUITE.erl b/lib/debugger/test/bs_construct_SUITE.erl
index 4870c87d74..8a6798c6ad 100644
--- a/lib/debugger/test/bs_construct_SUITE.erl
+++ b/lib/debugger/test/bs_construct_SUITE.erl
@@ -647,19 +647,27 @@ make_sub_bin(Bin0) ->
%% give the same result.
dynamic(Config) when is_list(Config) ->
- ?line dynamic_1(fun dynamic_big/5),
- ?line dynamic_1(fun dynamic_little/5),
+ Ps = [spawn_monitor(fun() ->
+ dynamic_1(Fun)
+ end) || Fun <- [fun dynamic_big/5,
+ fun dynamic_little/5]],
+ [receive
+ {'DOWN',Ref,process,Pid,normal} ->
+ ok;
+ {'DOWN',Ref,process,Pid,Exit} ->
+ ?t:fail({Pid,Exit})
+ end || {Pid,Ref} <- Ps],
ok.
dynamic_1(Dynamic) ->
- <<Lpad:128>> = erlang:md5([0]),
- <<Rpad:128>> = erlang:md5([1]),
- <<Int:128>> = erlang:md5([2]),
- 8385 = dynamic_2(0, {Int,Lpad,Rpad,Dynamic}, 0).
+ <<Lpad:64,_/binary>> = erlang:md5([0]),
+ <<Rpad:64,_/binary>> = erlang:md5([1]),
+ <<Int:64,_/binary>> = erlang:md5([2]),
+ 2145 = dynamic_2(0, {Int,Lpad,Rpad,Dynamic}, 0).
-dynamic_2(129, _, Count) -> Count;
+dynamic_2(64+1, _, Count) -> Count;
dynamic_2(Bef, Data, Count0) ->
- Count = dynamic_3(Bef, 128-Bef, Data, Count0),
+ Count = dynamic_3(Bef, 64-Bef, Data, Count0),
dynamic_2(Bef+1, Data, Count).
dynamic_3(_, -1, _, Count) -> Count;
@@ -680,13 +688,13 @@ dynamic_big(Bef, N, Int, Lpad, Rpad) ->
<<MaskedInt:N>> = NumBin,
%% Construct the binary in two different ways.
- Bin = id(<<Lpad:Bef,NumBin/bitstring,Rpad:(128-Bef-N)>>),
- Bin = <<Lpad:Bef,Int:N,Rpad:(128-Bef-N)>>,
+ Bin = id(<<Lpad:Bef,NumBin/bitstring,Rpad:(64-Bef-N)>>),
+ Bin = <<Lpad:Bef,Int:N,Rpad:(64-Bef-N)>>,
%% Further verify the result by matching.
LpadMasked = Lpad band ((1 bsl Bef) - 1),
- RpadMasked = Rpad band ((1 bsl (128-Bef-N)) - 1),
- Rbits = (128-Bef-N),
+ RpadMasked = Rpad band ((1 bsl (64-Bef-N)) - 1),
+ Rbits = (64-Bef-N),
<<LpadMasked:Bef,MaskedInt:N,RpadMasked:Rbits>> = id(Bin),
ok.
@@ -696,13 +704,13 @@ dynamic_little(Bef, N, Int, Lpad, Rpad) ->
<<MaskedInt:N/little>> = NumBin,
%% Construct the binary in two different ways.
- Bin = id(<<Lpad:Bef/little,NumBin/bitstring,Rpad:(128-Bef-N)/little>>),
- Bin = <<Lpad:Bef/little,Int:N/little,Rpad:(128-Bef-N)/little>>,
+ Bin = id(<<Lpad:Bef/little,NumBin/bitstring,Rpad:(64-Bef-N)/little>>),
+ Bin = <<Lpad:Bef/little,Int:N/little,Rpad:(64-Bef-N)/little>>,
%% Further verify the result by matching.
LpadMasked = Lpad band ((1 bsl Bef) - 1),
- RpadMasked = Rpad band ((1 bsl (128-Bef-N)) - 1),
- Rbits = (128-Bef-N),
+ RpadMasked = Rpad band ((1 bsl (64-Bef-N)) - 1),
+ Rbits = (64-Bef-N),
<<LpadMasked:Bef/little,MaskedInt:N/little,RpadMasked:Rbits/little>> = id(Bin),
ok.
diff --git a/lib/debugger/test/debugger_SUITE.erl b/lib/debugger/test/debugger_SUITE.erl
index 6f5442e97d..c74550be86 100644
--- a/lib/debugger/test/debugger_SUITE.erl
+++ b/lib/debugger/test/debugger_SUITE.erl
@@ -27,13 +27,13 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
- app_test/1,erts_debug/1,encrypted_debug_info/1,
+ app_test/1,appup_test/1,erts_debug/1,encrypted_debug_info/1,
no_abstract_code/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test, erts_debug, no_abstract_code,
+ [app_test, appup_test, erts_debug, no_abstract_code,
encrypted_debug_info].
groups() ->
@@ -64,6 +64,9 @@ app_test(Config) when is_list(Config) ->
?line ?t:app_test(debugger),
ok.
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(debugger).
+
erts_debug(Config) when is_list(Config) ->
c:l(erts_debug),
ok.
diff --git a/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
index be9312b68f..ba60ed6fb3 100644
--- a/lib/debugger/test/erl_eval_SUITE.erl
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -64,6 +64,7 @@ config(priv_dir,_) ->
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
end_per_testcase(_Case, Config) ->
diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl
index 4ffcf7888e..ecbd68ab40 100644
--- a/lib/debugger/test/int_eval_SUITE.erl
+++ b/lib/debugger/test/int_eval_SUITE.erl
@@ -28,7 +28,7 @@
bifs_outside_erlang/1, spawning/1, applying/1,
catch_and_throw/1, external_call/1, test_module_info/1,
apply_interpreted_fun/1, apply_uninterpreted_fun/1,
- interpreted_exit/1, otp_8310/1, stacktrace/1]).
+ interpreted_exit/1, otp_8310/1, stacktrace/1, maps/1]).
%% Helpers.
-export([applier/3]).
@@ -44,7 +44,7 @@ all() ->
[bifs_outside_erlang, spawning, applying,
catch_and_throw, external_call, test_module_info,
apply_interpreted_fun, apply_uninterpreted_fun,
- interpreted_exit, otp_8310, stacktrace].
+ interpreted_exit, otp_8310, stacktrace, maps].
groups() ->
[].
@@ -291,6 +291,11 @@ stacktrace(Config) when is_list(Config) ->
end,
ok.
+maps(Config) when is_list(Config) ->
+ Fun = fun () -> ?IM:empty_map_update([camembert]) end,
+ {'EXIT',{{badarg,[camembert]},_}} = spawn_eval(Fun),
+ ok.
+
do_eval(Config, Mod) ->
?line DataDir = ?config(data_dir, Config),
diff --git a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
index ab485fd350..e047a33d8c 100644
--- a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
+++ b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
@@ -29,6 +29,7 @@
-export([more_catch/1,more_nocatch/1,exit_me/0]).
-export([f/1, f_try/1, f_catch/1]).
-export([otp_5837/1, otp_8310/0]).
+-export([empty_map_update/1]).
%% Internal exports.
-export([echo/2,my_subtract/2,catch_a_ball/0,throw_a_ball/0]).
@@ -241,3 +242,5 @@ otp_8310() ->
true = begin (X3 = true) orelse X3, X3 end,
false = begin (X4 = false) orelse X4, X4 end,
ok.
+
+empty_map_update(Map) -> Map#{}.
diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl
new file mode 100644
index 0000000000..0076193725
--- /dev/null
+++ b/lib/debugger/test/map_SUITE.erl
@@ -0,0 +1,1003 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. 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(map_SUITE).
+
+%% Copied from map_SUITE in erts.
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2
+ ]).
+
+-export([
+ t_build_and_match_literals/1,
+ t_update_literals/1,t_match_and_update_literals/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1,t_update_exact/1,
+ t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
+ t_guard_receive/1, t_guard_fun/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ %t_size/1,
+ t_map_size/1,
+
+ %% Specific Map BIFs
+ t_bif_map_get/1,
+ t_bif_map_find/1,
+ t_bif_map_is_key/1,
+ t_bif_map_keys/1,
+ t_bif_map_merge/1,
+ t_bif_map_new/1,
+ t_bif_map_put/1,
+ t_bif_map_remove/1,
+ t_bif_map_update/1,
+ t_bif_map_values/1,
+ t_bif_map_to_list/1,
+ t_bif_map_from_list/1,
+
+ %% erlang
+ t_erlang_hash/1,
+ t_map_encode_decode/1,
+
+ %% maps module not bifs
+ t_maps_fold/1,
+ t_maps_map/1,
+ t_maps_size/1,
+ t_maps_without/1,
+
+ %% misc
+ t_pdict/1,
+ t_ets/1,
+ t_dets/1
+ ]).
+
+-include_lib("stdlib/include/ms_transform.hrl").
+
+suite() -> [].
+
+all() -> [
+ t_build_and_match_literals,
+ t_update_literals, t_match_and_update_literals,
+ t_update_map_expressions,
+ t_update_assoc,t_update_exact,
+ t_guard_bifs, t_guard_sequence, t_guard_update,
+ t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_sort_literals,
+
+ %% Specific Map BIFs
+ t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
+ t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
+ t_bif_map_put,
+ t_bif_map_remove, t_bif_map_update,
+ t_bif_map_values,
+ t_bif_map_to_list, t_bif_map_from_list,
+
+ %% erlang
+ t_erlang_hash, t_map_encode_decode,
+ t_map_size,
+
+ %% maps module
+ t_maps_fold, t_maps_map,
+ t_maps_size, t_maps_without,
+
+
+ %% Other functions
+ t_pdict,
+ t_ets
+ ].
+
+groups() -> [].
+
+init_per_suite(Config) ->
+ test_lib:interpret(?MODULE),
+ Config.
+
+end_per_suite(_Config) -> ok.
+
+init_per_group(_GroupName, Config) -> Config.
+end_per_group(_GroupName, Config) -> Config.
+
+%% tests
+
+t_build_and_match_literals(Config) when is_list(Config) ->
+ #{} = id(#{}),
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% error case
+ %V = 32,
+ %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ ok.
+
+
+%% Tests size(Map).
+%% not implemented, perhaps it shouldn't be either
+
+%t_size(Config) when is_list(Config) ->
+% 0 = size(#{}),
+% 1 = size(#{a=>1}),
+% 1 = size(#{a=>#{a=>1}}),
+% 2 = size(#{a=>1, b=>2}),
+% 3 = size(#{a=>1, b=>2, b=>"3"}),
+% ok.
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 1),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "c" => 2}, 2),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+% test map updates without matching
+t_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
+
+% test map updates with matching
+t_match_and_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+
+t_update_map_expressions(Config) when is_list(Config) ->
+ M = maps:new(),
+ #{ a := 1 } = M#{a => 1},
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{{badarg,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{{badarg,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+
+t_update_assoc(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{{badarg,BadMap},_}} = (catch BadMap#{nonexisting=>val}),
+
+ ok.
+
+t_update_exact(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ %% M2 = M0#{3=>wrong,3.0:=new}, %% FIXME
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+
+ ok.
+
+t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ ok.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+t_guard_sequence(Config) when is_list(Config) ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+
+t_guard_update(Config) when is_list(Config) ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(_, _) -> error.
+
+t_guard_receive(Config) when is_list(Config) ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <<B1/binary, B2/binary>>,
+
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
+
+
+t_list_comprehension(Config) when is_list(Config) ->
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
+
+t_guard_fun(Config) when is_list(Config) ->
+ F1 = fun
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
+ ok.
+
+
+t_map_sort_literals(Config) when is_list(Config) ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = #{ a => 1 } < id(#{ b => 1}),
+ false = #{ b => 1 } < id(#{ a => 1}),
+ true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}),
+ true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
+ false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
+ false = #{ 1 => 1 } < id(#{ 1.0 => 1}),
+ false = #{ 1.0 => 1 } < id(#{ 1 => 1}),
+
+ %% value order
+ true = #{ a => 1 } < id(#{ a => 2}),
+ false = #{ a => 2 } < id(#{ a => 1}),
+ false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}),
+ true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}),
+
+ true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+%% BIFs
+t_bif_map_get(Config) when is_list(Config) ->
+
+ 1 = maps:get(a, #{ a=> 1}),
+ 2 = maps:get(b, #{ a=> 1, b => 2}),
+ "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}),
+ "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])),
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})),
+ ok.
+
+t_bif_map_find(Config) when is_list(Config) ->
+
+ {ok, 1} = maps:find(a, #{ a=> 1}),
+ {ok, 2} = maps:find(b, #{ a=> 1, b => 2}),
+ {ok, "int"} = maps:find(1, #{ 1 => "int"}),
+ {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}),
+
+ {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
+ {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ error = maps:find(a,#{}),
+ error = maps:find(a,#{b=>1, c=>2}),
+ error = maps:find(1.0, #{ 1 => "int"}),
+ error = maps:find(1, #{ 1.0 => "float"}),
+ error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+
+
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))),
+ ok.
+
+
+t_bif_map_is_key(Config) when is_list(Config) ->
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(int, M1),
+ true = maps:is_key(<<"key">>, M1),
+ true = maps:is_key(4, M1),
+
+ false = maps:is_key(5, M1),
+ false = maps:is_key(<<"key2">>, M1),
+ false = maps:is_key("h", M1),
+ false = maps:is_key("hello", M1),
+ false = maps:is_key(atom, M1),
+ false = maps:is_key(any, id(#{})),
+
+ false = maps:is_key("hi", maps:remove("hi", M1)),
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(1, maps:put(1, "number", M1)),
+ false = maps:is_key(1.0, maps:put(1, "number", M1)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))),
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))),
+ ok.
+
+t_bif_map_keys(Config) when is_list(Config) ->
+ [] = maps:keys(#{}),
+
+ [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ [4,int,"hi",<<"key">>] = maps:keys(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)),
+ ok.
+
+t_bif_map_new(Config) when is_list(Config) ->
+ #{} = maps:new(),
+ 0 = erlang:map_size(maps:new()),
+ ok.
+
+t_bif_map_merge(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:merge(#{},#{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}),
+
+ M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer },
+
+ #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0),
+
+ #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )),
+
+ ok.
+
+
+t_bif_map_put(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}),
+
+ ["hi"] = maps:keys(M1),
+ ["hello"] = maps:values(M1),
+
+ M2 = #{ int := 3 } = maps:put(int, 3, M1),
+
+ [int,"hi"] = maps:keys(M2),
+ [3,"hello"] = maps:values(M2),
+
+ M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),
+
+ [int,"hi",<<"key">>] = maps:keys(M3),
+ [3,"hello",<<"value">>] = maps:values(M3),
+
+ M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),
+
+ [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4),
+ [wat,3,"hello",<<"value">>] = maps:values(M4),
+
+ M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5),
+ [number,wat,3,"hello",<<"value">>] = maps:values(M5),
+
+ M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6),
+ [number,wat,3,"hello",<<"other value">>] = maps:values(M6),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)),
+ ok.
+
+t_bif_map_remove(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:remove(some_key, #{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = maps:remove("hi", M0),
+ [4,18446744073709551629,int,<<"key">>] = maps:keys(M1),
+ [number,wat,3,<<"value">>] = maps:values(M1),
+
+ M2 = maps:remove(int, M1),
+ [4,18446744073709551629,<<"key">>] = maps:keys(M2),
+ [number,wat,<<"value">>] = maps:values(M2),
+
+ M3 = maps:remove(<<"key">>, M2),
+ [4,18446744073709551629] = maps:keys(M3),
+ [number,wat] = maps:values(M3),
+
+ M4 = maps:remove(18446744073709551629, M3),
+ [4] = maps:keys(M4),
+ [number] = maps:values(M4),
+
+ M5 = maps:remove(4, M4),
+ [] = maps:keys(M5),
+ [] = maps:values(M5),
+
+ M0 = maps:remove(5,M0),
+ M0 = maps:remove("hi there",M0),
+
+ #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)),
+ ok.
+
+t_bif_map_update(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0),
+
+ #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)),
+
+ ok.
+
+
+
+t_bif_map_values(Config) when is_list(Config) ->
+
+ [] = maps:values(#{}),
+
+ [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
+ [number,3,"hello2",<<"value2">>] = maps:values(M2),
+ [number,3,"hello",<<"value">>] = maps:values(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)),
+ ok.
+
+t_erlang_hash(Config) when is_list(Config) ->
+
+ ok = t_bif_erlang_phash2(),
+ ok = t_bif_erlang_phash(),
+ ok = t_bif_erlang_hash(),
+
+ ok.
+
+t_bif_erlang_phash2() ->
+
+ 39679005 = erlang:phash2(#{}),
+ 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
+ 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
+ 14363616 = erlang:phash2(#{ 1 => a }),
+ 51612236 = erlang:phash2(#{ a => 1 }),
+
+ 37468437 = erlang:phash2(#{{} => <<>>}),
+ 44049159 = erlang:phash2(#{<<>> => {}}),
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 118679416 = erlang:phash2(M0),
+ 51612236 = erlang:phash2(M1),
+ 118679416 = erlang:phash2(M2),
+ ok.
+
+t_bif_erlang_phash() ->
+ Sz = 1 bsl 32,
+ 268440612 = erlang:phash(#{},Sz),
+ 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 1394238263 = erlang:phash(#{ 1 => a },Sz),
+ 4066388227 = erlang:phash(#{ a => 1 },Sz),
+
+ 1578050717 = erlang:phash(#{{} => <<>>},Sz),
+ 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 3590546636 = erlang:phash(M0,Sz),
+ 4066388227 = erlang:phash(M1,Sz),
+ 3590546636 = erlang:phash(M2,Sz),
+ ok.
+
+t_bif_erlang_hash() ->
+ Sz = 1 bsl 27 - 1,
+ 5158 = erlang:hash(#{},Sz),
+ 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 126071654 = erlang:hash(#{ 1 => a },Sz),
+ 126426236 = erlang:hash(#{ a => 1 },Sz),
+
+ 101655720 = erlang:hash(#{{} => <<>>},Sz),
+ 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 38260486 = erlang:hash(M0,Sz),
+ 126426236 = erlang:hash(M1,Sz),
+ 38260486 = erlang:hash(M2,Sz),
+ ok.
+
+
+t_map_encode_decode(Config) when is_list(Config) ->
+ <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}),
+ Pairs = [
+ {a,b},{"key","values"},{<<"key">>,<<"value">>},
+ {1,b},{[atom,1],{<<"wat">>,1,2,3}},
+ {aa,"values"},
+ {1 bsl 64 + (1 bsl 50 - 1), sc1},
+ {99, sc2},
+ {1 bsl 65 + (1 bsl 51 - 1), sc3},
+ {88, sc4},
+ {1 bsl 66 + (1 bsl 52 - 1), sc5},
+ {77, sc6},
+ {1 bsl 67 + (1 bsl 53 - 1), sc3},
+ {75, sc6}, {-10,sc8},
+ {<<>>, sc9}, {3.14158, sc10},
+ {[3.14158], sc11}, {more_atoms, sc12},
+ {{more_tuples}, sc13}, {self(), sc14},
+ {{},{}},{[],[]}
+ ],
+ ok = map_encode_decode_and_match(Pairs,[],#{}),
+
+ %% check sorting
+
+ %% literally #{ b=>2, a=>1 } in the internal order
+ #{ a:=1, b:=2 } =
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,97,2,100,0,1,97,97,1>>),
+
+
+ %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
+ #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
+ 107,0,2,104,105, % "hi" :: list()
+ 107,0,5,118,97,108,117,101, % "value" :: list()
+ 100,0,1,97, % a :: atom()
+ 97,33, % 33 :: integer()
+ 100,0,1,98, % b :: atom()
+ 97,55 % 55 :: integer()
+ >>),
+
+
+ %% error cases
+ %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>>
+ %% which is: #{ a=>1, b=>1 }
+
+ %% uniqueness violation
+ %% literally #{ a=>1, "hi"=>"value", a=>2 }
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)),
+
+ %% bad size (too large)
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)),
+
+ %% bad size (too small) .. should fail just truncate it .. weird.
+ %% possibly change external format so truncated will be #{a:=1}
+ #{ a:=b } =
+ erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),
+
+ ok.
+
+map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
+ M1 = maps:put(K,V,M0),
+ B0 = erlang:term_to_binary(M1),
+ Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
+ %% sort Ks and Vs according to term spec, then match it
+ KVbins = lists:foldr(fun({_,Kbin,Vbin}, Acc) -> [Kbin,Vbin | Acc] end, [], Ls),
+ ok = match_encoded_map(B0, length(Ls), KVbins),
+ %% decode and match it
+ M1 = erlang:binary_to_term(B0),
+ map_encode_decode_and_match(Pairs,Ls,M1);
+map_encode_decode_and_match([],_,_) -> ok.
+
+match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) ->
+ match_encoded_map(Encoded,Items);
+match_encoded_map(_,_,_) -> no_match_size.
+
+match_encoded_map(<<>>,[]) -> ok;
+match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
+ Size = erlang:byte_size(Item),
+ <<EncodedTerm:Size/binary, Bin1/binary>> = Bin,
+ EncodedTerm = Item, %% Asssert
+ match_encoded_map(Bin1,Items).
+
+
+t_bif_map_to_list(Config) when is_list(Config) ->
+ [] = maps:to_list(#{}),
+ [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}),
+ [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
+ [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}),
+
+ [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
+ <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))),
+ ok.
+
+
+t_bif_map_from_list(Config) when is_list(Config) ->
+ #{} = maps:from_list([]),
+ A = maps:from_list([]),
+ 0 = erlang:map_size(A),
+
+ #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]),
+ #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]),
+ #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]),
+
+ #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]),
+
+ #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} =
+ maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),
+
+ #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} =
+ maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
+ {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
+ ok.
+
+%% Maps module, not BIFs
+t_maps_fold(_Config) ->
+ Vs = lists:seq(1,100),
+ M = maps:from_list([{{k,I},{v,I}}||I<-Vs]),
+
+ %% fold
+ 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M),
+
+ ok.
+
+t_maps_map(_Config) ->
+ Vs = lists:seq(1,100),
+ M1 = maps:from_list([{I,I}||I<-Vs]),
+ M2 = maps:from_list([{I,{token,I}}||I<-Vs]),
+
+ M2 = maps:map(fun(_K,V) -> {token,V} end, M1),
+ ok.
+
+t_maps_size(_Config) ->
+ Vs = lists:seq(1,100),
+ lists:foldl(fun(I,M) ->
+ M1 = maps:put(I,I,M),
+ I = maps:size(M1),
+ M1
+ end, #{}, Vs),
+ ok.
+
+
+t_maps_without(_Config) ->
+ Ki = [11,22,33,44,55,66,77,88,99],
+ M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
+ M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]),
+ M1 = maps:without([{k,I}||I <- Ki],M0),
+ ok.
+
+
+%% MISC
+t_pdict(_Config) ->
+
+ put(#{ a => b, b => a},#{ c => d}),
+ put(get(#{ a => b, b => a}),1),
+ 1 = get(#{ c => d}),
+ #{ c := d } = get(#{ a => b, b => a}).
+
+t_ets(_Config) ->
+
+ Tid = ets:new(map_table,[]),
+
+ [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)],
+
+
+ [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }),
+
+ %% Test equal
+ [3,4] = lists:sort(
+ ets:select(Tid,[{{'$1','$2'},
+ [{'or',{'==','$1',#{ 3 => -3 }},
+ {'==','$1',#{ 4 => -4 }}}],
+ ['$2']}])),
+ %% Test match
+ [30,50] = lists:sort(
+ ets:select(Tid,
+ [{{#{ 30 => -30}, '$1'},[],['$1']},
+ {{#{ 50 => -50}, '$1'},[],['$1']}]
+ )),
+
+ ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}),
+
+ %% Test equal with map of different size
+ [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]),
+
+ %% Test match with map of different size
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),
+
+ %%% Test match with don't care value
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),
+
+ %% Test is_map bif
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{not_a_map,2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{{nope,a,tuple},2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+
+ %% Test map_size bif
+ [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}],
+ [{map_size,'$1'}]}]),
+
+ true = ets:delete(Tid,#{50 => -50}),
+ [] = ets:lookup(Tid,#{50 => -50}),
+
+ ets:delete(Tid),
+ ok.
+
+t_dets(_Config) ->
+ ok.
+
+getmsg(_Tracer) ->
+ receive V -> V after 100 -> timeout end.
+
+trace_collector(Msg,Parent) ->
+ io:format("~p~n",[Msg]),
+ Parent ! Msg,
+ Parent.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index a245e26a55..cd107599e9 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 3.2.12
+DEBUGGER_VSN = 4.0
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 2a631c3010..a92b890a80 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -229,7 +229,7 @@
match.</item>
<tag><c><![CDATA[-Wno_opaque]]></c></tag>
<item>Suppress warnings for violations of opaqueness of data types.</item>
- <tag><c><![CDATA[-Wno_behaviours]]></c>***</tag>
+ <tag><c><![CDATA[-Wno_behaviours]]></c></tag>
<item>Suppress warnings about behaviour callbacks which drift from the
published recommended interfaces.</item>
<tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index 9222a28a77..1756800c4f 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -29,17 +29,22 @@
dialyzer_cl_parse,
dialyzer_codeserver,
dialyzer_contracts,
+ dialyzer_coordinator,
dialyzer_dataflow,
dialyzer_dep,
dialyzer_explanation,
- dialyzer_gui,
dialyzer_gui_wx,
dialyzer_options,
dialyzer_plt,
dialyzer_races,
dialyzer_succ_typings,
dialyzer_typesig,
- dialyzer_utils]},
+ dialyzer_utils,
+ dialyzer_timing,
+ dialyzer_worker]},
{registered, []},
{applications, [compiler, gs, hipe, kernel, stdlib, wx]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","syntax_tools-1.6.14","stdlib-2.0",
+ "kernel-3.0","hipe-3.10.3","erts-6.0",
+ "compiler-5.0"]}]}.
diff --git a/lib/dialyzer/src/dialyzer.appup.src b/lib/dialyzer/src/dialyzer.appup.src
index 26d14ee8f4..1e293e407a 100644
--- a/lib/dialyzer/src/dialyzer.appup.src
+++ b/lib/dialyzer/src/dialyzer.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -15,6 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-
-{"%VSN%",[],[]}.
+{"%VSN%",
+ [{<<".*">>,[{restart_application, dialyzer}]}],
+ [{<<".*">>,[{restart_application, dialyzer}]}]
+}.
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index bb7e39dfda..cec94a49fd 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -172,7 +172,7 @@ run(Opts) ->
end,
case dialyzer_cl:start(OptsRecord) of
{?RET_DISCREPANCIES, Warnings} -> Warnings;
- {?RET_NOTHING_SUSPICIOUS, []} -> []
+ {?RET_NOTHING_SUSPICIOUS, _} -> []
end
catch
throw:{dialyzer_error, ErrorMsg} ->
@@ -447,7 +447,6 @@ message_to_string({opaque_size, [SizeType, Size]}) ->
message_to_string({opaque_call, [M, F, Args, Culprit, OpaqueType]}) ->
io_lib:format("The call ~s:~s~s breaks the opaqueness of the term ~s :: ~s\n",
[M, F, Args, Culprit, OpaqueType]);
-
%%----- Warnings for concurrency errors --------------------
message_to_string({race_condition, [M, F, Args, Reason]}) ->
io_lib:format("The call ~w:~w~s ~s\n", [M, F, Args, Reason]);
@@ -474,7 +473,14 @@ message_to_string({callback_missing, [B, F, A]}) ->
io_lib:format("Undefined callback function ~w/~w (behaviour '~w')\n",
[F, A, B]);
message_to_string({callback_info_missing, [B]}) ->
- io_lib:format("Callback info about the ~w behaviour is not available\n", [B]).
+ io_lib:format("Callback info about the ~w behaviour is not available\n", [B]);
+%%----- Warnings for unknown functions, types, and behaviours -------------
+message_to_string({unknown_type, {M, F, A}}) ->
+ io_lib:format("Unknown type ~w:~w/~w", [M, F, A]);
+message_to_string({unknown_function, {M, F, A}}) ->
+ io_lib:format("Unknown function ~w:~w/~w", [M, F, A]);
+message_to_string({unknown_behaviour, B}) ->
+ io_lib:format("Unknown behaviour ~w", [B]).
%%-----------------------------------------------------------------------------
%% Auxiliary functions below
@@ -557,4 +563,4 @@ form_position_string(ArgNs) ->
ordinal(1) -> "1st";
ordinal(2) -> "2nd";
ordinal(3) -> "3rd";
-ordinal(N) when is_integer(N) -> io_lib:format("~wth",[N]).
+ordinal(N) when is_integer(N) -> io_lib:format("~wth", [N]).
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 105a174e31..9a25f86512 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%%% Copyright Ericsson AB 2006-2014. 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
@@ -58,6 +58,7 @@
-define(WARN_RACE_CONDITION, warn_race_condition).
-define(WARN_BEHAVIOUR, warn_behaviour).
-define(WARN_UNDEFINED_CALLBACK, warn_undefined_callbacks).
+-define(WARN_UNKNOWN, warn_unknown).
%%
%% The following type has double role:
@@ -73,7 +74,7 @@
| ?WARN_CONTRACT_SUPERTYPE | ?WARN_CALLGRAPH
| ?WARN_UNMATCHED_RETURN | ?WARN_RACE_CONDITION
| ?WARN_BEHAVIOUR | ?WARN_CONTRACT_RANGE
- | ?WARN_UNDEFINED_CALLBACK.
+ | ?WARN_UNDEFINED_CALLBACK | ?WARN_UNKNOWN.
%%
%% This is the representation of each warning as they will be returned
@@ -88,12 +89,6 @@
-type dial_error() :: any(). %% XXX: underspecified
%%--------------------------------------------------------------------
-%% THIS TYPE SHOULD ONE DAY DISAPPEAR -- IT DOES NOT BELONG HERE
-%%--------------------------------------------------------------------
-
--type ordset(T) :: [T] . %% XXX: temporarily
-
-%%--------------------------------------------------------------------
%% Basic types used either in the record definitions below or in other
%% parts of the application
%%--------------------------------------------------------------------
@@ -143,7 +138,7 @@
init_plts = [] :: [file:filename()],
include_dirs = [] :: [file:filename()],
output_plt = none :: 'none' | file:filename(),
- legal_warnings = ordsets:new() :: ordset(dial_warn_tag()),
+ legal_warnings = ordsets:new() :: ordsets:ordset(dial_warn_tag()),
report_mode = normal :: rep_mode(),
erlang_mode = false :: boolean(),
use_contracts = true :: boolean(),
@@ -167,4 +162,4 @@
dialyzer_timing:end_stamp(Server),
Var
end).
--define(timing(Server, Msg, Expr),?timing(Server, Msg, _T, Expr)).
+-define(timing(Server, Msg, Expr), ?timing(Server, Msg, _T, Expr)).
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 0fbaf1d47c..6a33a2acb3 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -39,6 +39,8 @@
one_file_result/0,
compile_result/0]).
+-export_type([no_warn_unused/0]).
+
-include("dialyzer.hrl").
-record(analysis_state,
@@ -48,7 +50,7 @@
defines = [] :: [dial_define()],
doc_plt :: dialyzer_plt:plt(),
include_dirs = [] :: [file:filename()],
- no_warn_unused :: set(),
+ no_warn_unused :: no_warn_unused(),
parent :: pid(),
plt :: dialyzer_plt:plt(),
start_from = byte_code :: start_from(),
@@ -59,6 +61,8 @@
-record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}).
+-type no_warn_unused() :: sets:set(mfa()).
+
%%--------------------------------------------------------------------
%% Main
%%--------------------------------------------------------------------
@@ -168,7 +172,7 @@ analysis_start(Parent, Analysis) ->
throw:{error, _ErrorMsg} = Error -> exit(Error)
end,
NewPlt0 = dialyzer_plt:insert_types(Plt, dialyzer_codeserver:get_records(NewCServer)),
- ExpTypes = dialyzer_codeserver:get_exported_types(NewCServer),
+ ExpTypes = dialyzer_codeserver:get_exported_types(NewCServer),
NewPlt1 = dialyzer_plt:insert_exported_types(NewPlt0, ExpTypes),
State0 = State#analysis_state{plt = NewPlt1},
dump_callgraph(Callgraph, State0, Analysis),
@@ -245,7 +249,7 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
timing_server = Timing,
parent = Parent} = State) ->
send_log(Parent, "Reading files and computing callgraph... "),
- {T1, _} = statistics(runtime),
+ {T1, _} = statistics(wall_clock),
Callgraph = dialyzer_callgraph:new(),
CompileInit = make_compile_init(State, Callgraph),
{{Failed, NoWarn, Modules}, NextLabel} =
@@ -268,13 +272,13 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
[[Reason || {_Filename, Reason} <- Failed]]),
exit({error, Msg})
end,
- {T2, _} = statistics(runtime),
+ {T2, _} = statistics(wall_clock),
Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]),
send_log(Parent, Msg1),
Callgraph =
?timing(Timing, "clean", _C2,
cleanup_callgraph(State, CServer2, Callgraph, Modules)),
- {T3, _} = statistics(runtime),
+ {T3, _} = statistics(wall_clock),
Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]),
send_log(Parent, Msg2),
{Callgraph, sets:from_list(NoWarn), CServer2}.
@@ -423,11 +427,8 @@ abs_get_nowarn(Abs, M) ->
false ->
[{M, F, A} || {function, _, F, A, _} <- Abs]; % all functions
true ->
- OnLoad =
- lists:flatten([{M, F, A} || {attribute, _, on_load, {F, A}} <- Abs]),
- OnLoad ++ [{M, F, A} ||
- {nowarn_unused_function, FAs} <- Opts,
- {F, A} <- lists:flatten([FAs])]
+ [{M, F, A} || {nowarn_unused_function, FAs} <- Opts,
+ {F, A} <- lists:flatten([FAs])]
end.
get_exported_types_from_core(Core) ->
@@ -619,9 +620,9 @@ dump_callgraph(CallGraph, State, #analysis{callgraph_file = File} = Analysis) ->
Extension = filename:extension(File),
Start_Msg = io_lib:format("Dumping the callgraph... ", []),
send_log(State#analysis_state.parent, Start_Msg),
- {T1, _} = statistics(runtime),
+ {T1, _} = statistics(wall_clock),
dump_callgraph(CallGraph, State, Analysis, Extension),
- {T2, _} = statistics(runtime),
+ {T2, _} = statistics(wall_clock),
Finish_Msg = io_lib:format("done in ~2f secs\n", [(T2-T1)/1000]),
send_log(State#analysis_state.parent, Finish_Msg),
ok.
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 2b9a69ce77..1d458b49fc 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -40,15 +40,17 @@
-type behaviour() :: atom().
+-type rectab() :: erl_types:type_table().
+
-record(state, {plt :: dialyzer_plt:plt(),
codeserver :: dialyzer_codeserver:codeserver(),
filename :: file:filename(),
behlines :: [{behaviour(), non_neg_integer()}],
- records :: dict()}).
+ records :: rectab()}).
%%--------------------------------------------------------------------
--spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], dict(),
+-spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], rectab(),
dialyzer_plt:plt(),
dialyzer_codeserver:codeserver()) -> [dial_warning()].
diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl
index bc32110751..00b01fbd36 100644
--- a/lib/dialyzer/src/dialyzer_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_callgraph.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -64,14 +64,16 @@
put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2,
get_behaviour_api_calls/1, dispose_race_server/1, duplicate/1]).
--export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0]).
+-export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0, mod_deps/0]).
-include("dialyzer.hrl").
%%----------------------------------------------------------------------
-type scc() :: [mfa_or_funlbl()].
--type mfa_calls() :: [{mfa_or_funlbl(), mfa_or_funlbl()}].
+-type mfa_call() :: {mfa_or_funlbl(), mfa_or_funlbl()}.
+-type mfa_calls() :: [mfa_call()].
+-type mod_deps() :: dict:dict(module(), [module()]).
%%-----------------------------------------------------------------------------
%% A callgraph is a directed graph where the nodes are functions and a
@@ -93,7 +95,7 @@
%% whenever applicable.
%%-----------------------------------------------------------------------------
--record(callgraph, {digraph = digraph:new() :: digraph(),
+-record(callgraph, {digraph = digraph:new() :: digraph:graph(),
active_digraph :: active_digraph(),
esc :: ets:tid(),
letrec_map :: ets:tid(),
@@ -105,7 +107,7 @@
race_detection = false :: boolean(),
race_data_server = new_race_data_server() :: pid()}).
--record(race_data_state, {race_code = dict:new() :: dict(),
+-record(race_data_state, {race_code = dict:new() :: dict:dict(),
public_tables = [] :: [label()],
named_tables = [] :: [string()],
beh_api_calls = [] :: [{mfa(), mfa()}]}).
@@ -114,7 +116,7 @@
-type callgraph() :: #callgraph{}.
--type active_digraph() :: {'d', digraph()} | {'e', ets:tid(), ets:tid()}.
+-type active_digraph() :: {'d', digraph:graph()} | {'e', ets:tid(), ets:tid()}.
%%----------------------------------------------------------------------
@@ -222,7 +224,10 @@ non_local_calls(#callgraph{digraph = DG}) ->
Edges = digraph_edges(DG),
find_non_local_calls(Edges, sets:new()).
--spec find_non_local_calls([{mfa_or_funlbl(), mfa_or_funlbl()}], set()) -> mfa_calls().
+-type call_tab() :: sets:set(mfa_call()).
+
+-spec find_non_local_calls([{mfa_or_funlbl(), mfa_or_funlbl()}], call_tab()) ->
+ mfa_calls().
find_non_local_calls([{{M,_,_}, {M,_,_}}|Left], Set) ->
find_non_local_calls(Left, Set);
@@ -267,7 +272,7 @@ get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) ->
modules(#callgraph{digraph = DG}) ->
ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]).
--spec module_postorder(callgraph()) -> {[module()], {'d', digraph()}}.
+-spec module_postorder(callgraph()) -> {[module()], {'d', digraph:graph()}}.
module_postorder(#callgraph{digraph = DG}) ->
Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)),
@@ -287,7 +292,7 @@ edge_fold(_, Set) -> Set.
%% The module deps of a module are modules that depend on the module
--spec module_deps(callgraph()) -> dict().
+-spec module_deps(callgraph()) -> mod_deps().
module_deps(#callgraph{digraph = DG}) ->
Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)),
@@ -301,7 +306,7 @@ module_deps(#callgraph{digraph = DG}) ->
digraph_delete(MDG),
dict:from_list(Deps).
--spec strip_module_deps(dict(), set()) -> dict().
+-spec strip_module_deps(mod_deps(), sets:set(module())) -> mod_deps().
strip_module_deps(ModDeps, StripSet) ->
FilterFun1 = fun(Val) -> not sets:is_element(Val, StripSet) end,
@@ -575,7 +580,7 @@ digraph_reaching_subgraph(Funs, DG) ->
%% Races
%%----------------------------------------------------------------------
--spec renew_race_info(callgraph(), dict(), [label()], [string()]) ->
+-spec renew_race_info(callgraph(), dict:dict(), [label()], [string()]) ->
callgraph().
renew_race_info(#callgraph{race_data_server = RaceDataServer} = CG,
@@ -641,7 +646,7 @@ duplicate(#callgraph{race_data_server = RaceDataServer} = Callgraph) ->
dispose_race_server(#callgraph{race_data_server = RaceDataServer}) ->
race_data_server_cast(stop, RaceDataServer).
--spec get_digraph(callgraph()) -> digraph().
+-spec get_digraph(callgraph()) -> digraph:graph().
get_digraph(#callgraph{digraph = Digraph}) ->
Digraph.
@@ -656,7 +661,7 @@ get_named_tables(#callgraph{race_data_server = RaceDataServer}) ->
get_public_tables(#callgraph{race_data_server = RaceDataServer}) ->
race_data_server_call(get_public_tables, RaceDataServer).
--spec get_race_code(callgraph()) -> dict().
+-spec get_race_code(callgraph()) -> dict:dict().
get_race_code(#callgraph{race_data_server = RaceDataServer}) ->
race_data_server_call(get_race_code, RaceDataServer).
@@ -677,12 +682,12 @@ race_code_new(#callgraph{race_data_server = RaceDataServer} = CG) ->
ok = race_data_server_cast(race_code_new, RaceDataServer),
CG.
--spec put_digraph(digraph(), callgraph()) -> callgraph().
+-spec put_digraph(digraph:graph(), callgraph()) -> callgraph().
put_digraph(Digraph, Callgraph) ->
Callgraph#callgraph{digraph = Digraph}.
--spec put_race_code(dict(), callgraph()) -> callgraph().
+-spec put_race_code(dict:dict(), callgraph()) -> callgraph().
put_race_code(RaceCode, #callgraph{race_data_server = RaceDataServer} = CG) ->
ok = race_data_server_cast({put_race_code, RaceCode}, RaceDataServer),
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index cda801bf6c..3e7d9dfa99 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -40,7 +40,7 @@
external_calls = [] :: [mfa()],
external_types = [] :: [mfa()],
legal_warnings = ordsets:new() :: [dial_warn_tag()],
- mod_deps = dict:new() :: dict(),
+ mod_deps = dict:new() :: dialyzer_callgraph:mod_deps(),
output = standard_io :: io:device(),
output_format = formatted :: format(),
filename_opt = basename :: fopt(),
@@ -504,7 +504,7 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
_ ->
Mods = [lists, dict, digraph, digraph_utils, ets,
gb_sets, gb_trees, ordsets, sets, sofs,
- cerl, cerl_trees, erl_types, erl_bif_types,
+ cerl, erl_types, cerl_trees, erl_bif_types,
dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
dialyzer_codeserver, dialyzer_contracts,
dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
@@ -533,7 +533,7 @@ hc(Mod) ->
case code:is_module_native(Mod) of
true -> ok;
false ->
- %% io:format(" ~s", [Mod]),
+ %% io:format(" ~w", [Mod]),
{ok, Mod} = hipe:c(Mod),
ok
end.
@@ -656,7 +656,8 @@ return_value(State = #cl_state{erlang_mode = ErlangMode,
mod_deps = ModDeps,
output_plt = OutputPlt,
plt_info = PltInfo,
- stored_warnings = StoredWarnings},
+ stored_warnings = StoredWarnings,
+ legal_warnings = LegalWarnings},
Plt) ->
case OutputPlt =:= none of
true -> ok;
@@ -676,16 +677,33 @@ return_value(State = #cl_state{erlang_mode = ErlangMode,
maybe_close_output_file(State),
{RetValue, []};
true ->
- {RetValue, process_warnings(StoredWarnings)}
+ Unknown =
+ case ordsets:is_element(?WARN_UNKNOWN, LegalWarnings) of
+ true ->
+ unknown_functions(State) ++
+ unknown_types(State) ++
+ unknown_behaviours(State);
+ false -> []
+ end,
+ UnknownWarnings =
+ [{?WARN_UNKNOWN, {_Filename = "", _Line = 0}, W} || W <- Unknown],
+ AllWarnings =
+ UnknownWarnings ++ process_warnings(StoredWarnings),
+ {RetValue, AllWarnings}
end.
+unknown_functions(#cl_state{external_calls = Calls}) ->
+ [{unknown_function, MFA} || MFA <- Calls].
+
print_ext_calls(#cl_state{report_mode = quiet}) ->
ok;
print_ext_calls(#cl_state{output = Output,
external_calls = Calls,
stored_warnings = Warnings,
- output_format = Format}) ->
- case Calls =:= [] of
+ output_format = Format,
+ legal_warnings = LegalWarnings}) ->
+ case not ordsets:is_element(?WARN_UNKNOWN, LegalWarnings)
+ orelse Calls =:= [] of
true -> ok;
false ->
case Warnings =:= [] of
@@ -708,14 +726,19 @@ do_print_ext_calls(Output, [{M,F,A}|T], Before) ->
do_print_ext_calls(_, [], _) ->
ok.
+unknown_types(#cl_state{external_types = Types}) ->
+ [{unknown_type, MFA} || MFA <- Types].
+
print_ext_types(#cl_state{report_mode = quiet}) ->
ok;
print_ext_types(#cl_state{output = Output,
external_calls = Calls,
external_types = Types,
stored_warnings = Warnings,
- output_format = Format}) ->
- case Types =:= [] of
+ output_format = Format,
+ legal_warnings = LegalWarnings}) ->
+ case not ordsets:is_element(?WARN_UNKNOWN, LegalWarnings)
+ orelse Types =:= [] of
true -> ok;
false ->
case Warnings =:= [] andalso Calls =:= [] of
@@ -738,6 +761,15 @@ do_print_ext_types(Output, [{M,F,A}|T], Before) ->
do_print_ext_types(_, [], _) ->
ok.
+unknown_behaviours(#cl_state{unknown_behaviours = DupBehaviours,
+ legal_warnings = LegalWarnings}) ->
+ case ordsets:is_element(?WARN_BEHAVIOUR, LegalWarnings) of
+ false -> [];
+ true ->
+ Behaviours = lists:usort(DupBehaviours),
+ [{unknown_behaviour, B} || B <- Behaviours]
+ end.
+
%%print_unknown_behaviours(#cl_state{report_mode = quiet}) ->
%% ok;
print_unknown_behaviours(#cl_state{output = Output,
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 216e06219c..aab3d6add6 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -64,6 +64,12 @@
-type dict_ets() :: ets:tid().
-type set_ets() :: ets:tid().
+-type types() :: erl_types:type_table().
+-type mod_records() :: dict:dict(module(), types()).
+
+-type contracts() :: dict:dict(mfa(),dialyzer_contracts:file_contract()).
+-type mod_contracts() :: dict:dict(module(), contracts()).
+
-record(codeserver, {next_core_label = 0 :: label(),
code :: dict_ets(),
exported_types :: set_ets(), % set(mfa())
@@ -160,12 +166,12 @@ insert(Mod, ModCode, CS) ->
true = ets:insert(CS#codeserver.code, [ModEntry|Funs]),
CS.
--spec get_temp_exported_types(codeserver()) -> set().
+-spec get_temp_exported_types(codeserver()) -> sets:set(mfa()).
get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) ->
ets_set_to_set(TempExpTypes).
--spec insert_temp_exported_types(set(), codeserver()) -> codeserver().
+-spec insert_temp_exported_types(sets:set(mfa()), codeserver()) -> codeserver().
insert_temp_exported_types(Set, CS) ->
TempExportedTypes = CS#codeserver.temp_exported_types,
@@ -183,17 +189,17 @@ insert_exports(List, #codeserver{exports = Exports} = CS) ->
is_exported(MFA, #codeserver{exports = Exports}) ->
ets_set_is_element(MFA, Exports).
--spec get_exported_types(codeserver()) -> set(). % set(mfa())
+-spec get_exported_types(codeserver()) -> sets:set(mfa()).
get_exported_types(#codeserver{exported_types = ExpTypes}) ->
ets_set_to_set(ExpTypes).
--spec get_exports(codeserver()) -> set(). % set(mfa())
+-spec get_exports(codeserver()) -> sets:set(mfa()).
get_exports(#codeserver{exports = Exports}) ->
ets_set_to_set(Exports).
--spec finalize_exported_types(set(), codeserver()) -> codeserver().
+-spec finalize_exported_types(sets:set(mfa()), codeserver()) -> codeserver().
finalize_exported_types(Set, CS) ->
ExportedTypes = ets_read_concurrent_table(dialyzer_codeserver_exported_types),
@@ -222,7 +228,7 @@ get_next_core_label(#codeserver{next_core_label = NCL}) ->
set_next_core_label(NCL, CS) ->
CS#codeserver{next_core_label = NCL}.
--spec lookup_mod_records(atom(), codeserver()) -> dict().
+-spec lookup_mod_records(atom(), codeserver()) -> types().
lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) ->
case ets_dict_find(Mod, RecDict) of
@@ -230,12 +236,12 @@ lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) ->
{ok, Dict} -> Dict
end.
--spec get_records(codeserver()) -> dict().
+-spec get_records(codeserver()) -> mod_records().
get_records(#codeserver{records = RecDict}) ->
ets_dict_to_dict(RecDict).
--spec store_temp_records(atom(), dict(), codeserver()) -> codeserver().
+-spec store_temp_records(module(), types(), codeserver()) -> codeserver().
store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS)
when is_atom(Mod) ->
@@ -244,12 +250,12 @@ store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS)
false -> CS#codeserver{temp_records = ets_dict_store(Mod, Dict, TempRecDict)}
end.
--spec get_temp_records(codeserver()) -> dict().
+-spec get_temp_records(codeserver()) -> mod_records().
get_temp_records(#codeserver{temp_records = TempRecDict}) ->
ets_dict_to_dict(TempRecDict).
--spec set_temp_records(dict(), codeserver()) -> codeserver().
+-spec set_temp_records(mod_records(), codeserver()) -> codeserver().
set_temp_records(Dict, CS) ->
true = ets:delete(CS#codeserver.temp_records),
@@ -257,7 +263,7 @@ set_temp_records(Dict, CS) ->
true = ets_dict_store_dict(Dict, TempRecords),
CS#codeserver{temp_records = TempRecords}.
--spec finalize_records(dict(), codeserver()) -> codeserver().
+-spec finalize_records(mod_records(), codeserver()) -> codeserver().
finalize_records(Dict, CS) ->
true = ets:delete(CS#codeserver.temp_records),
@@ -265,7 +271,7 @@ finalize_records(Dict, CS) ->
true = ets_dict_store_dict(Dict, Records),
CS#codeserver{records = Records, temp_records = clean}.
--spec lookup_mod_contracts(atom(), codeserver()) -> dict().
+-spec lookup_mod_contracts(atom(), codeserver()) -> contracts().
lookup_mod_contracts(Mod, #codeserver{contracts = ContDict})
when is_atom(Mod) ->
@@ -284,7 +290,7 @@ get_contract_pair(Key, ContDict) ->
lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) ->
ets_dict_find(MFA, ContDict).
--spec get_contracts(codeserver()) -> dict().
+-spec get_contracts(codeserver()) -> mod_contracts().
get_contracts(#codeserver{contracts = ContDict}) ->
ets_dict_to_dict(ContDict).
@@ -294,7 +300,7 @@ get_contracts(#codeserver{contracts = ContDict}) ->
get_callbacks(#codeserver{callbacks = CallbDict}) ->
ets:tab2list(CallbDict).
--spec store_temp_contracts(atom(), dict(), dict(), codeserver()) ->
+-spec store_temp_contracts(module(), contracts(), contracts(), codeserver()) ->
codeserver().
store_temp_contracts(Mod, SpecDict, CallbackDict,
@@ -313,13 +319,14 @@ store_temp_contracts(Mod, SpecDict, CallbackDict,
CS1#codeserver{temp_callbacks = ets_dict_store(Mod, CallbackDict, Cb)}
end.
--spec get_temp_contracts(codeserver()) -> {dict(), dict()}.
+-spec get_temp_contracts(codeserver()) -> {mod_contracts(), mod_contracts()}.
get_temp_contracts(#codeserver{temp_contracts = TempContDict,
temp_callbacks = TempCallDict}) ->
{ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}.
--spec finalize_contracts(dict(), dict(), codeserver()) -> codeserver().
+-spec finalize_contracts(mod_contracts(), mod_contracts(), codeserver()) ->
+ codeserver().
finalize_contracts(SpecDict, CallbackDict, CS) ->
Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts),
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 3467ab4e65..283031eb9a 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -20,6 +20,8 @@
-module(dialyzer_contracts).
+-compile(export_all).
+
-export([check_contract/2,
check_contracts/4,
contracts_without_fun/3,
@@ -52,7 +54,7 @@
%% to expand records and/or remote types that they might contain.
%%-----------------------------------------------------------------------
--type tmp_contract_fun() :: fun((set(), dict()) -> contract_pair()).
+-type tmp_contract_fun() :: fun((sets:set(mfa()), types()) -> contract_pair()).
-record(tmp_contract, {contract_funs = [] :: [tmp_contract_fun()],
forms = [] :: [{_, _}]}).
@@ -163,8 +165,10 @@ process_contract_remote_types(CodeServer) ->
-type opaques() :: [erl_types:erl_type()] | 'universe'.
-type opaques_fun() :: fun((module()) -> opaques()).
+-type fun_types() :: dict:dict(label(), erl_types:type_table()).
+
-spec check_contracts([{mfa(), file_contract()}],
- dialyzer_callgraph:callgraph(), dict(),
+ dialyzer_callgraph:callgraph(), fun_types(),
opaques_fun()) -> plt_contracts().
check_contracts(Contracts, Callgraph, FunTypes, FindOpaques) ->
@@ -292,7 +296,8 @@ is_not_nil_list(Type) ->
erl_types:t_is_list(Type) andalso not erl_types:t_is_nil(Type).
%% This is the heart of the "range function"
--spec process_contracts([contract_pair()], [erl_types:erl_type()]) -> erl_types:erl_type().
+-spec process_contracts([contract_pair()], [erl_types:erl_type()]) ->
+ erl_types:erl_type().
process_contracts(OverContracts, Args) ->
process_contracts(OverContracts, Args, erl_types:t_none()).
@@ -307,7 +312,8 @@ process_contracts([OverContract|Left], Args, AccRange) ->
process_contracts([], _Args, AccRange) ->
AccRange.
--spec process_contract(contract_pair(), [erl_types:erl_type()]) -> 'error' | {'ok', erl_types:erl_type()}.
+-spec process_contract(contract_pair(), [erl_types:erl_type()]) ->
+ 'error' | {'ok', erl_types:erl_type()}.
process_contract({Contract, Constraints}, CallTypes0) ->
CallTypesFun = erl_types:t_fun(CallTypes0, erl_types:t_any()),
@@ -343,8 +349,11 @@ solve_constraints(Contract, Call, Constraints) ->
%% ?debug("Inf: ~s\n", [erl_types:t_to_string(Inf)]),
%% erl_types:t_assign_variables_to_subtype(Contract, Inf).
+-type contracts() :: dict:dict(mfa(),dialyzer_contracts:file_contract()).
+
%% Checks the contracts for functions that are not implemented
--spec contracts_without_fun(dict(), [_], dialyzer_callgraph:callgraph()) -> [dial_warning()].
+-spec contracts_without_fun(contracts(), [_], dialyzer_callgraph:callgraph()) ->
+ [dial_warning()].
contracts_without_fun(Contracts, AllFuns0, Callgraph) ->
AllFuns1 = [{dialyzer_callgraph:lookup_name(Label, Callgraph), Arity}
@@ -377,7 +386,10 @@ insert_constraints([{subtype, Type1, Type2}|Left], Dict) ->
end;
insert_constraints([], Dict) -> Dict.
--spec store_tmp_contract(mfa(), file_line(), [_], dict(), dict()) -> dict().
+-type types() :: erl_types:type_table().
+
+-spec store_tmp_contract(mfa(), file_line(), [_], contracts(), types()) ->
+ contracts().
store_tmp_contract(MFA, FileLine, TypeSpec, SpecDict, RecordsDict) ->
%% io:format("contract from form: ~p\n", [TypeSpec]),
@@ -404,7 +416,8 @@ contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], RecDict,
throw({error, NewMsg})
end,
NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords),
- {NewType, []}
+ NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType),
+ {NewTypeNoVars, []}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, []} | FormAcc],
@@ -418,7 +431,8 @@ contract_from_form([{type, _L1, bounded_fun,
process_constraints(Constr, RecDict, ExpTypes, AllRecords),
Type = erl_types:t_from_form(Form, RecDict, VarDict),
NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords),
- {NewType, Constr1}
+ NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType),
+ {NewTypeNoVars, Constr1}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, Constr} | FormAcc],
@@ -427,7 +441,8 @@ contract_from_form([], _RecDict, _FileLine, TypeAcc, FormAcc) ->
{lists:reverse(TypeAcc), lists:reverse(FormAcc)}.
process_constraints(Constrs, RecDict, ExpTypes, AllRecords) ->
- Init = initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords),
+ Init0 = initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords),
+ Init = remove_cycles(Init0),
constraints_fixpoint(Init, RecDict, ExpTypes, AllRecords).
initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords) ->
@@ -467,12 +482,9 @@ constraints_fixpoint(OldVarDict, Constrs, RecDict, ExpTypes, AllRecords) ->
constraints_fixpoint(NewVarDict, Constrs, RecDict, ExpTypes, AllRecords)
end.
--define(TYPE_LIMIT, 4).
-
final_form(Form, RecDict, ExpTypes, AllRecords, VarDict) ->
T1 = erl_types:t_from_form(Form, RecDict, VarDict),
- T2 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords),
- erl_types:t_limit(T2, ?TYPE_LIMIT).
+ erl_types:t_solve_remote(T1, ExpTypes, AllRecords).
constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, VarDict) ->
Subtypes =
@@ -487,6 +499,74 @@ constraints_to_subs([C|Rest], RecDict, ExpTypes, AllRecords, VarDict, Acc) ->
NewAcc = [{subtype, T1, T2}|Acc],
constraints_to_subs(Rest, RecDict, ExpTypes, AllRecords, VarDict, NewAcc).
+%% Replaces variables with '_' when necessary to break up cycles among
+%% the constraints.
+
+remove_cycles(Constrs0) ->
+ Uses = find_uses(Constrs0),
+ G = digraph:new(),
+ Vs0 = [V || {V, _} <- Uses] ++ [V || {_, V} <- Uses],
+ Vs = lists:usort(Vs0),
+ lists:foreach(fun(V) -> _ = digraph:add_vertex(G, V) end, Vs),
+ lists:foreach(fun({From, To}) ->
+ _ = digraph:add_edge(G, {From, To}, From, To, [])
+ end, Uses),
+ ok = remove_cycles(G, Vs),
+ ToRemove = ordsets:subtract(ordsets:from_list(Uses),
+ ordsets:from_list(digraph:edges(G))),
+ Constrs = remove_uses(ToRemove, Constrs0),
+ digraph:delete(G),
+ Constrs.
+
+find_uses([{Var, Form}|Constrs]) ->
+ UsedVars = form_vars(Form, []),
+ VarName = erl_types:t_var_name(Var),
+ [{VarName, UsedVar} || UsedVar <- UsedVars] ++ find_uses(Constrs);
+find_uses([]) ->
+ [].
+
+form_vars({var, _, '_'}, Vs) -> Vs;
+form_vars({var, _, V}, Vs) -> [V|Vs];
+form_vars(T, Vs) when is_tuple(T) ->
+ form_vars(tuple_to_list(T), Vs);
+form_vars([E|Es], Vs) ->
+ form_vars(Es, form_vars(E, Vs));
+form_vars(_, Vs) -> Vs.
+
+remove_cycles(G, Vs) ->
+ NumberOfEdges = digraph:no_edges(G),
+ lists:foreach(fun(V) ->
+ case digraph:get_cycle(G, V) of
+ false -> true;
+ [V] -> digraph:del_edge(G, {V, V});
+ [V, V1|_] -> digraph:del_edge(G, {V, V1})
+ end
+ end, Vs),
+ case digraph:no_edges(G) =:= NumberOfEdges of
+ true -> ok;
+ false -> remove_cycles(G, Vs)
+ end.
+
+remove_uses([], Constrs) -> Constrs;
+remove_uses([{Var, Use}|ToRemove], Constrs0) ->
+ Constrs = remove_uses(Var, Use, Constrs0),
+ remove_uses(ToRemove, Constrs).
+
+remove_uses(_Var, _Use, []) -> [];
+remove_uses(Var, Use, [Constr|Constrs]) ->
+ {V, Form} = Constr,
+ case erl_types:t_var_name(V) =:= Var of
+ true -> [{V, remove_use(Form, Use)}|Constrs];
+ false -> [Constr|remove_uses(Var, Use, Constrs)]
+ end.
+
+remove_use({var, L, V}, V) -> {var, L, '_'};
+remove_use(T, V) when is_tuple(T) ->
+ list_to_tuple(remove_use(tuple_to_list(T), V));
+remove_use([E|Es], V) ->
+ [remove_use(E, V)|remove_use(Es, V)];
+remove_use(T, _V) -> T.
+
%% Gets the most general domain of a list of domains of all
%% the overloaded contracts
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 33fa107019..e0873b17f8 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -58,10 +58,12 @@
t_fun_range/2, t_integer/0, t_integers/1,
t_is_any/1, t_is_atom/1, t_is_atom/2, t_is_any_atom/3,
t_is_boolean/2,
- t_is_integer/2, t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
+ t_is_integer/2, t_is_list/1,
+ t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
t_is_number/2, t_is_reference/2, t_is_pid/2, t_is_port/2,
t_is_unit/1,
- t_limit/2, t_list/0, t_maybe_improper_list/0, t_module/0,
+ t_limit/2, t_list/0, t_list_elements/2,
+ t_maybe_improper_list/0, t_module/0,
t_none/0, t_non_neg_integer/0, t_number/0, t_number_vals/2,
t_pid/0, t_port/0, t_product/1, t_reference/0,
t_to_string/2, t_to_tlist/1,
@@ -84,39 +86,53 @@
%%--------------------------------------------------------------------
+-type type() :: erl_types:erl_type().
+-type types() :: erl_types:type_table().
+
-define(no_arg, no_arg).
-define(TYPE_LIMIT, 3).
-record(state, {callgraph :: dialyzer_callgraph:callgraph(),
- envs :: dict(),
- fun_tab :: dict(),
+ envs :: env_tab(),
+ fun_tab :: fun_tab(),
plt :: dialyzer_plt:plt(),
- opaques :: [erl_types:erl_type()],
+ opaques :: [type()],
races = dialyzer_races:new() :: dialyzer_races:races(),
- records = dict:new() :: dict(),
- tree_map :: dict(),
+ records = dict:new() :: types(),
+ tree_map :: dict:dict(label(), cerl:cerl()),
warning_mode = false :: boolean(),
warnings = [] :: [dial_warning()],
- work :: {[_], [_], set()},
+ work :: {[_], [_], sets:set()},
module :: module()
}).
--record(map, {dict = dict:new() :: dict(),
- subst = dict:new() :: dict(),
+-record(map, {dict = dict:new() :: type_tab(),
+ subst = dict:new() :: subst_tab(),
modified = [] :: [Key :: term()],
modified_stack = [] :: [{[Key :: term()],reference()}],
ref = undefined :: reference() | undefined}).
+-type nowarn() :: dialyzer_analysis_callgraph:no_warn_unused().
+-type env_tab() :: dict:dict(label(), #map{}).
+-type fun_entry() :: {Args :: [type()], RetType :: type()}.
+-type fun_tab() :: dict:dict('top' | label(),
+ {'not_handled', fun_entry()} | fun_entry()).
+-type key() :: label() | cerl:cerl().
+-type type_tab() :: dict:dict(key(), type()).
+-type subst_tab() :: dict:dict(key(), cerl:cerl()).
+
%% Exported Types
-opaque state() :: #state{}.
%%--------------------------------------------------------------------
+-type fun_types() :: dict:dict(label(), type()).
+
-spec get_warnings(cerl:c_module(), dialyzer_plt:plt(),
- dialyzer_callgraph:callgraph(), dict(), set()) ->
- {[dial_warning()], dict()}.
+ dialyzer_callgraph:callgraph(), types(), nowarn()) ->
+ {[dial_warning()], fun_types()}.
get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) ->
State1 = analyze_module(Tree, Plt, Callgraph, Records, true),
@@ -127,7 +143,8 @@ get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) ->
{State4#state.warnings, state__all_fun_types(State4)}.
-spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(),
- dialyzer_callgraph:callgraph(), dict()) -> dict().
+ dialyzer_callgraph:callgraph(),
+ types()) -> fun_types().
get_fun_types(Tree, Plt, Callgraph, Records) ->
State = analyze_module(Tree, Plt, Callgraph, Records, false),
@@ -293,6 +310,7 @@ traverse(Tree, Map, State) ->
t_is_any(ArgType)
orelse t_is_simple(ArgType, State)
orelse is_call_to_send(Arg)
+ orelse is_lc_simple_list(Arg, ArgType, State)
of
true -> % do not warn in these cases
State1;
@@ -345,20 +363,24 @@ traverse_list([], Map, State, Acc) ->
handle_apply(Tree, Map, State) ->
Args = cerl:apply_args(Tree),
Op = cerl:apply_op(Tree),
- {State1, Map1, ArgTypes} = traverse_list(Args, Map, State),
- {State2, Map2, OpType} = traverse(Op, Map1, State1),
+ {State0, Map1, ArgTypes} = traverse_list(Args, Map, State),
+ {State1, Map2, OpType} = traverse(Op, Map1, State0),
case any_none(ArgTypes) of
true ->
- {State2, Map2, t_none()};
+ {State1, Map2, t_none()};
false ->
- {CallSitesKnown, FunList} =
- case state__lookup_call_site(Tree, State2) of
- error -> {false, []};
- {ok, [external]} -> {false, []};
- {ok, List} -> {true, List}
+ FunList =
+ case state__lookup_call_site(Tree, State) of
+ error -> [external]; %% so that we go directly in the fallback
+ {ok, List} -> List
end,
- case CallSitesKnown of
- false ->
+ FunInfoList = [{local, state__fun_info(Fun, State)} || Fun <- FunList],
+ case
+ handle_apply_or_call(FunInfoList, Args, ArgTypes, Map2, Tree, State1)
+ of
+ {had_external, State2} ->
+ %% Fallback: use whatever info we collected from traversing the op
+ %% instead of the result that has been generalized to t_any().
Arity = length(Args),
OpType1 = t_inf(OpType, t_fun(Arity, t_any())),
case t_is_none(OpType1) of
@@ -390,25 +412,23 @@ handle_apply(Tree, Map, State) ->
{State2, enter_type(Op, OpType1, Map3), Range}
end
end;
- true ->
- FunInfoList = [{local, state__fun_info(Fun, State)}
- || Fun <- FunList],
- handle_apply_or_call(FunInfoList, Args, ArgTypes, Map2, Tree, State1)
+ Normal -> Normal
end
end.
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State) ->
None = t_none(),
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State,
- [None || _ <- ArgTypes], None).
+ [None || _ <- ArgTypes], None, false).
handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State,
- _AccArgTypes, _AccRet) ->
+ _AccArgTypes, _AccRet, _HadExternal) ->
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State,
- ArgTypes, t_any());
+ ArgTypes, t_any(), true);
handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
Args, ArgTypes, Map, Tree,
- #state{opaques = Opaques} = State, AccArgTypes, AccRet) ->
+ #state{opaques = Opaques} = State,
+ AccArgTypes, AccRet, HadExternal) ->
Any = t_any(),
AnyArgs = [Any || _ <- Args],
GenSig = {AnyArgs, fun(_) -> t_any() end},
@@ -555,11 +575,16 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
NewAccRet = t_sup(AccRet, TotalRet),
?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]),
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
- State3, NewAccArgTypes, NewAccRet);
+ State3, NewAccArgTypes, NewAccRet, HadExternal);
handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
- AccArgTypes, AccRet) ->
- NewMap = enter_type_lists(Args, AccArgTypes, Map),
- {State, NewMap, AccRet}.
+ AccArgTypes, AccRet, HadExternal) ->
+ case HadExternal of
+ false ->
+ NewMap = enter_type_lists(Args, AccArgTypes, Map),
+ {State, NewMap, AccRet};
+ true ->
+ {had_external, State}
+ end.
apply_fail_reason(FailedSig, FailedBif, FailedContract) ->
if
@@ -2296,12 +2321,15 @@ bind_guard_list([], Map, _Env, _Eval, _State, Acc) ->
-type eval() :: 'pos' | 'neg' | 'dont_know'.
--spec signal_guard_fail(eval(), cerl:c_call(), [erl_types:erl_type()],
+-spec signal_guard_fail(eval(), cerl:c_call(), [type()],
state()) -> no_return().
signal_guard_fail(Eval, Guard, ArgTypes, State) ->
signal_guard_failure(Eval, Guard, ArgTypes, fail, State).
+-spec signal_guard_fatal_fail(eval(), cerl:c_call(), [erl_types:erl_type()],
+ state()) -> no_return().
+
signal_guard_fatal_fail(Eval, Guard, ArgTypes, State) ->
signal_guard_failure(Eval, Guard, ArgTypes, fatal_fail, State).
@@ -2710,6 +2738,13 @@ is_call_to_send(Tree) ->
andalso (Arity =:= 2)
end.
+is_lc_simple_list(Tree, TreeType, State) ->
+ Opaques = State#state.opaques,
+ Ann = cerl:get_ann(Tree),
+ lists:member(list_comprehension, Ann)
+ andalso t_is_list(TreeType)
+ andalso t_is_simple(t_list_elements(TreeType, Opaques), State).
+
filter_match_fail([Clause] = Cls) ->
Body = cerl:clause_body(Clause),
case cerl:type(Body) of
@@ -3148,7 +3183,7 @@ state__get_callgraph(#state{callgraph = Callgraph}) ->
state__get_races(#state{races = Races}) ->
Races.
--spec state__get_records(state()) -> dict().
+-spec state__get_records(state()) -> types().
state__get_records(#state{records = Records}) ->
Records.
@@ -3247,7 +3282,7 @@ get_file([_|Tail]) -> get_file(Tail).
is_compiler_generated(Ann) ->
lists:member(compiler_generated, Ann) orelse (get_line(Ann) < 1).
--spec format_args([cerl:cerl()], [erl_types:erl_type()], state()) ->
+-spec format_args([cerl:cerl()], [type()], state()) ->
nonempty_string().
format_args([], [], _State) ->
@@ -3282,17 +3317,17 @@ format_arg(Arg) ->
Default
end.
--spec format_type(erl_types:erl_type(), state()) -> string().
+-spec format_type(type(), state()) -> string().
format_type(Type, #state{records = R}) ->
t_to_string(Type, R).
--spec format_field_diffs(erl_types:erl_type(), state()) -> string().
+-spec format_field_diffs(type(), state()) -> string().
format_field_diffs(RecConstruction, #state{records = R}) ->
erl_types:record_field_diffs_to_string(RecConstruction, R).
--spec format_sig_args(erl_types:erl_type(), state()) -> string().
+-spec format_sig_args(type(), state()) -> string().
format_sig_args(Type, #state{opaques = Opaques} = State) ->
SigArgs = t_fun_args(Type, Opaques),
diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl
index a81ea1a98b..572e60278d 100644
--- a/lib/dialyzer/src/dialyzer_dep.erl
+++ b/lib/dialyzer/src/dialyzer_dep.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -55,17 +55,17 @@
%%
%% Letrecs = a dict mapping var labels to their recursive definition.
%% top-level letrecs are not included as they are handled
-%% separatedly.
+%% separately.
%%
-spec analyze(cerl:c_module()) ->
- {dict(), ordset('external' | label()), dict(), dict()}.
+ {dict:dict(), ordsets:ordset('external' | label()), dict:dict(), dict:dict()}.
analyze(Tree) ->
%% io:format("Handling ~w\n", [cerl:atom_val(cerl:module_name(Tree))]),
{_, State} = traverse(Tree, map__new(), state__new(Tree), top),
Esc = state__esc(State),
- %% Add dependency from 'external' to all escaping function
+ %% Add dependency from 'external' to all escaping functions
State1 = state__add_deps(external, output(Esc), State),
Deps = state__deps(State1),
Calls = state__calls(State1),
@@ -124,8 +124,10 @@ traverse(Tree, Out, State, CurrentFun) ->
TmpState = state__add_deps(Label, O1, State),
state__add_deps(CurrentFun, O2,TmpState)
end,
- {BodyFuns, State2} = traverse(Body, Out, State1,
- cerl_trees:get_label(Tree)),
+ Vars = cerl:fun_vars(Tree),
+ Out1 = bind_single(Vars, output(set__singleton(external)), Out),
+ {BodyFuns, State2} =
+ traverse(Body, Out1, State1, cerl_trees:get_label(Tree)),
{output(set__singleton(Label)), state__add_esc(BodyFuns, State2)};
'let' ->
Vars = cerl:let_vars(Tree),
@@ -309,7 +311,7 @@ primop(Tree, ArgFuns, State) ->
%% Set
%%
--record(set, {set :: set()}).
+-record(set, {set :: sets:set()}).
set__singleton(Val) ->
#set{set = sets:add_element(Val, sets:new())}.
@@ -478,19 +480,30 @@ all_vars(Tree, AccIn) ->
-type local_set() :: 'none' | #set{}.
--record(state, {deps :: dict(),
+-record(state, {deps :: dict:dict(),
esc :: local_set(),
- call :: dict(),
- arities :: dict(),
- letrecs :: dict()}).
+ call :: dict:dict(),
+ arities :: dict:dict(),
+ letrecs :: dict:dict()}).
state__new(Tree) ->
Exports = set__from_list([X || X <- cerl:module_exports(Tree)]),
- InitEsc = set__from_list([cerl_trees:get_label(Fun)
- || {Var, Fun} <- cerl:module_defs(Tree),
- set__is_element(Var, Exports)]),
+ %% get the labels of all exported functions
+ ExpLs = [cerl_trees:get_label(Fun) || {Var, Fun} <- cerl:module_defs(Tree),
+ set__is_element(Var, Exports)],
+ %% make sure to also initiate an analysis from all functions called
+ %% from on_load attributes; in Core these exist as a list of {F,A} pairs
+ OnLoadFAs = lists:flatten([cerl:atom_val(Args)
+ || {Attr, Args} <- cerl:module_attrs(Tree),
+ cerl:atom_val(Attr) =:= on_load]),
+ OnLoadLs = [cerl_trees:get_label(Fun)
+ || {Var, Fun} <- cerl:module_defs(Tree),
+ lists:member(cerl:var_name(Var), OnLoadFAs)],
+ %% init the escaping function labels to exported + called from on_load
+ InitEsc = set__from_list(OnLoadLs ++ ExpLs),
Arities = cerl_trees:fold(fun find_arities/2, dict:new(), Tree),
- #state{deps = map__new(), esc = InitEsc, call = map__new(), arities = Arities, letrecs = map__new()}.
+ #state{deps = map__new(), esc = InitEsc, call = map__new(),
+ arities = Arities, letrecs = map__new()}.
find_arities(Tree, AccMap) ->
case cerl:is_c_fun(Tree) of
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 08f31c1e13..7070fa240d 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -61,7 +61,7 @@
init_plt :: dialyzer_plt:plt(),
dir_entry :: wx:wx_object(),
file_box :: wx:wx_object(),
- files_to_analyze :: ordset(string()),
+ files_to_analyze :: ordsets:ordset(string()),
gui :: wx:wx_object(),
log :: wx:wx_object(),
menu :: menu(),
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index 06672e595f..a92b8b1958 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -51,7 +51,8 @@ build(Opts) ->
?WARN_CONTRACT_TYPES,
?WARN_CONTRACT_SYNTAX,
?WARN_BEHAVIOUR,
- ?WARN_UNDEFINED_CALLBACK],
+ ?WARN_UNDEFINED_CALLBACK,
+ ?WARN_UNKNOWN],
DefaultWarns1 = ordsets:from_list(DefaultWarns),
InitPlt = dialyzer_plt:get_default_plt(),
DefaultOpts = #options{},
@@ -310,6 +311,8 @@ build_warnings([Opt|Opts], Warnings) ->
ordsets:add_element(?WARN_CONTRACT_SUBTYPE, Warnings);
underspecs ->
ordsets:add_element(?WARN_CONTRACT_SUPERTYPE, Warnings);
+ no_unknown ->
+ ordsets:del_element(?WARN_UNKNOWN, Warnings);
OtherAtom ->
bad_option("Unknown dialyzer warning option", OtherAtom)
end,
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index 5f64099210..63798f44b1 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -2,7 +2,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -67,7 +67,7 @@
%%----------------------------------------------------------------------
--type mod_deps() :: dict().
+-type mod_deps() :: dialyzer_callgraph:mod_deps().
-type deep_string() :: string() | [deep_string()].
@@ -80,11 +80,11 @@
%%----------------------------------------------------------------------
--record(plt, {info = table_new() :: dict(),
- types = table_new() :: dict(),
- contracts = table_new() :: dict(),
- callbacks = table_new() :: dict(),
- exported_types = sets:new() :: set()}).
+-record(plt, {info = table_new() :: dict:dict(),
+ types = table_new() :: dict:dict(),
+ contracts = table_new() :: dict:dict(),
+ callbacks = table_new() :: dict:dict(),
+ exported_types = sets:new() :: sets:set()}).
-record(mini_plt, {info :: ets:tid(),
contracts :: ets:tid(),
@@ -96,15 +96,15 @@
-include("dialyzer.hrl").
-type file_md5() :: {file:filename(), binary()}.
--type plt_info() :: {[file_md5()], dict()}.
+-type plt_info() :: {[file_md5()], dict:dict()}.
-record(file_plt, {version = "" :: string(),
file_md5_list = [] :: [file_md5()],
- info = dict:new() :: dict(),
- contracts = dict:new() :: dict(),
- callbacks = dict:new() :: dict(),
- types = dict:new() :: dict(),
- exported_types = sets:new() :: set(),
+ info = dict:new() :: dict:dict(),
+ contracts = dict:new() :: dict:dict(),
+ callbacks = dict:new() :: dict:dict(),
+ types = dict:new() :: dict:dict(),
+ exported_types = sets:new() :: sets:set(),
mod_deps :: mod_deps(),
implementation_md5 = [] :: [file_md5()]}).
@@ -184,22 +184,22 @@ lookup(Plt, Label) when is_integer(Label) ->
lookup_1(#mini_plt{info = Info}, MFAorLabel) ->
ets_table_lookup(Info, MFAorLabel).
--spec insert_types(plt(), dict()) -> plt().
+-spec insert_types(plt(), dict:dict()) -> plt().
insert_types(PLT, Rec) ->
PLT#plt{types = Rec}.
--spec insert_exported_types(plt(), set()) -> plt().
+-spec insert_exported_types(plt(), sets:set()) -> plt().
insert_exported_types(PLT, Set) ->
PLT#plt{exported_types = Set}.
--spec get_types(plt()) -> dict().
+-spec get_types(plt()) -> dict:dict().
get_types(#plt{types = Types}) ->
Types.
--spec get_exported_types(plt()) -> set().
+-spec get_exported_types(plt()) -> sets:set().
get_exported_types(#plt{exported_types = ExpTypes}) ->
ExpTypes.
@@ -211,7 +211,7 @@ get_exported_types(#plt{exported_types = ExpTypes}) ->
lookup_module(#plt{info = Info}, M) when is_atom(M) ->
table_lookup_module(Info, M).
--spec all_modules(plt()) -> set().
+-spec all_modules(plt()) -> sets:set().
all_modules(#plt{info = Info, contracts = Cs}) ->
sets:union(table_all_modules(Info), table_all_modules(Cs)).
diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl
index 2aa8343bce..b1f849b16f 100644
--- a/lib/dialyzer/src/dialyzer_races.erl
+++ b/lib/dialyzer/src/dialyzer_races.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -85,6 +85,12 @@
-type race_tag() :: 'whereis_register' | 'whereis_unregister'
| 'ets_lookup_insert' | 'mnesia_dirty_read_write'.
+%% The following type is similar to the dial_warning() type but has a
+%% tag which is local to this module and is not propagated to outside
+-type dial_race_warning() :: {race_warn_tag(), file_line(), {atom(), [term()]}}.
+-type race_warn_tag() :: ?WARN_WHEREIS_REGISTER | ?WARN_WHEREIS_UNREGISTER
+ | ?WARN_ETS_LOOKUP_INSERT | ?WARN_MNESIA_DIRTY_READ_WRITE.
+
-record(beg_clause, {arg :: var_to_map1(),
pats :: var_to_map1(),
guard :: cerl:cerl()}).
@@ -98,14 +104,14 @@
def_vars :: [core_vars()],
arg_types :: [erl_types:erl_type()],
call_vars :: [core_vars()],
- var_map :: dict()}).
+ var_map :: dict:dict()}).
-record(dep_call, {call_name :: dep_calls(),
args :: args(),
arg_types :: [erl_types:erl_type()],
vars :: [core_vars()],
- state :: _, %% XXX: recursive
+ state :: dialyzer_dataflow:state(),
file_line :: file_line(),
- var_map :: dict()}).
+ var_map :: dict:dict()}).
-record(fun_call, {caller :: dialyzer_callgraph:mfa_or_funlbl(),
callee :: dialyzer_callgraph:mfa_or_funlbl(),
arg_types :: [erl_types:erl_type()],
@@ -114,7 +120,7 @@
arg :: var_to_map1()}).
-record(warn_call, {call_name :: warn_calls(),
args :: args(),
- var_map :: dict()}).
+ var_map :: dict:dict()}).
-type case_tags() :: 'beg_case' | #beg_clause{} | #end_clause{} | #end_case{}.
-type code() :: [#dep_call{} | #fun_call{} | #warn_call{} |
@@ -141,7 +147,7 @@
race_tags = [] :: [#race_fun{}],
%% true for fun types and warning mode
race_analysis = false :: boolean(),
- race_warnings = [] :: [dial_warning()]}).
+ race_warnings = [] :: [dial_race_warning()]}).
%%% ===========================================================================
%%%
@@ -1565,7 +1571,7 @@ any_args(StrList) ->
end
end.
--spec bind_dict_vars(label(), label(), dict()) -> dict().
+-spec bind_dict_vars(label(), label(), dict:dict()) -> dict:dict().
bind_dict_vars(Key, Label, RaceVarMap) ->
case Key =:= Label of
@@ -1751,7 +1757,7 @@ compare_vars(Var1, Var2, RaceVarMap) when is_integer(Var1), is_integer(Var2) ->
compare_vars(_Var1, _Var2, _RaceVarMap) ->
false.
--spec compare_var_list(label_type(), [label_type()], dict()) -> boolean().
+-spec compare_var_list(label_type(), [label_type()], dict:dict()) -> boolean().
compare_var_list(Var, VarList, RaceVarMap) ->
lists:any(fun (V) -> compare_vars(Var, V, RaceVarMap) end, VarList).
@@ -1763,7 +1769,7 @@ ets_list_args(MaybeList) ->
catch _:_ -> [?no_label]
end;
false -> [ets_tuple_args(MaybeList)]
- end.
+ end.
ets_list_argtypes(ListStr) ->
ListStr1 = string:strip(ListStr, left, $[),
@@ -1956,7 +1962,8 @@ mnesia_tuple_argtypes(TupleStr) ->
[TupleStr2|_T] = string:tokens(TupleStr1, " ,"),
lists:flatten(string:tokens(TupleStr2, " |")).
--spec race_var_map(var_to_map1(), var_to_map2(), dict(), op()) -> dict().
+-spec race_var_map(var_to_map1(), var_to_map2(), dict:dict(), op()) ->
+ dict:dict().
race_var_map(Vars1, Vars2, RaceVarMap, Op) ->
case Vars1 =:= ?no_arg orelse Vars1 =:= ?bypassed
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index f0488b5ee3..ef9b00e203 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -72,7 +72,7 @@
-record(st, {callgraph :: dialyzer_callgraph:callgraph(),
codeserver :: dialyzer_codeserver:codeserver(),
- no_warn_unused :: set(),
+ no_warn_unused :: sets:set(mfa()),
parent = none :: parent(),
timing_server :: dialyzer_timing:timing_server(),
solvers :: [solver()],
@@ -137,7 +137,7 @@ get_refined_success_typings(SCCs, #st{callgraph = Callgraph,
-type doc_plt() :: 'undefined' | dialyzer_plt:plt().
-spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(),
- doc_plt(), dialyzer_codeserver:codeserver(), set(),
+ doc_plt(), dialyzer_codeserver:codeserver(), sets:set(mfa()),
dialyzer_timing:timing_server(), [solver()], pid()) ->
{[dial_warning()], dialyzer_plt:plt(), doc_plt()}.
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index b4b3d5a092..31ceaf5ac5 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -83,7 +83,7 @@
list :: [constr()],
deps :: [dep()],
masks :: [{dep(),[non_neg_integer()]}] |
- {'d',dict()},
+ {'d',dict:dict(dep(), [non_neg_integer()])},
id :: {'list', dep()}}).
-type constraint_list() :: #constraint_list{}.
@@ -94,25 +94,30 @@
-type constr() :: constraint() | constraint_list() | constraint_ref().
--type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, dict()}].
+-type types() :: erl_types:type_table().
+
+-type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, types()}].
-type typesig_funmap() :: [{type_var(), type_var()}]. %% Orddict
--type dict_or_ets() :: {'d', dict()} | {'e', ets:tid()}.
+-type prop_types() :: dict:dict(label(), types()).
+
+-type dict_or_ets() :: {'d', prop_types()} | {'e', ets:tid()}.
-record(state, {callgraph :: dialyzer_callgraph:callgraph(),
cs = [] :: [constr()],
cmap = {'d', dict:new()} :: dict_or_ets(),
fun_map = [] :: typesig_funmap(),
- fun_arities = dict:new() :: dict(),
+ fun_arities = dict:new() :: dict:dict(type_var(), arity()),
in_match = false :: boolean(),
in_guard = false :: boolean(),
module :: module(),
- name_map = dict:new() :: dict(),
+ name_map = dict:new() :: dict:dict(mfa(),
+ cerl:c_fun()),
next_label = 0 :: label(),
self_rec :: 'false' | erl_types:erl_type(),
plt :: dialyzer_plt:plt(),
prop_types = {'d', dict:new()} :: dict_or_ets(),
- records = dict:new() :: dict(),
+ records = dict:new() :: types(),
scc = [] :: [type_var()],
mfas :: [tuple()],
solvers = [] :: [solver()]
@@ -167,7 +172,7 @@
-spec analyze_scc(typesig_scc(), label(),
dialyzer_callgraph:callgraph(),
- dialyzer_plt:plt(), dict(), [solver()]) -> dict().
+ dialyzer_plt:plt(), prop_types(), [solver()]) -> prop_types().
analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) ->
Solvers = solvers(Solvers0),
@@ -1890,7 +1895,7 @@ sane_maps(Map1, Map2, Keys, _S1, _S2) ->
%% Solver v2
--record(v2_state, {constr_data = dict:new() :: dict(),
+-record(v2_state, {constr_data = dict:new() :: dict:dict(),
state :: #state{}}).
v2_solve_ref(Fun, Map, State) ->
@@ -2535,8 +2540,10 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
erase_type(Key, Map);
false ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
- [?debug("LimitedVal ~s\n", [format_type(LimitedVal)]) ||
- not is_equal(LimitedVal, Val)],
+ case is_equal(LimitedVal, Val) of
+ true -> ok;
+ false -> ?debug("LimitedVal ~s\n", [format_type(LimitedVal)])
+ end,
case dict:find(Key, Map) of
{ok, Value} ->
case is_equal(Value, LimitedVal) of
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index a4c4d37a0f..21183e3459 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -164,14 +164,14 @@ get_core_from_abstract_code(AbstrCode, Opts) ->
%% ============================================================================
-spec get_record_and_type_info(abstract_code()) ->
- {'ok', dict()} | {'error', string()}.
+ {'ok', dict:dict()} | {'error', string()}.
get_record_and_type_info(AbstractCode) ->
Module = get_module(AbstractCode),
get_record_and_type_info(AbstractCode, Module, dict:new()).
--spec get_record_and_type_info(abstract_code(), module(), dict()) ->
- {'ok', dict()} | {'error', string()}.
+-spec get_record_and_type_info(abstract_code(), module(), dict:dict()) ->
+ {'ok', dict:dict()} | {'error', string()}.
get_record_and_type_info(AbstractCode, Module, RecDict) ->
get_record_and_type_info(AbstractCode, Module, [], RecDict).
@@ -304,7 +304,7 @@ process_record_remote_types(CServer) ->
CServer1 = dialyzer_codeserver:finalize_records(NewRecords, CServer),
dialyzer_codeserver:finalize_exported_types(TempExpTypes, CServer1).
--spec merge_records(dict(), dict()) -> dict().
+-spec merge_records(dict:dict(), dict:dict()) -> dict:dict().
merge_records(NewRecords, OldRecords) ->
dict:merge(fun(_Key, NewVal, _OldVal) -> NewVal end, NewRecords, OldRecords).
@@ -315,10 +315,10 @@ merge_records(NewRecords, OldRecords) ->
%%
%% ============================================================================
--type spec_dict() :: dict().
--type callback_dict() :: dict().
+-type spec_dict() :: dict:dict().
+-type callback_dict() :: dict:dict().
--spec get_spec_info(atom(), abstract_code(), dict()) ->
+-spec get_spec_info(atom(), abstract_code(), dict:dict()) ->
{'ok', spec_dict(), callback_dict()} | {'error', string()}.
get_spec_info(ModName, AbstractCode, RecordsDict) ->
@@ -383,7 +383,7 @@ get_spec_info([], SpecDict, CallbackDict, _RecordsDict, _ModName, _File) ->
%%
%% ============================================================================
--spec sets_filter([module()], set()) -> set().
+-spec sets_filter([module()], sets:set()) -> sets:set().
sets_filter([], ExpTypes) ->
ExpTypes;
@@ -434,7 +434,7 @@ format_errors([]) ->
format_sig(Type) ->
format_sig(Type, dict:new()).
--spec format_sig(erl_types:erl_type(), dict()) -> string().
+-spec format_sig(erl_types:erl_type(), dict:dict()) -> string().
format_sig(Type, RecDict) ->
"fun(" ++ Sig = lists:flatten(erl_types:t_to_string(Type, RecDict)),
@@ -450,11 +450,10 @@ flat_format(Fmt, Lst) ->
%% Created : 5 March 2007
%%-------------------------------------------------------------------
+-spec pp_hook() -> fun((cerl:cerl(), _, _) -> term()).
pp_hook() ->
fun pp_hook/3.
--spec pp_hook() -> fun((cerl:cerl(), _, _) -> term()).
-
pp_hook(Node, Ctxt, Cont) ->
case cerl:type(Node) of
binary ->
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
index 27cabc8ef8..f43e04dd59 100644
--- a/lib/dialyzer/test/Makefile
+++ b/lib/dialyzer/test/Makefile
@@ -11,6 +11,7 @@ AUXILIARY_FILES=\
dialyzer_test_constants.hrl\
dialyzer_common.erl\
file_utils.erl\
+ dialyzer_SUITE.erl\
plt_SUITE.erl
# ----------------------------------------------------
diff --git a/lib/dialyzer/test/dialyzer_SUITE.erl b/lib/dialyzer/test/dialyzer_SUITE.erl
new file mode 100644
index 0000000000..1b62291a00
--- /dev/null
+++ b/lib/dialyzer/test/dialyzer_SUITE.erl
@@ -0,0 +1,77 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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(dialyzer_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+-define(application, dialyzer).
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases must be exported.
+-export([app_test/1, appup_test/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test, appup_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%
+%%% Test cases starts here.
+%%%
+
+app_test(doc) ->
+ ["Test that the .app file does not contain any `basic' errors"];
+app_test(suite) ->
+ [];
+app_test(Config) when is_list(Config) ->
+ ?line ?t:app_test(dialyzer).
+
+%% Test that the .appup file does not contain any `basic' errors
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(dialyzer).
diff --git a/lib/dialyzer/test/file_utils.erl b/lib/dialyzer/test/file_utils.erl
index 36b368760c..e1314ec8de 100644
--- a/lib/dialyzer/test/file_utils.erl
+++ b/lib/dialyzer/test/file_utils.erl
@@ -106,7 +106,7 @@ lcs_fast(S1, S2) ->
-spec lcs_fast([string()], [string()],
pos_integer(), pos_integer(),
- non_neg_integer(), array()) -> {[string()], array()}.
+ non_neg_integer(), array:array()) -> {[string()], array:array()}.
lcs_fast([], _, _, _, _, Acc) ->
{[], Acc};
diff --git a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
index 3ff26b87db..44a65f6e90 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
+{dialyzer_options, [{warnings, [no_unused, no_return, no_unknown]}]}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/array b/lib/dialyzer/test/opaque_SUITE_data/results/array
index b05d088a03..9921b61669 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/array
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/array
@@ -1,3 +1,3 @@
-array_use.erl:12: The type test is_tuple(array()) breaks the opaqueness of the term array()
-array_use.erl:9: The attempt to match a term of type array() against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term
+array_use.erl:12: The type test is_tuple(array:array(_)) breaks the opaqueness of the term array:array(_)
+array_use.erl:9: The attempt to match a term of type array:array(_) against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/dict b/lib/dialyzer/test/opaque_SUITE_data/results/dict
index 5c6bf6a927..42f6663191 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/dict
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/dict
@@ -1,15 +1,15 @@
-dict_use.erl:41: The attempt to match a term of type dict() against the pattern 'gazonk' breaks the opaqueness of the term
-dict_use.erl:45: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
-dict_use.erl:46: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
-dict_use.erl:51: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
-dict_use.erl:52: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
-dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict()
-dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict()
-dict_use.erl:64: Guard test length(D::dict()) breaks the opaqueness of its argument
-dict_use.erl:65: Guard test is_atom(D::dict()) breaks the opaqueness of its argument
-dict_use.erl:66: Guard test is_list(D::dict()) breaks the opaqueness of its argument
-dict_use.erl:70: The type test is_list(dict()) breaks the opaqueness of the term dict()
-dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict() as 2nd argument
+dict_use.erl:41: The attempt to match a term of type dict:dict(_,_) against the pattern 'gazonk' breaks the opaqueness of the term
+dict_use.erl:45: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opaqueness of the term
+dict_use.erl:46: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:51: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opaqueness of the term
+dict_use.erl:52: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict:dict(_,_)
+dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict:dict(_,_)
+dict_use.erl:64: Guard test length(D::dict:dict(_,_)) breaks the opaqueness of its argument
+dict_use.erl:65: Guard test is_atom(D::dict:dict(_,_)) breaks the opaqueness of its argument
+dict_use.erl:66: Guard test is_list(D::dict:dict(_,_)) breaks the opaqueness of its argument
+dict_use.erl:70: The type test is_list(dict:dict(_,_)) breaks the opaqueness of the term dict:dict(_,_)
+dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict:dict(_,_) as 2nd argument
dict_use.erl:76: The call dict:merge(Fun::any(),42,[1 | 2,...]) does not have opaque terms as 2nd and 3rd arguments
-dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict() as 3rd argument
+dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict:dict(_,_) as 3rd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/ets b/lib/dialyzer/test/opaque_SUITE_data/results/ets
index e79696bc30..e11c7a8352 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/ets
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/ets
@@ -1,4 +1,4 @@
-ets_use.erl:12: Guard test is_integer(T::atom() | tid()) breaks the opaqueness of its argument
-ets_use.erl:20: The type test is_integer(atom() | tid()) breaks the opaqueness of the term atom() | tid()
-ets_use.erl:7: Guard test is_integer(T::tid()) breaks the opaqueness of its argument
+ets_use.erl:12: Guard test is_integer(T::atom() | ets:tid()) breaks the opaqueness of its argument
+ets_use.erl:20: The type test is_integer(atom() | ets:tid()) breaks the opaqueness of the term atom() | ets:tid()
+ets_use.erl:7: Guard test is_integer(T::ets:tid()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/ewgi b/lib/dialyzer/test/opaque_SUITE_data/results/ewgi
index 5bc6b87fbb..209f27b2f2 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/ewgi
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/ewgi
@@ -1,4 +1,4 @@
-ewgi_api.erl:55: The call gb_trees:to_list({non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_tree() as 1st argument
-ewgi_testapp.erl:35: The call ewgi_testapp:htmlise_data("request_data",{non_neg_integer(),'nil' | {_,_,_,_}}) does not have a term of type [{_,_}] | gb_tree() (with opaque subterms) as 2nd argument
-ewgi_testapp.erl:43: The call gb_trees:to_list(T::{non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_tree() as 1st argument
+ewgi_api.erl:55: The call gb_trees:to_list({non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument
+ewgi_testapp.erl:35: The call ewgi_testapp:htmlise_data("request_data",{non_neg_integer(),'nil' | {_,_,_,_}}) does not have a term of type [{_,_}] | gb_trees:tree(_,_) (with opaque subterms) as 2nd argument
+ewgi_testapp.erl:43: The call gb_trees:to_list(T::{non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1 b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1
index 4fe5fcfe2d..ac5ef14041 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1
@@ -2,4 +2,4 @@
inf_loop1.erl:119: The pattern [{_, LNorms}] can never match the type []
inf_loop1.erl:121: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type []
inf_loop1.erl:129: The pattern [{_, Norm} | _] can never match the type []
-inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array()) does not have an opaque term of type gb_tree() as 2nd argument
+inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array:array(_)) does not have an opaque term of type gb_trees:tree(_,_) as 2nd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2 b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2
index 4f0b79eb35..8cd2abe8cd 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2
@@ -2,4 +2,4 @@
inf_loop2.erl:122: The pattern [{_, LNorms}] can never match the type []
inf_loop2.erl:124: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type []
inf_loop2.erl:132: The pattern [{_, Norm} | _] can never match the type []
-inf_loop2.erl:74: The call gb_trees:get(Edge::any(),Etab::array()) does not have an opaque term of type gb_tree() as 2nd argument
+inf_loop2.erl:74: The call gb_trees:get(Edge::any(),Etab::array:array(_)) does not have an opaque term of type gb_trees:tree(_,_) as 2nd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/para b/lib/dialyzer/test/opaque_SUITE_data/results/para
new file mode 100644
index 0000000000..3aaa238de6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/para
@@ -0,0 +1,21 @@
+
+para1.erl:18: The test para1:t(atom()) =:= para1:t(integer()) can never evaluate to 'true'
+para1.erl:23: The test para1:t(atom()) =:= para1:t() can never evaluate to 'true'
+para1.erl:28: The test para1:t() =:= para1:t(integer()) can never evaluate to 'true'
+para1.erl:33: The test {3,2} =:= {'a','b'} can never evaluate to 'true'
+para1.erl:38: Attempt to test for equality between a term of type para1_adt:t(integer()) and a term of opaque type para1_adt:t(atom())
+para1.erl:43: Attempt to test for equality between a term of type para1_adt:t() and a term of opaque type para1_adt:t(atom())
+para1.erl:48: Attempt to test for equality between a term of type para1_adt:t(integer()) and a term of opaque type para1_adt:t()
+para1.erl:53: The test {3,2} =:= {'a','b'} can never evaluate to 'true'
+para2.erl:103: Attempt to test for equality between a term of type para2_adt:circ({{integer(),integer()},{integer(),integer()}},{{integer(),integer()},{integer(),integer()}}) and a term of opaque type para2_adt:circ({{integer(),integer()},{integer(),integer()}})
+para2.erl:117: Attempt to test for equality between a term of type para2_adt:un(atom(),integer()) and a term of opaque type para2_adt:un(integer(),atom())
+para2.erl:31: The test 'a' =:= 'b' can never evaluate to 'true'
+para2.erl:61: Attempt to test for equality between a term of type para2_adt:c2() and a term of opaque type para2_adt:c1()
+para2.erl:66: The test 'a' =:= 'b' can never evaluate to 'true'
+para2.erl:88: The test para2:circ({{integer(),integer()},{integer(),integer()}}) =:= para2:circ({{integer(),integer()},{integer(),integer()}},{{integer(),integer()},{integer(),integer()}}) can never evaluate to 'true'
+para3.erl:28: Invalid type specification for function para3:ot2/0. The success typing is () -> 'foo'
+para3.erl:36: The pattern {{{17}}} can never match the type {{{{{{_,_,_,_,_}}}}}}
+para3.erl:55: Invalid type specification for function para3:t2/0. The success typing is () -> 'foo'
+para3.erl:65: The attempt to match a term of type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}} against the pattern {{{{{17}}}}} breaks the opaqueness of para3_adt:ot1(_,_,_,_,_)
+para3.erl:68: The pattern {{{{17}}}} can never match the type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}}
+para3.erl:74: Invalid type specification for function para3:exp_adt/0. The success typing is () -> 3
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/queue b/lib/dialyzer/test/opaque_SUITE_data/results/queue
index 59ce33f098..5b3813c418 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/queue
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/queue
@@ -1,11 +1,11 @@
-queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue() as 1st argument
-queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue() as 2nd argument
-queue_use.erl:27: The attempt to match a term of type queue() against the pattern {"*", Q2} breaks the opaqueness of the term
-queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue()
-queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term
-queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument
-queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions
-queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue()
-queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument
-queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue() as 2nd argument
+queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue:queue(_) as 1st argument
+queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue:queue(_) as 2nd argument
+queue_use.erl:27: The attempt to match a term of type queue:queue(_) against the pattern {"*", Q2} breaks the opaqueness of the term
+queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue:queue(_)
+queue_use.erl:36: The attempt to match a term of type queue:queue(_) against the pattern {F, _R} breaks the opaqueness of the term
+queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue:queue(_) as 1st argument
+queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue:queue(_)}) contains an opaque term as 2nd argument when terms of different types are expected in these positions
+queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue:queue(_)} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue:queue(_)
+queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue:queue(_)} (with opaque subterms) as 1st argument
+queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue:queue(_) as 2nd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/simple b/lib/dialyzer/test/opaque_SUITE_data/results/simple
index f55b384cbe..072ac9be8f 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/simple
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/simple
@@ -1,6 +1,6 @@
-exact_api.erl:17: The call exact_api:set_type(A::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph() as 1st argument
-exact_api.erl:23: The call digraph:delete(G::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph() as 1st argument
+exact_api.erl:17: The call exact_api:set_type(A::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph:graph() as 1st argument
+exact_api.erl:23: The call digraph:delete(G::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph:graph() as 1st argument
exact_api.erl:55: The attempt to match a term of type exact_adt:exact_adt() against the pattern {'exact_adt'} breaks the opaqueness of the term
exact_api.erl:59: The call exact_adt:exact_adt_set_type2(A::#exact_adt{}) does not have an opaque term of type exact_adt:exact_adt() as 1st argument
is_rec.erl:10: The call erlang:is_record(simple1_adt:d1(),'r',2) contains an opaque term as 1st argument when terms of different types are expected in these positions
@@ -73,7 +73,7 @@ simple1_api.erl:536: Guard test A::simple1_adt:d1() == 3 contains an opaque term
simple1_api.erl:538: Guard test A::simple1_adt:d1() =:= 3 contains an opaque term as 1st argument
simple1_api.erl:548: The call erlang:'<'(A::simple1_adt:d1(),3) contains an opaque term as 1st argument when terms of different types are expected in these positions
simple1_api.erl:558: The call erlang:'=<'(A::simple1_adt:d1(),B::simple1_adt:d2()) contains an opaque term as 1st argument when terms of different types are expected in these positions
-simple1_api.erl:565: Guard test {digraph(),3} > {digraph(),atom() | tid()} contains an opaque term as 2nd argument
+simple1_api.erl:565: Guard test {digraph:graph(),3} > {digraph:graph(),atom() | ets:tid()} contains an opaque term as 2nd argument
simple1_api.erl:91: Invalid type specification for function simple1_api:tup/0. The success typing is () -> {'a','b'}
simple2_api.erl:100: The call lists:flatten(A::simple1_adt:tuple1()) contains an opaque term as 1st argument when a structured term of type [any()] is expected
simple2_api.erl:116: The call lists:flatten({simple1_adt:tuple1()}) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/wings b/lib/dialyzer/test/opaque_SUITE_data/results/wings
index 0ca91ae331..511263b70a 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/wings
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/wings
@@ -1,11 +1,11 @@
-wings_dissolve.erl:103: Guard test is_list(List::gb_set()) breaks the opaqueness of its argument
-wings_dissolve.erl:19: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
-wings_dissolve.erl:272: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
-wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_set() as 1st argument
+wings_dissolve.erl:103: Guard test is_list(List::gb_sets:set(_)) breaks the opaqueness of its argument
+wings_dissolve.erl:19: Guard test is_list(Faces::gb_sets:set(_)) breaks the opaqueness of its argument
+wings_dissolve.erl:272: Guard test is_list(Faces::gb_sets:set(_)) breaks the opaqueness of its argument
+wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_sets:set(_) as 1st argument
wings_edge.erl:205: The pattern <Edge, 'hard', Htab> can never match the type <_,'soft',_>
-wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_set()) does not have an opaque term of type gb_tree() as 1st argument
+wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_sets:set(_)) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument
wings_edge_cmd.erl:32: The pattern [_ | Parts] can never match the type []
wings_edge_cmd.erl:32: The pattern [{_, P} | _] can never match the type []
-wings_io.erl:30: The attempt to match a term of type {'empty',queue()} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue()
-wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_tree()) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected
+wings_io.erl:30: The attempt to match a term of type {'empty',queue:queue(_)} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue:queue(_)
+wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_trees:tree(_,_)) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
index 8a2cd86f43..a4cec065ab 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
@@ -24,7 +24,7 @@ ok3() ->
ok4() ->
dict:fetch(foo, dict:new()).
-ok5() -> % this is OK since some_mod:new/0 might be returning a dict()
+ok5() -> % this is OK since some_mod:new/0 might be returning a dict:dict()
dict:fetch(foo, some_mod:new()).
ok6() ->
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl
index 4eb202f16a..593d9a669d 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl
@@ -17,6 +17,6 @@ t3() ->
is_atom(n()). % no warning since atom() is possible
t4() ->
- is_integer(n()). % opaque warning since tid() is opaque
+ is_integer(n()). % opaque warning since ets:tid() is opaque
-n() -> ets:new(n, [named_table]). % -> atom() | tid()
+n() -> ets:new(n, [named_table]). % -> atom() | ets:tid()
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl
index 0b98f550f1..5cbc79f948 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl
@@ -28,7 +28,7 @@
%% @type bag() = gb_tree()
-ifdef(HAS_GB_TREE_SPEC).
--type bag() :: gb_tree().
+-type bag() :: gb_trees:tree().
-else.
-type bag() :: {non_neg_integer(), {any(), any(), any(), any()} | 'nil'}.
-endif.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl
index 5da8ff0ecf..d8e15cb081 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl
@@ -29,7 +29,7 @@
%% @type bag() = gb_tree()
-ifdef(HAS_GB_TREE_SPEC).
--type bag() :: gb_tree().
+-type bag() :: gb_trees:tree().
-else.
-type bag() :: {non_neg_integer(), {any(), any(), any(), any()} | 'nil'}.
-endif.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl b/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl
index 008b0a486a..7c34b01c2d 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl
@@ -12,12 +12,12 @@
-export([new/0, get_g/1]).
--record(rec, {g :: gb_set()}).
+-record(rec, {g :: gb_sets:set()}).
-spec new() -> #rec{}.
new() ->
#rec{g = gb_sets:empty()}.
--spec get_g(#rec{}) -> gb_set().
+-spec get_g(#rec{}) -> gb_sets:set().
get_g(R) ->
R#rec.g.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl
index 0dff16cf14..3275736e75 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl
@@ -1,7 +1,7 @@
%% -*- erlang-indent-level: 2 -*-
%%----------------------------------------------------------------------------
%% Non-sensical (i.e., stripped-down) program that sends the analysis
-%% into an infinite loop. The #we.es field was originally a gb_tree()
+%% into an infinite loop. The #we.es field was originally a gb_trees:tree()
%% but the programmer declared it as an array in order to change it to
%% that data type instead. In the file, there are two calls to function
%% gb_trees:get/2 which seem to be the ones responsible for sending the
@@ -14,7 +14,7 @@
-export([command/1]).
-record(we, {id,
- es = array:new() :: array(),
+ es = array:new() :: array:array(),
vp,
mirror = none}).
-record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl
index 659ccaf015..3787fc6750 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl
@@ -4,7 +4,7 @@
%% restored.
%% Non-sensical (i.e., stripped-down) program that sends the analysis
-%% into an infinite loop. The #we.es field was originally a gb_tree()
+%% into an infinite loop. The #we.es field was originally a gb_trees:tree()
%% but the programmer declared it as an array in order to change it to
%% that data type instead. In the file, there are two calls to function
%% gb_trees:get/2 which seem to be the ones responsible for sending the
@@ -17,7 +17,7 @@
-export([command/1]).
-record(we, {id,
- es = array:new() :: array(),
+ es = array:new() :: array:array(),
vp,
mirror = none}).
-record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl b/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl
index 9ecd4f92a1..24d0793a7c 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl
@@ -1,4 +1,4 @@
-%% -*- coding: utf-8 -*-
+%%
%%
%% %CopyrightBegin%
%%
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl b/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl
index 9e695cec1d..e9f7ad825b 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl
@@ -2,7 +2,7 @@
-export([weird/1]).
--spec weird(dict() | gb_tree()) -> 42.
+-spec weird(dict:dict() | gb_trees:tree()) -> 42.
weird(gazonk) -> 42.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl
new file mode 100644
index 0000000000..68e2c60368
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl
@@ -0,0 +1,93 @@
+-module(para1).
+
+-compile(export_all).
+
+%% Parameterized opaque types
+
+-export_type([t/0, t/1]).
+
+-opaque t() :: {integer(), integer()}.
+
+-opaque t(A) :: {A, A}.
+
+-type y(A) :: {A, A}.
+
+tt1() ->
+ I = t1(),
+ A = t2(),
+ A =:= I. % never 'true'
+
+tt2() ->
+ I = t0(),
+ A = t2(),
+ A =:= I. % never 'true'
+
+tt3() ->
+ I1 = t0(),
+ I2 = t1(),
+ I1 =:= I2. % never true
+
+tt4() ->
+ I1 = y1(),
+ I2 = y2(),
+ I1 =:= I2. % cannot evaluate to true
+
+adt_tt1() ->
+ I = adt_t1(),
+ A = adt_t2(),
+ A =:= I. % opaque attempt
+
+adt_tt2() ->
+ I = adt_t0(),
+ A = adt_t2(),
+ A =:= I. % opaque attempt
+
+adt_tt3() ->
+ I1 = adt_t0(),
+ I2 = adt_t1(),
+ I1 =:= I2. % opaque attempt
+
+adt_tt4() ->
+ I1 = adt_y1(),
+ I2 = adt_y2(),
+ I1 =:= I2. % cannot evaluate to true
+
+-spec t0() -> t().
+
+t0() ->
+ {3, 2}.
+
+-spec t1() -> t(integer()).
+
+t1() ->
+ {3, 3}.
+
+-spec t2() -> t(atom()).
+
+t2() ->
+ {a, b}.
+
+-spec y1() -> y(integer()).
+
+y1() ->
+ {3, 2}.
+
+-spec y2() -> y(atom()).
+
+y2() ->
+ {a, b}.
+
+adt_t0() ->
+ para1_adt:t0().
+
+adt_t1() ->
+ para1_adt:t1().
+
+adt_t2() ->
+ para1_adt:t2().
+
+adt_y1() ->
+ para1_adt:y1().
+
+adt_y2() ->
+ para1_adt:y2().
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl
new file mode 100644
index 0000000000..95ac6b7982
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl
@@ -0,0 +1,36 @@
+-module(para1_adt).
+
+-export([t0/0, t1/0, t2/0, y1/0, y2/0]).
+
+-export_type([t/0, t/1, y/1]).
+
+-opaque t() :: {integer(), integer()}.
+
+-opaque t(A) :: {A, A}.
+
+-type y(A) :: {A, A}.
+
+-spec t0() -> t().
+
+t0() ->
+ {3, 2}.
+
+-spec t1() -> t(integer()).
+
+t1() ->
+ {3, 3}.
+
+-spec t2() -> t(atom()).
+
+t2() ->
+ {a, b}.
+
+-spec y1() -> y(integer()).
+
+y1() ->
+ {3, 2}.
+
+-spec y2() -> y(atom()).
+
+y2() ->
+ {a, b}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl
new file mode 100644
index 0000000000..09b2235fa5
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl
@@ -0,0 +1,123 @@
+-module(para2).
+
+-compile(export_all).
+
+%% More parameterized opaque types
+
+-export_type([strange/1]).
+
+-export_type([c1/0, c2/0]).
+
+-export_type([circ/1, circ/2]).
+
+-opaque strange(A) :: {B, B, A}.
+
+-spec t(strange(integer())) -> strange(atom()).
+
+t({3, 4, 5}) ->
+ {a, b, c}.
+
+-opaque c1() :: c2().
+-opaque c2() :: c1().
+
+c() ->
+ A = c1(),
+ B = c2(),
+ A =:= B.
+
+t() ->
+ A = ct1(),
+ B = ct2(),
+ A =:= B. % can never evaluate to 'true'
+
+-spec c1() -> c1().
+
+c1() ->
+ a.
+
+-spec c2() -> c2().
+
+c2() ->
+ a.
+
+-type ct1() :: ct2().
+-type ct2() :: ct1().
+
+-spec ct1() -> ct1().
+
+ct1() ->
+ a.
+
+-spec ct2() -> ct2().
+
+ct2() ->
+ b.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+c_adt() ->
+ A = c1_adt(),
+ B = c2_adt(),
+ A =:= B. % opaque attempt
+
+t_adt() ->
+ A = ct1_adt(),
+ B = ct2_adt(),
+ A =:= B. % can never evaluate to true
+
+c1_adt() ->
+ para2_adt:c1().
+
+c2_adt() ->
+ para2_adt:c2().
+
+ct1_adt() ->
+ para2_adt:ct1().
+
+ct2_adt() ->
+ para2_adt:ct2().
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-opaque circ(A) :: circ(A, A).
+-opaque circ(A, B) :: circ({A, B}).
+
+tcirc() ->
+ A = circ1(),
+ B = circ2(),
+ A =:= B. % can never evaluate to 'true' (but the types are not OK, or?)
+
+-spec circ1() -> circ(integer()).
+
+circ1() ->
+ 3.
+
+-spec circ2() -> circ(integer(), integer()).
+
+circ2() ->
+ {3, 3}.
+
+tcirc_adt() ->
+ A = circ1_adt(),
+ B = circ2_adt(),
+ A =:= B. % opaque attempt (one would expect them to be the same...)
+
+circ1_adt() ->
+ para2_adt:circ1().
+
+circ2_adt() ->
+ para2_adt:circ2().
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+u_adt() ->
+ A = u1_adt(),
+ B = u2_adt(),
+ %% The resulting types are equal, but not the parameters:
+ A =:= B. % opaque attempt
+
+u1_adt() ->
+ para2_adt:u1().
+
+u2_adt() ->
+ para2_adt:u2().
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl
new file mode 100644
index 0000000000..96df437c67
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl
@@ -0,0 +1,64 @@
+-module(para2_adt).
+
+%% More parameterized opaque types
+
+-export_type([c1/0, c2/0]).
+
+-export_type([ct1/0, ct2/0]).
+
+-export_type([circ/1, circ/2]).
+
+-export_type([un/2]).
+
+-export([c1/0, c2/0, ct1/0, ct2/0, circ1/0, circ2/0, u1/0, u2/0]).
+
+-opaque c1() :: c2().
+-opaque c2() :: c1().
+
+-spec c1() -> c1().
+
+c1() ->
+ a.
+
+-spec c2() -> c2().
+
+c2() ->
+ a.
+
+-type ct1() :: ct2().
+-type ct2() :: ct1().
+
+-spec ct1() -> ct1().
+
+ct1() ->
+ a.
+
+-spec ct2() -> ct2().
+
+ct2() ->
+ b.
+
+-opaque circ(A) :: circ(A, A).
+-opaque circ(A, B) :: circ({A, B}).
+
+-spec circ1() -> circ(integer()).
+
+circ1() ->
+ 3.
+
+-spec circ2() -> circ(integer(), integer()).
+
+circ2() ->
+ {3, 3}.
+
+-opaque un(A, B) :: A | B.
+
+-spec u1() -> un(integer(), atom()).
+
+u1() ->
+ 3.
+
+-spec u2() -> un(atom(), integer()).
+
+u2() ->
+ 3.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl
new file mode 100644
index 0000000000..792ae40d39
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl
@@ -0,0 +1,77 @@
+-module(para3).
+
+-export([t/0, t1/1, t2/0, ot1/1, ot2/0, t1_adt/0, t2_adt/0]).
+
+-export([exp_adt/0]).
+
+%% More opaque tests.
+
+-export_type([ot1/0, ot1/1, ot1/2, ot1/3, ot1/4, ot1/5]).
+
+-opaque ot1() :: {ot1(_)}.
+
+-opaque ot1(A) :: {ot1(A, A)}.
+
+-opaque ot1(A, B) :: {ot1(A, B, A)}.
+
+-opaque ot1(A, B, C) :: {ot1(A, B, C, A)}.
+
+-opaque ot1(A, B, C, D) :: {ot1(A, B, C, D, A)}.
+
+-opaque ot1(A, B, C, D, E) :: {A, B, C, D, E}.
+
+-spec ot1(_) -> ot1().
+
+ot1(A) ->
+ {{{{{A, A, A, A, A}}}}}.
+
+-spec ot2() -> ot1(). % invalid type spec
+
+ot2() ->
+ foo.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+t() ->
+ {{{17}}} = t1(3). %% pattern can never match
+
+-type t1() :: {t1(_)}.
+
+-type t1(A) :: {t1(A, A)}.
+
+-type t1(A, B) :: {t1(A, B, A)}.
+
+-type t1(A, B, C) :: {t1(A, B, C, A)}.
+
+-type t1(A, B, C, D) :: {t1(A, B, C, D, A)}.
+
+-type t1(A, B, C, D, E) :: {A, B, C, D, E}.
+
+-spec t1(_) -> t1().
+
+t1(A) ->
+ {{{{{A, A, A, A, A}}}}}.
+
+-spec t2() -> t1(). % invalid type spec
+
+t2() ->
+ foo.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Shows that the list TypeNames in t_from_form must include ArgsLen.
+
+t1_adt() ->
+ {{{{{17}}}}} = para3_adt:t1(3). % breaks the opaqueness
+
+t2_adt() ->
+ {{{{17}}}} = para3_adt:t1(3). % can never match
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-type exp() :: para3_adt:exp1(para3_adt:exp2()).
+
+-spec exp_adt() -> exp().
+
+exp_adt() ->
+ 3.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl
new file mode 100644
index 0000000000..3919b846e6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl
@@ -0,0 +1,27 @@
+-module(para3_adt).
+
+-export([t1/1]).
+
+-export_type([t1/0, t1/1, t1/2, t1/3, t1/4, ot1/5]).
+
+-export_type([exp1/1, exp2/0]).
+
+-type t1() :: {t1(_)}.
+
+-type t1(A) :: {t1(A, A)}.
+
+-type t1(A, B) :: {t1(A, B, A)}.
+
+-type t1(A, B, C) :: {t1(A, B, C, A)}.
+
+-type t1(A, B, C, D) :: {ot1(A, B, C, D, A)}.
+
+-opaque ot1(A, B, C, D, E) :: {A, B, C, D, E}.
+
+-spec t1(_) -> t1().
+
+t1(A) ->
+ {{{{{A, A, A, A, A}}}}}.
+
+-opaque exp1(T) :: T.
+-opaque exp2() :: integer().
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl b/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl
index 5f7ab4f3aa..c19330eb30 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl
@@ -10,14 +10,14 @@
ntab = notable :: ets:tab(),
cyclic = true :: boolean()}).
--spec new() -> digraph().
+-spec new() -> digraph:graph().
new() ->
A = #digraph{},
set_type(A), % does not have an opaque term as 1st argument
A.
--spec set_type(digraph()) -> true.
+-spec set_type(digraph:graph()) -> true.
set_type(G) ->
digraph:delete(G).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
index 8f0da1f5dc..ca6bc0ab4a 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
@@ -14,12 +14,12 @@
rel2fam(Rel) ->
sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))).
-%% a definition that does not violate the opaqueness of gb_tree()
+%% a definition that does not violate the opaqueness of gb_trees:tree()
gb_trees_smallest_key(Tree) ->
{Key, _V} = gb_trees:smallest(Tree),
Key.
-%% a definition that violates the opaqueness of gb_tree()
+%% a definition that violates the opaqueness of gb_trees:tree()
gb_trees_largest_key({_, Tree}) ->
largest_key1(Tree).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl
index 38c6051c58..e094d1982b 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl
@@ -2,7 +2,7 @@
-export([get/2]).
--opaque data() :: gb_tree().
+-opaque data() :: gb_trees:tree().
-spec get(term(), data()) -> term().
diff --git a/lib/dialyzer/test/options1_SUITE_data/dialyzer_options b/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
index c612e77d3e..65d233ac0d 100644
--- a/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
-{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists]}]}.
+{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists, no_unknown]}]}.
{time_limit, 30}.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options b/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
index e00e23bb66..ba0e6b1ad7 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
-{dialyzer_options, [{defines, [{vsn, 42}]}]}.
+{dialyzer_options, [{defines, [{vsn, 42}]}, {warnings, [no_unknown]}]}.
{time_limit, 20}.
diff --git a/lib/dialyzer/test/race_SUITE_data/dialyzer_options b/lib/dialyzer/test/race_SUITE_data/dialyzer_options
index 44e1720715..6992fc6c40 100644
--- a/lib/dialyzer/test/race_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/race_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{warnings, [race_conditions]}]}.
+{dialyzer_options, [{warnings, [race_conditions, no_unknown]}]}.
diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3
index d640f564cd..0382627cfc 100644
--- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3
+++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3
@@ -1,3 +1,3 @@
-ets_insert_control_flow3.erl:21: The call ets:insert(Table::atom() | tid(),{'root',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'root') call in ets_insert_control_flow3.erl on line 12
-ets_insert_control_flow3.erl:23: The call ets:insert(Table::atom() | tid(),{'user',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'user') call in ets_insert_control_flow3.erl on line 13
+ets_insert_control_flow3.erl:21: The call ets:insert(Table::atom() | ets:tid(),{'root',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'root') call in ets_insert_control_flow3.erl on line 12
+ets_insert_control_flow3.erl:23: The call ets:insert(Table::atom() | ets:tid(),{'user',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'user') call in ets_insert_control_flow3.erl on line 13
diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4
index 6f34e75902..22944fd066 100644
--- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4
+++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4
@@ -1,3 +1,3 @@
-ets_insert_control_flow4.erl:21: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 13
-ets_insert_control_flow4.erl:23: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 13
+ets_insert_control_flow4.erl:21: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 13
+ets_insert_control_flow4.erl:23: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 13
diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5
index 5af592f43f..e172887f34 100644
--- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5
+++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5
@@ -1,5 +1,5 @@
-ets_insert_control_flow5.erl:22: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16
-ets_insert_control_flow5.erl:23: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 13
-ets_insert_control_flow5.erl:25: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16
-ets_insert_control_flow5.erl:26: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 13
+ets_insert_control_flow5.erl:22: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16
+ets_insert_control_flow5.erl:23: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 13
+ets_insert_control_flow5.erl:25: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16
+ets_insert_control_flow5.erl:26: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 13
diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param
index 58f934a190..6a34337a2c 100644
--- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param
+++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param
@@ -1,5 +1,5 @@
-ets_insert_param.erl:13: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_param.erl on line 10
-ets_insert_param.erl:14: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 14, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 15
-ets_insert_param.erl:17: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_param.erl on line 10
-ets_insert_param.erl:18: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 18
+ets_insert_param.erl:13: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_param.erl on line 10
+ets_insert_param.erl:14: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_param.erl on line 14, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_param.erl on line 15
+ets_insert_param.erl:17: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_param.erl on line 10
+ets_insert_param.erl:18: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_param.erl on line 18
diff --git a/lib/dialyzer/test/small_SUITE_data/dialyzer_options b/lib/dialyzer/test/small_SUITE_data/dialyzer_options
index 50991c9bc5..0d91699e4d 100644
--- a/lib/dialyzer/test/small_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/small_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, []}.
+{dialyzer_options, [{warnings, [no_unknown]}]}.
diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
index 173ff3a9f1..fbdd182358 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
@@ -1,27 +1,28 @@
contracts_with_subtypes.erl:106: The call contracts_with_subtypes:rec_arg({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
contracts_with_subtypes.erl:107: The call contracts_with_subtypes:rec_arg({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:108: The call contracts_with_subtypes:rec_arg({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:110: The call contracts_with_subtypes:rec_arg({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:142: The pattern 1 can never match the type string()
-contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()}
-contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()}
-contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X}
-contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X}
-contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X}
-contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X}
-contracts_with_subtypes.erl:202: The pattern 1 can never match the type string()
-contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return
-contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed')
+contracts_with_subtypes.erl:135: The call contracts_with_subtypes:rec2({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:136: The call contracts_with_subtypes:rec2({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:137: The call contracts_with_subtypes:rec2({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:138: The call contracts_with_subtypes:rec2({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:171: The pattern 1 can never match the type string()
+contracts_with_subtypes.erl:174: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()}
+contracts_with_subtypes.erl:176: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()}
+contracts_with_subtypes.erl:192: The pattern 'alpha' can never match the type {'ok',_}
+contracts_with_subtypes.erl:194: The pattern 42 can never match the type {'ok',_}
+contracts_with_subtypes.erl:212: The pattern 'alpha' can never match the type {'ok',_}
+contracts_with_subtypes.erl:214: The pattern 42 can never match the type {'ok',_}
+contracts_with_subtypes.erl:231: The pattern 1 can never match the type string()
+contracts_with_subtypes.erl:234: The pattern {'ok', _} can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:235: The pattern 'alpha' can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:236: The pattern {'ok', 42} can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:237: The pattern 42 can never match the type {'ok',_,string()}
contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something'
-contracts_with_subtypes.erl:261: Function factored_ets_new_t/0 has no local return
-contracts_with_subtypes.erl:262: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term())
+contracts_with_subtypes.erl:263: Function flat_ets_new_t/0 has no local return
+contracts_with_subtypes.erl:264: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed')
+contracts_with_subtypes.erl:290: Function factored_ets_new_t/0 has no local return
+contracts_with_subtypes.erl:291: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term())
contracts_with_subtypes.erl:77: The call contracts_with_subtypes:foo1(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,atom()), is_subtype(Res,atom())
contracts_with_subtypes.erl:78: The call contracts_with_subtypes:foo2(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,Arg2), is_subtype(Arg2,atom()), is_subtype(Res,atom())
contracts_with_subtypes.erl:79: The call contracts_with_subtypes:foo3(5) breaks the contract (Arg1) -> Res when is_subtype(Arg2,atom()), is_subtype(Arg1,Arg2), is_subtype(Res,atom())
diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2 b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2
new file mode 100644
index 0000000000..9f5433a13d
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2
@@ -0,0 +1,3 @@
+
+contracts_with_subtypes2.erl:18: Function t/0 has no local return
+contracts_with_subtypes2.erl:19: The call contracts_with_subtypes2:t({'a',{'b',{'c',{'d',{'e',{'g',3}}}}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A}), is_subtype(A,{'b',B}), is_subtype(B,{'c',C}), is_subtype(C,{'d',D}), is_subtype(D,{'e',E}), is_subtype(E,{'f',_})
diff --git a/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside b/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside
new file mode 100644
index 0000000000..3e597ef1bc
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside
@@ -0,0 +1,7 @@
+
+funs_from_outside.erl:18: The pattern 'error' can never match the type {'ok','nothing' | 'something'}
+funs_from_outside.erl:32: Function run2/2 has no local return
+funs_from_outside.erl:35: Function testb/3 has no local return
+funs_from_outside.erl:41: The pattern 'error' can never match the type {'ok','nothing' | 'something'}
+funs_from_outside.erl:78: Function test2/1 has no local return
+funs_from_outside.erl:83: The pattern 'error' can never match the type 'ok'
diff --git a/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2 b/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2
new file mode 100644
index 0000000000..4565112ea0
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2
@@ -0,0 +1,2 @@
+
+scala_user.erl:5: Invalid type specification for function scala_user:is_list/2. The success typing is (maybe_improper_list() | tuple(),_) -> boolean()
diff --git a/lib/dialyzer/test/small_SUITE_data/results/predef b/lib/dialyzer/test/small_SUITE_data/results/predef
new file mode 100644
index 0000000000..85e210d6e4
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/predef
@@ -0,0 +1,8 @@
+
+predef.erl:19: Invalid type specification for function predef:array/1. The success typing is (array:array(_)) -> array:array(_)
+predef.erl:24: Invalid type specification for function predef:dict/1. The success typing is (dict:dict(_,_)) -> dict:dict(_,_)
+predef.erl:29: Invalid type specification for function predef:digraph/1. The success typing is (digraph:graph()) -> [any()]
+predef.erl:39: Invalid type specification for function predef:gb_set/1. The success typing is (gb_sets:set(_)) -> gb_sets:set(_)
+predef.erl:44: Invalid type specification for function predef:gb_tree/1. The success typing is (gb_trees:tree(_,_)) -> gb_trees:tree(_,_)
+predef.erl:49: Invalid type specification for function predef:queue/1. The success typing is (queue:queue(_)) -> queue:queue(_)
+predef.erl:54: Invalid type specification for function predef:set/1. The success typing is (sets:set(_)) -> sets:set(_)
diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_construct b/lib/dialyzer/test/small_SUITE_data/results/record_construct
index c0110b144f..4c40fec298 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/record_construct
+++ b/lib/dialyzer/test/small_SUITE_data/results/record_construct
@@ -1,6 +1,6 @@
record_construct.erl:15: Function t_opa/0 has no local return
-record_construct.erl:16: Record construction #r_opa{b::gb_set(),c::42,e::'false'} violates the declared type of field c::boolean()
+record_construct.erl:16: Record construction #r_opa{b::gb_sets:set(_),c::42,e::'false'} violates the declared type of field c::boolean()
record_construct.erl:20: Function t_rem/0 has no local return
record_construct.erl:21: Record construction #r_rem{a::'gazonk'} violates the declared type of field a::string()
record_construct.erl:6: Function t_loc/0 has no local return
diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl
index d72138d509..d7dfd9752e 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl
@@ -103,15 +103,44 @@ c(babb) -> rec_arg({b, {a, {b, b}}});
c(ababb) -> rec_arg({a, {b, {a, {b, b}}}});
c(babaa) -> rec_arg({b, {a, {b, {a, a}}}}).
-w(ab) -> rec_arg({a, b});
-w(ba) -> rec_arg({b, a});
-w(aba) -> rec_arg({a, {b, a}});
-w(bab) -> rec_arg({b, {a, b}});
-w(abab) -> rec_arg({a, {b, {a, b}}});
-w(baba) -> rec_arg({b, {a, {b, a}}});
+w(ab) -> rec_arg({a, b}); % breaks the contract
+w(ba) -> rec_arg({b, a}); % breaks the contract
+w(aba) -> rec_arg({a, {b, a}}); % no longer breaks the contract
+w(bab) -> rec_arg({b, {a, b}}); % breaks the contract
+w(abab) -> rec_arg({a, {b, {a, b}}}); % no longer breaks the contract
+w(baba) -> rec_arg({b, {a, {b, a}}}); % no longer breaks the contract
w(ababa) -> rec_arg({a, {b, {a, {b, a}}}});
w(babab) -> rec_arg({b, {a, {b, {a, b}}}}).
+%% For comparison: the same thing with types
+
+-type ab() :: {a, a()} | {b, b()}.
+-type a() :: a | {b, b()}.
+-type b() :: b | {a, a()}.
+
+-spec rec2(Arg) -> ok when
+ Arg :: ab().
+
+rec2(X) -> get(X).
+
+d(aa) -> rec2({a, a});
+d(bb) -> rec2({b, b});
+d(abb) -> rec2({a, {b, b}});
+d(baa) -> rec2({b, {a, a}});
+d(abaa) -> rec2({a, {b, {a, a}}});
+d(babb) -> rec2({b, {a, {b, b}}});
+d(ababb) -> rec2({a, {b, {a, {b, b}}}});
+d(babaa) -> rec2({b, {a, {b, {a, a}}}}).
+
+q(ab) -> rec2({a, b}); % breaks the contract
+q(ba) -> rec2({b, a}); % breaks the contract
+q(aba) -> rec2({a, {b, a}}); % breaks the contract
+q(bab) -> rec2({b, {a, b}}); % breaks the contract
+q(abab) -> rec2({a, {b, {a, b}}});
+q(baba) -> rec2({b, {a, {b, a}}});
+q(ababa) -> rec2({a, {b, {a, {b, a}}}});
+q(babab) -> rec2({b, {a, {b, {a, b}}}}).
+
%===============================================================================
-type dublo(X) :: {X, X}.
@@ -143,7 +172,7 @@ st(X) when is_atom(X) ->
_Other -> ok
end;
alpha -> bad;
- {ok, 42} -> bad;
+ {ok, 42} -> ok;
42 -> bad
end.
@@ -161,7 +190,7 @@ dt(X) when is_atom(X) ->
err2 -> ok;
{ok, X} -> ok;
alpha -> bad;
- {ok, 42} -> bad;
+ {ok, 42} -> ok;
42 -> bad
end.
@@ -181,7 +210,7 @@ dt2(X) when is_atom(X) ->
err2 -> ok;
{ok, X} -> ok;
alpha -> bad;
- {ok, 42} -> bad;
+ {ok, 42} -> ok;
42 -> bad
end.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl
new file mode 100644
index 0000000000..d2f945b284
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl
@@ -0,0 +1,40 @@
+-module(contracts_with_subtypes2).
+
+-compile(export_all).
+
+-behaviour(supervisor).
+
+-spec t(Arg) -> ok when
+ Arg :: {a, A},
+ A :: {b, B},
+ B :: {c, C},
+ C :: {d, D},
+ D :: {e, E},
+ E :: {f, _}.
+
+t(X) ->
+ get(X).
+
+t() ->
+ t({a, {b, {c, {d, {e, {g, 3}}}}}}). % breaks the contract
+
+%% This one should possibly result in warnings about unused variables.
+-spec l() -> ok when
+ X :: Y,
+ Y :: X.
+
+l() ->
+ ok.
+
+%% This is the example from seq12547 (ticket OTP-11798).
+%% There used to be a warning.
+
+-spec init(term()) -> Result when
+ Result :: {ok, {{supervisor:strategy(),
+ non_neg_integer(),
+ pos_integer()},
+ [supervisor:child_spec()]}}
+ | ignore.
+
+init(_) ->
+ foo:bar().
diff --git a/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl b/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl
new file mode 100644
index 0000000000..f4cbf31160
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl
@@ -0,0 +1,83 @@
+-module(funs_from_outside).
+
+-export([run1/2, run2/2, run3/2]).
+-export([test1/1, test2/1]).
+
+%%------------------------------------------------------------------------------
+
+run1(X, Y) ->
+ testa(fun do_something/1, X, Y).
+
+testa(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ {ok, _} -> ok;
+ error -> error
+ end.
+
+do_nothing(_) -> {ok, nothing}.
+
+do_something(_) -> {ok, something}.
+
+even(X) ->
+ X rem 2 =:= 0.
+
+%%------------------------------------------------------------------------------
+
+%% Duplicating code since we are monovariant...
+
+run2(X, Y) ->
+ testb(fun do_something/1, X, Y).
+
+testb(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ error -> error
+ end.
+
+%%------------------------------------------------------------------------------
+
+%% Duplicating code since we are monovariant...
+
+run3(X, Y) ->
+ testc(fun do_something_2/1, X, Y).
+
+testc(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ {ok, _} -> ok;
+ %% This pattern can match.
+ error -> error
+ end.
+
+do_something_2(foo) -> {ok, something};
+do_something_2(_) -> error.
+
+%%------------------------------------------------------------------------------
+
+test1(Fun) ->
+ F = case get(test1) of
+ test1_t -> Fun;
+ test1_f -> fun fok/0
+ end,
+ error = F().
+
+fok() -> ok.
+
+%%------------------------------------------------------------------------------
+
+test2(Fun) ->
+ F = case get(test1) of
+ test1_t -> fun fok/0;
+ test1_f -> fun fok/0
+ end,
+ error = F().
diff --git a/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl
new file mode 100644
index 0000000000..c26787fe24
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl
@@ -0,0 +1,5 @@
+-module(scala_data).
+
+-export_type([data/0]).
+
+-opaque data() :: {'data', term()}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl
new file mode 100644
index 0000000000..4e981f3b74
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl
@@ -0,0 +1,8 @@
+-module(scala_user).
+
+-export([is_list/2]).
+
+-spec is_list(atom(), scala_data:data()) -> boolean().
+
+is_list( List,Data) when is_list(List) -> true;
+is_list(Tuple,Data) when is_tuple(Tuple) -> false.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl b/lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl
new file mode 100644
index 0000000000..70059f73b6
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl
@@ -0,0 +1,12 @@
+-module(maps_redef).
+
+-export([t/0]).
+
+%% OK in Erlang/OTP 17, at least.
+
+-type map() :: atom(). % redefine built-in type
+
+-spec t() -> map().
+
+t() ->
+ a. % OK
diff --git a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl
index 16533a9caa..7242ac2016 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl
@@ -1,4 +1,6 @@
%%% This is to ensure that "on_load" functions are never reported as unused.
+%%% In addition, all functions called by a function in an on_load attribute
+%%% should be considered as called by an entry point of the module.
-module(on_load).
@@ -8,4 +10,7 @@
foo() -> ok.
-bar() -> ok.
+bar() -> gazonk(17).
+
+gazonk(N) when N < 42 -> gazonk(N+1);
+gazonk(42) -> ok.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef.erl b/lib/dialyzer/test/small_SUITE_data/src/predef.erl
new file mode 100644
index 0000000000..ee9073aa67
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/predef.erl
@@ -0,0 +1,67 @@
+-module(predef).
+
+-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1,
+ queue/1, set/1, tid/0, tid2/0]).
+
+-export_type([array/0, digraph/0, gb_set/0]).
+
+%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque
+%% types were ignored but did not generate any warning.
+-opaque array() :: atom().
+-opaque digraph() :: atom().
+-opaque gb_set() :: atom().
+-type dict() :: atom().
+-type gb_tree() :: atom().
+-type queue() :: atom().
+-type set() :: atom().
+-type tid() :: atom().
+
+-spec array(array()) -> array:array().
+
+array(A) ->
+ array:relax(A).
+
+-spec dict(dict()) -> dict:dict().
+
+dict(D) ->
+ dict:store(1, a, D).
+
+-spec digraph(digraph()) -> [digraph:edge()].
+
+digraph(G) ->
+ digraph:edges(G).
+
+-spec digraph2(digraph:graph()) -> [digraph:edge()].
+
+digraph2(G) ->
+ digraph:edges(G).
+
+-spec gb_set(gb_set()) -> gb_sets:set().
+
+gb_set(S) ->
+ gb_sets:balance(S).
+
+-spec gb_tree(gb_tree()) -> gb_trees:tree().
+
+gb_tree(S) ->
+ gb_trees:balance(S).
+
+-spec queue(queue()) -> queue:queue().
+
+queue(Q) ->
+ queue:reverse(Q).
+
+-spec set(set()) -> sets:set().
+
+set(S) ->
+ sets:union([S]).
+
+-spec tid() -> tid().
+
+tid() ->
+ ets:new(tid, []).
+
+-spec tid2() -> ets:tid().
+
+tid2() ->
+ ets:new(tid, []).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef2.erl b/lib/dialyzer/test/small_SUITE_data/src/predef2.erl
new file mode 100644
index 0000000000..b1d941a49a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/predef2.erl
@@ -0,0 +1,56 @@
+-module(predef2).
+
+-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1,
+ queue/1, set/1, tid/0, tid2/0]).
+
+-export_type([array/0, digraph/0, gb_set/0]).
+
+-spec array(array()) -> array:array().
+
+array(A) ->
+ array:relax(A).
+
+-spec dict(dict()) -> dict:dict().
+
+dict(D) ->
+ dict:store(1, a, D).
+
+-spec digraph(digraph()) -> [digraph:edge()].
+
+digraph(G) ->
+ digraph:edges(G).
+
+-spec digraph2(digraph:graph()) -> [digraph:edge()].
+
+digraph2(G) ->
+ digraph:edges(G).
+
+-spec gb_set(gb_set()) -> gb_sets:set().
+
+gb_set(S) ->
+ gb_sets:balance(S).
+
+-spec gb_tree(gb_tree()) -> gb_trees:tree().
+
+gb_tree(S) ->
+ gb_trees:balance(S).
+
+-spec queue(queue()) -> queue:queue().
+
+queue(Q) ->
+ queue:reverse(Q).
+
+-spec set(set()) -> sets:set().
+
+set(S) ->
+ sets:union([S]).
+
+-spec tid() -> tid().
+
+tid() ->
+ ets:new(tid, []).
+
+-spec tid2() -> ets:tid().
+
+tid2() ->
+ ets:new(tid, []).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl b/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl
index 54cc2601bd..b250c6ee65 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl
@@ -7,7 +7,7 @@ t_loc() ->
#r_loc{}.
-record(r_opa, {a :: atom(),
- b = gb_sets:new() :: gb_set(),
+ b = gb_sets:new() :: gb_sets:set(),
c = 42 :: boolean(),
d, % untyped on purpose
e = false :: boolean()}).
diff --git a/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options b/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
index f7197ac30f..6843119b9d 100644
--- a/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{warnings, [underspecs]}]}.
+{dialyzer_options, [{warnings, [underspecs, no_unknown]}]}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options b/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..49ac917f61
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [unmatched_returns]}]}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings
new file mode 100644
index 0000000000..24b44c1b5c
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings
@@ -0,0 +1,5 @@
+
+lc_warnings.erl:32: Expression produces a value of type [opaque_atom_adt:opaque_atom()], but this value is unmatched
+lc_warnings.erl:43: Expression produces a value of type [array:array(_)], but this value is unmatched
+lc_warnings.erl:65: Expression produces a value of type [lc_warnings:opaque_tuple()], but this value is unmatched
+lc_warnings.erl:7: Expression produces a value of type ['ok' | {'error',atom()}], but this value is unmatched
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl
new file mode 100644
index 0000000000..cb01a8fde3
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl
@@ -0,0 +1,95 @@
+-module(lc_warnings).
+-compile([export_all]).
+
+close(Fs) ->
+ %% There should be a warning since we ignore a potential
+ %% {error,Error} return from file:close/1.
+ [file:close(F) || F <- Fs],
+
+ %% No warning because the type of unmatched return will be ['ok']
+ %% (which is a list of a simple type).
+ [ok = file:close(F) || F <- Fs],
+
+ %% Suppressed.
+ _ = [file:close(F) || F <- Fs],
+ ok.
+
+format(X) ->
+ %% No warning since the result of the list comprehension is
+ %% a list of simple.
+ [io:format("~p\n", [E]) || E <- X],
+
+ %% Warning explicitly suppressed.
+ _ = [io:format("~p\n", [E]) || E <- X],
+ ok.
+
+opaque1() ->
+ List = gen_atom(),
+ %% This is a list of an externally defined opaque type. Since
+ %% we are not allowed to peek inside opaque types, there should
+ %% be a warning (even though the type in this case happens to be
+ %% an atom).
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque2() ->
+ List = gen_array(),
+ %% This is an list of an externally defined opaque type. Since
+ %% we are not allowed to peek inside opaque types, there should
+ %% be a warning.
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque3() ->
+ List = gen_int(),
+
+ %% No warning, since we are allowed to look into the type and can
+ %% see that it is a simple type.
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque4() ->
+ List = gen_tuple(),
+
+ %% There should be a warning, since we are allowed to look inside
+ %% the opaque type and see that it is a tuple (non-simple).
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+gen_atom() ->
+ [opaque_atom_adt:atom(ok)].
+
+gen_array() ->
+ [array:new()].
+
+
+gen_int() ->
+ [opaque_int(42)].
+
+gen_tuple() ->
+ [opaque_tuple(x, 25)].
+
+-opaque opaque_int() :: integer().
+
+-spec opaque_int(integer()) -> opaque_int().
+
+opaque_int(Int) -> Int.
+
+-opaque opaque_tuple() :: {any(),any()}.
+
+-spec opaque_tuple(any(), any()) -> opaque_tuple().
+
+opaque_tuple(X, Y) ->
+ {X,Y}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl
new file mode 100644
index 0000000000..b5b51fe75b
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl
@@ -0,0 +1,9 @@
+-module(opaque_atom_adt).
+-export([atom/1]).
+
+-opaque opaque_atom() :: atom().
+
+-spec atom(atom()) -> opaque_atom().
+
+atom(Atom) ->
+ Atom.
diff --git a/lib/dialyzer/test/user_SUITE_data/dialyzer_options b/lib/dialyzer/test/user_SUITE_data/dialyzer_options
index 513ed7752b..d20ecd389f 100644
--- a/lib/dialyzer/test/user_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/user_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
-{dialyzer_options, []}.
+{dialyzer_options, [{warnings, [no_unknown]}]}.
{time_limit, 3}. \ No newline at end of file
diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml
index 13ec5bbfc1..0c7e6b794d 100644
--- a/lib/diameter/doc/src/diameter_make.xml
+++ b/lib/diameter/doc/src/diameter_make.xml
@@ -52,7 +52,7 @@ under the License.
<p>
The function &codec; is used to compile a diameter
&dictionary; into Erlang source.
-The resulting source implements the interface diameter required
+The resulting source implements the interface diameter requires
to encode and decode the dictionary's messages and AVPs.</p>
<p>
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index cd14195e78..e2390df37c 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -11,7 +11,7 @@
<header>
<copyright>
<year>2011</year>
-<year>2013</year>
+<year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -73,12 +73,6 @@ first.</p>
<p>
Own Id: OTP-11361</p>
</item>
- <item>
- <p>
- Fix silent make rules (Thanks to Anthony Ramine)</p>
- <p>
- Own Id: OTP-11514</p>
- </item>
</list>
</section>
diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl
index 55aae3a243..c8f706dc3e 100644
--- a/lib/diameter/include/diameter_gen.hrl
+++ b/lib/diameter/include/diameter_gen.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -25,6 +25,11 @@
-define(THROW(T), throw({?MODULE, T})).
+%% Key to a value in the process dictionary that determines whether or
+%% not an unrecognized AVP setting the M-bit should be regarded as an
+%% error or not. See is_strict/0.
+-define(STRICT_KEY, strict).
+
-type parent_name() :: atom(). %% parent = Message or AVP
-type parent_record() :: tuple(). %%
-type avp_name() :: atom().
@@ -35,6 +40,18 @@
-type grouped_avp() :: nonempty_improper_list(#diameter_avp{}, [avp()]).
-type avp() :: non_grouped_avp() | grouped_avp().
+%% Use a (hopefully) unique key when manipulating the process
+%% dictionary.
+
+putr(K,V) ->
+ put({?MODULE, K}, V).
+
+getr(K) ->
+ get({?MODULE, K}).
+
+eraser(K) ->
+ erase({?MODULE, K}).
+
%% ---------------------------------------------------------------------------
%% # encode_avps/2
%% ---------------------------------------------------------------------------
@@ -212,12 +229,61 @@ decode(Name, #diameter_avp{code = Code, vendor_id = Vid} = Avp, Acc) ->
%% decode/4
+%% AVP is defined in the dictionary ...
decode(Name, {AvpName, Type}, Avp, Acc) ->
d(Name, Avp#diameter_avp{name = AvpName, type = Type}, Acc);
+%% ... or not.
decode(Name, 'AVP', Avp, Acc) ->
decode_AVP(Name, Avp, Acc).
+%% 6733, 4.4:
+%%
+%% Receivers of a Grouped AVP that does not have the 'M' (mandatory)
+%% bit set and one or more of the encapsulated AVPs within the group
+%% has the 'M' (mandatory) bit set MAY simply be ignored if the
+%% Grouped AVP itself is unrecognized. The rule applies even if the
+%% encapsulated AVP with its 'M' (mandatory) bit set is further
+%% encapsulated within other sub-groups, i.e., other Grouped AVPs
+%% embedded within the Grouped AVP.
+%%
+%% The first sentence is slightly mangled, but take it to mean this:
+%%
+%% An unrecognized AVP of type Grouped that does not set the 'M' bit
+%% MAY be ignored even if one of its encapsulated AVPs sets the 'M'
+%% bit.
+%%
+%% The text above is a change from RFC 3588, which instead says this:
+%%
+%% Further, if any of the AVPs encapsulated within a Grouped AVP has
+%% the 'M' (mandatory) bit set, the Grouped AVP itself MUST also
+%% include the 'M' bit set.
+%%
+%% Both of these texts have problems. If the AVP is unknown then its
+%% type is unknown since the type isn't sent over the wire, so the
+%% 6733 text becomes a non-statement: don't know that the AVP not
+%% setting the M-bit is of type Grouped, therefore can't know that its
+%% data consists of encapsulated AVPs, therefore can't but ignore that
+%% one of these might set the M-bit. It should be no worse if we know
+%% the AVP to have type Grouped.
+%%
+%% Similarly, for the 3588 text: if we receive an AVP that doesn't set
+%% the M-bit and don't know that the AVP has type Grouped then we
+%% can't realize that its data contains an AVP that sets the M-bit, so
+%% can't regard the AVP as erroneous on this account. Again, it should
+%% be no worse if the type is known to be Grouped, but in this case
+%% the RFC forces us to regard the AVP as erroneous. This is
+%% inconsistent, and the 3588 text has never been enforced.
+%%
+%% So, if an AVP doesn't set the M-bit then we're free to ignore it,
+%% regardless of the AVP's type. If we know the type to be Grouped
+%% then we must ignore the M-bit on an encapsulated AVP. That means
+%% packing such an encapsulated AVP into an 'AVP' field if need be,
+%% not regarding the lack of a specific field as an error as is
+%% otherwise the case. (The lack of an AVP-specific field being how we
+%% defined the RFC's "unrecognized", which is slightly stronger than
+%% "not defined".)
+
%% d/3
%% Don't try to decode the value of a Failed-AVP component since it
@@ -230,9 +296,19 @@ d('Failed-AVP' = Name, Avp, Acc) ->
%% Or try to decode.
d(Name, Avp, {Avps, Acc}) ->
#diameter_avp{name = AvpName,
- data = Data}
+ data = Data,
+ type = Type,
+ is_mandatory = M}
= Avp,
+ %% Use the process dictionary is to keep track of whether or not
+ %% to ignore an M-bit on an encapsulated AVP. Not ideal, but the
+ %% alternative requires widespread changes to be able to pass the
+ %% value around through the entire decode. The solution here is
+ %% simple in comparison, both to implement and to understand.
+
+ Reset = relax(Type, M),
+
try avp(decode, Data, AvpName) of
V ->
{H, A} = ungroup(V, Avp),
@@ -250,8 +326,32 @@ d(Name, Avp, {Avps, Acc}) ->
{Reason, Avp, erlang:get_stacktrace()}),
{Rec, Failed} = Acc,
{[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}}
+ after
+ relax(Reset)
end.
+%% Set false in the process dictionary as soon as we see a Grouped AVP
+%% that doesn't set the M-bit, so that is_strict() can say whether or
+%% not to ignore the M-bit on an encapsulated AVP.
+relax('Grouped', M) ->
+ V = getr(?STRICT_KEY),
+ if V == undefined andalso not M ->
+ putr(?STRICT_KEY, M);
+ true ->
+ false
+ end;
+relax(_, _) ->
+ false.
+
+%% Reset strictness.
+relax(undefined) ->
+ eraser(?STRICT_KEY);
+relax(false) ->
+ ok.
+
+is_strict() ->
+ false /= getr(?STRICT_KEY).
+
%% decode_AVP/3
%%
%% Don't know this AVP: see if it can be packed in an 'AVP' field
@@ -310,15 +410,8 @@ pack_avp(_, Arity, Avp, Acc) ->
%% pack_AVP/3
-%% Give Failed-AVP special treatment since it'll contain any
-%% unrecognized mandatory AVP's.
-pack_AVP(Name, #diameter_avp{is_mandatory = true} = Avp, Acc)
- when Name /= 'Failed-AVP' ->
- {Rec, Failed} = Acc,
- {Rec, [{5001, Avp} | Failed]};
-
pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) ->
- case avp_arity(Name, 'AVP') of
+ case pack_arity(Name, M) of
0 ->
{Rec, Failed} = Acc,
{Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]};
@@ -326,6 +419,16 @@ pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) ->
pack(Arity, 'AVP', Avp, Acc)
end.
+%% Give Failed-AVP special treatment since it'll contain any
+%% unrecognized mandatory AVP's.
+pack_arity(Name, M) ->
+ case Name /= 'Failed-AVP' andalso M andalso is_strict() of
+ true ->
+ 0;
+ false ->
+ avp_arity(Name, 'AVP')
+ end.
+
%% 3588:
%%
%% DIAMETER_AVP_UNSUPPORTED 5001
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 578bbaee2e..127406ae23 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2013. All Rights Reserved.
+# Copyright Ericsson AB 2010-2014. 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
@@ -41,7 +41,7 @@ INCDIR = ../include
ABS_EBIN := $(shell cd $(EBIN) && pwd)
# Where make should look for dependencies.
-VPATH = .:base:compiler:transport:gen
+VPATH = .:base:compiler:transport:gen:info
# ----------------------------------------------------
# Target specs
@@ -55,13 +55,13 @@ DICT_ERLS = $(DICT_MODULES:%=%.erl)
DICT_HRLS = $(DICT_MODULES:%=%.hrl)
# Modules to build before compiling dictionaries.
-COMPILER_MODULES = $(notdir $(filter compiler/%, $(CT_MODULES))) \
- $(DICT_YRL)
+COMPILER_MODULES = $(notdir $(CT_MODULES)) $(DICT_YRL)
# All handwritten modules from which a depend.mk is generated.
MODULES = \
$(RT_MODULES) \
- $(CT_MODULES)
+ $(CT_MODULES) \
+ $(INFO_MODULES)
# Modules whose names are inserted into the app file.
APP_MODULES = \
@@ -72,6 +72,7 @@ APP_MODULES = \
TARGET_MODULES = \
$(APP_MODULES) \
$(CT_MODULES) \
+ $(INFO_MODULES) \
$(DICT_YRL:%=gen/%)
# What to build for the 'opt' target.
@@ -150,9 +151,13 @@ gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl
$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
$(gen_verbose) \
M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ C=`echo $(COMPILER_MODULES) | tr ' ' ,`; \
+ I=`echo $(notdir $(INFO_MODULES)) | tr ' ' ,`; \
R=`echo $(REGISTERED) | tr ' ' ,`; \
sed -e 's;%VSN%;$(VSN);' \
-e "s;%MODULES%;$$M;" \
+ -e "s;%COMPILER%;$$C;" \
+ -e "s;%INFO%;$$I;" \
-e "s;%REGISTERED%;$$R;" \
$< > $@
@@ -177,6 +182,8 @@ info:
@echo
@$(call list,CT_MODULES)
@echo
+ @$(call list,INFO_MODULES)
+ @echo
@$(call list,TARGET_MODULES)
@echo
@$(call list,TARGET_DIRS)
@@ -216,7 +223,7 @@ dialyze: opt $(PLT)
-Wno_improper_lists \
$(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \
$(patsubst %, $(EBIN)/%.$(EMULATOR), \
- $(notdir $(RT_MODULES) $(CT_MODULES)))
+ $(notdir $(RT_MODULES) $(CT_MODULES) $(INFO_MODULES)))
# Omit all but the common dictionary module since these
# (diameter_gen_relay in particular) generate warning depending on how
# much of the included diameter_gen.hrl they use.
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 1274e0fc48..8914992f17 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1389,6 +1389,8 @@ pick_peer(Local, Remote, Pid, _SvcName, #diameter_app{mutable = true} = App)
case call_service(Pid, {pick_peer, Local, Remote, App}) of
{TPid, _} = T when is_pid(TPid) ->
T;
+ false = No ->
+ No;
{error, _} ->
false
end;
diff --git a/lib/diameter/src/diameter.app.src b/lib/diameter/src/diameter.app.src
index ceefb9b398..d2290aeccc 100644
--- a/lib/diameter/src/diameter.app.src
+++ b/lib/diameter/src/diameter.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -20,9 +20,20 @@
{application, diameter,
[{description, "Diameter protocol"},
{vsn, "%VSN%"},
- {modules, [%MODULES%]},
+ {modules, [
+ %MODULES%
+ %,%COMPILER%
+ %,%INFO%
+ ]},
{registered, [%REGISTERED%]},
- {applications, [stdlib, kernel]},
+ {applications, [
+ stdlib, kernel
+ %, syntax_tools
+ %, runtime_tools
+ %, ssl
+ ]},
{env, []},
- {mod, {diameter_app, []}}
+ {mod, {diameter_app, []}},
+ {runtime_dependencies, ["syntax_tools-1.6.14","stdlib-2.0","ssl-5.3.4",
+ "runtime_tools-1.8.14","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/diameter/src/base/diameter_dbg.erl b/lib/diameter/src/info/diameter_dbg.erl
index 5b0ac3a3b6..1237007a75 100644
--- a/lib/diameter/src/base/diameter_dbg.erl
+++ b/lib/diameter/src/info/diameter_dbg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -17,6 +17,10 @@
%% %CopyrightEnd%
%%
+%%
+%% Information and debug functions.
+%%
+
-module(diameter_dbg).
-export([table/1,
@@ -29,8 +33,7 @@
compiled/0,
procs/0,
latest/0,
- nl/0,
- log/4]).
+ nl/0]).
-export([diameter_config/0,
diameter_peer/0,
@@ -52,9 +55,8 @@
tp/1]).
-include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
+-define(APPLICATION, diameter).
-define(INFO, diameter_info).
-define(SEP(), ?INFO:sep()).
@@ -68,9 +70,6 @@
-define(VALUES(Rec), tl(tuple_to_list(Rec))).
-log(_Slogan, _Mod, _Line, _Details) ->
- ok.
-
%%% ----------------------------------------------------------
%%% # help()
%%% ----------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_info.erl b/lib/diameter/src/info/diameter_info.erl
index 39d32d07cd..10972f3231 100644
--- a/lib/diameter/src/base/diameter_info.erl
+++ b/lib/diameter/src/info/diameter_info.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -17,6 +17,11 @@
%% %CopyrightEnd%
%%
+%%
+%% Generic functions for formatting table listings and more. Used by
+%% diameter_dbg.
+%%
+
-module(diameter_info).
-export([usage/1,
@@ -573,12 +578,7 @@ sys_info() ->
{A,V}.
os_info() ->
- {os:version(), case os:type() of
- {_Fam, _Name} = T ->
- T;
- Fam ->
- {Fam, ""}
- end}.
+ {os:version(), os:type()}.
chomp(S) ->
string:strip(S, right, $\n).
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index f8d3cf1d6f..a2a7a51892 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -1,8 +1,7 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2013. All Rights Reserved.
+# Copyright Ericsson AB 2010-2014. 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
@@ -64,16 +63,19 @@ RT_MODULES = \
transport/diameter_transport \
transport/diameter_transport_sup
-# Handwritten (compile time) modules not included in the app file.
+# Handwritten compiler modules not included in the app file.
CT_MODULES = \
- base/diameter_dbg \
- base/diameter_info \
compiler/diameter_codegen \
compiler/diameter_exprecs \
compiler/diameter_dict_scanner \
compiler/diameter_dict_util \
compiler/diameter_make
+# Info/debug modules, also not included in the app file.
+INFO_MODULES = \
+ info/diameter_dbg \
+ info/diameter_info
+
# Released hrl files in ../include intended for public consumption.
EXTERNAL_HRLS = \
diameter.hrl \
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index f5275e66b5..d0a01351f3 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -171,18 +171,33 @@ start_link(T) ->
info({gen_sctp, Sock}) ->
lists:flatmap(fun(K) -> info(K, Sock) end,
- [{socket, sockname},
- {peer, peername},
+ [{socket, socknames},
+ {peer, peernames},
{statistics, getstat}]).
info({K,F}, Sock) ->
case inet:F(Sock) of
{ok, V} ->
- [{K,V}];
+ [{K, map(F,V)}];
_ ->
[]
end.
+%% inet:{sock,peer}names/1 returns [{Addr, Port}] but the port number
+%% should be the same in each tuple. Map to a {[Addr], Port} tuple if
+%% so.
+map(K, [{_, Port} | _] = APs)
+ when K == socknames;
+ K == peernames ->
+ try [A || {A,P} <- APs, P == Port orelse throw(?MODULE)] of
+ As -> {As, Port}
+ catch
+ ?MODULE -> APs
+ end;
+
+map(_, V) ->
+ V.
+
%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -549,7 +564,7 @@ accept_peer(_, []) ->
ok;
accept_peer(Sock, Matches) ->
- {RAddrs, _} = ok(inet:peername(Sock)),
+ RAddrs = [A || {A,_} <- ok(inet:peernames(Sock))],
diameter_peer:match(RAddrs, Matches)
orelse x({accept, RAddrs, Matches}),
ok.
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 0b4568a9e5..90536dcf2b 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -1,8 +1,7 @@
-%% coding: utf-8
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
index 0c8d5b85f8..9e1155d3e8 100644
--- a/lib/edoc/src/edoc.app.src
+++ b/lib/edoc/src/edoc.app.src
@@ -22,4 +22,6 @@
otpsgml_layout]},
{registered,[]},
{applications, [compiler,kernel,stdlib,syntax_tools]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["xmerl-1.3.7","syntax_tools-1.6.14","stdlib-2.0",
+ "kernel-3.0","inets-5.10","erts-6.0"]}]}.
diff --git a/lib/edoc/src/edoc.appup.src b/lib/edoc/src/edoc.appup.src
index 54a63833e6..45b4046ed8 100644
--- a/lib/edoc/src/edoc.appup.src
+++ b/lib/edoc/src/edoc.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, edoc}]}],
+ [{<<".*">>,[{restart_application, edoc}]}]
+}.
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
index 466c9df951..211a354c74 100644
--- a/lib/edoc/src/edoc_specs.erl
+++ b/lib/edoc/src/edoc_specs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -35,7 +35,7 @@
%% Exported functions
%%
--spec type(Form::syntaxTree(), TypeDocs::dict()) -> #tag{}.
+-spec type(Form::syntaxTree(), TypeDocs::dict:dict()) -> #tag{}.
%% @doc Convert an Erlang type to EDoc representation.
%% TypeDocs is a dict of {Name, Doc}.
@@ -88,7 +88,7 @@ dummy_spec(Form) ->
-spec docs(Forms::[syntaxTree()],
CommentFun :: fun( ([syntaxTree()], Line :: term()) -> #tag{} ))
- -> dict().
+ -> dict:dict().
%% @doc Find comments after -type/-opaque declarations.
%% Postcomments "inside" the type are skipped.
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 74702102f0..264a533a52 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -454,7 +454,7 @@ check_type(#tag{line = L, data = Data}, P0, Ls, Ts) ->
check_type(#t_def{type = Type}, P, Ls, Ts) ->
check_type(Type, P, Ls, Ts);
check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) ->
- check_used_type(Name, Args, P, Ls),
+ _ = check_used_type(Name, Args, P, Ls),
check_types3(Args++Ts, P, Ls);
check_type(#t_var{}, P, Ls, Ts) ->
check_types3(Ts, P, Ls);
diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl
index b649971e99..c9c7811afb 100644
--- a/lib/edoc/test/edoc_SUITE.erl
+++ b/lib/edoc/test/edoc_SUITE.erl
@@ -22,12 +22,12 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([build_std/1,build_map_module/1]).
+-export([app/1,appup/1,build_std/1,build_map_module/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [build_std,build_map_module].
+ [app,appup,build_std,build_map_module].
groups() ->
[].
@@ -44,6 +44,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+%% Test that the .app file does not contain any `basic' errors
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(edoc).
+
+%% Test that the .appup file does not contain any `basic' errors
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(edoc).
build_std(suite) -> [];
build_std(doc) -> ["Build some documentation using standard EDoc layout"];
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 2fcc97e406..0172aac48b 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.12.1
+EDOC_VSN = 0.7.13
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 228d3b34c3..8009a8d6a3 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -69,12 +69,13 @@ filter() See present/1, substrings/2,
<fsummary>Open a connection to an LDAP server.</fsummary>
<type>
<v>Handle = handle()</v>
- <v>Option = {port, integer()} | {log, function()} | {timeout, integer()} | {ssl, boolean()} | {sslopts, list()}</v>
+ <v>Option = {port, integer()} | {log, function()} | {timeout, integer()} | {ssl, boolean()} | {sslopts, list()} | {tcpopts, list()}</v>
</type>
<desc>
<p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order.</p>
<p>The log function takes three arguments, <c>fun(Level, FormatString, [FormatArg]) end</c>.</p>
<p>Timeout set the maximum time in milliseconds that each server request may take.</p>
+ <p>Currently, the only TCP socket option accepted is <c>inet6</c>. Default is <c>inet</c>.</p>
</desc>
</func>
<func>
diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile
index ebb7967e11..2e1110ec2c 100644
--- a/lib/eldap/src/Makefile
+++ b/lib/eldap/src/Makefile
@@ -88,7 +88,7 @@ $(TARGET_FILES): $(HRL_FILES)
# Special Build Targets
# ----------------------------------------------------
$(ASN1_HRL): ../asn1/$(ASN1_FILES)
- $(asn_verbose)$(ERLC) -o $(EBIN) -bber $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
+ $(asn_verbose)$(ERLC) -o $(EBIN) +legacy_erlang_types -bber $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
# ----------------------------------------------------
# Release Target
diff --git a/lib/eldap/src/eldap.app.src b/lib/eldap/src/eldap.app.src
index 8215328910..03a7d7c562 100644
--- a/lib/eldap/src/eldap.app.src
+++ b/lib/eldap/src/eldap.app.src
@@ -4,5 +4,7 @@
{modules, [eldap, 'ELDAPv3']},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","kernel-3.0","erts-6.0",
+ "asn1-3.0"]}
]}.
diff --git a/lib/eldap/src/eldap.appup.src b/lib/eldap/src/eldap.appup.src
index 8d33482f11..9d77faf740 100644
--- a/lib/eldap/src/eldap.appup.src
+++ b/lib/eldap/src/eldap.appup.src
@@ -1,6 +1,25 @@
%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
{"%VSN%",
[
+ {<<".*">>,[{restart_application, eldap}]}
],
[
- ]}.
+ {<<".*">>,[{restart_application, eldap}]}
+ ]
+}.
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
index af5bf94c97..1cd328cde3 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -45,9 +45,10 @@
log, % User provided log function
timeout = infinity, % Request timeout
anon_auth = false, % Allow anonymous authentication
- ldaps = false, % LDAP/LDAPS
+ ldaps = false, % LDAP/LDAPS
using_tls = false, % true if LDAPS or START_TLS executed
- tls_opts = [] % ssl:ssloption()
+ tls_opts = [], % ssl:ssloption()
+ tcp_opts = [] % inet6 support
}).
%%% For debug purposes
@@ -372,6 +373,8 @@ parse_args([{sslopts, Opts}|T], Cpid, Data) when is_list(Opts) ->
parse_args(T, Cpid, Data#eldap{ldaps = true, using_tls=true, tls_opts = Opts ++ Data#eldap.tls_opts});
parse_args([{sslopts, _}|T], Cpid, Data) ->
parse_args(T, Cpid, Data);
+parse_args([{tcpopts, Opts}|T], Cpid, Data) when is_list(Opts) ->
+ parse_args(T, Cpid, Data#eldap{tcp_opts = inet6_opt(Opts) ++ Data#eldap.tcp_opts});
parse_args([{log, F}|T], Cpid, Data) when is_function(F) ->
parse_args(T, Cpid, Data#eldap{log = F});
parse_args([{log, _}|T], Cpid, Data) ->
@@ -382,6 +385,14 @@ parse_args([H|_], Cpid, _) ->
parse_args([], _, Data) ->
Data.
+inet6_opt(Opts) ->
+ case proplists:get_value(inet6, Opts) of
+ true ->
+ [inet6];
+ _ ->
+ []
+ end.
+
%%% Try to connect to the hosts in the listed order,
%%% and stop with the first one to which a successful
%%% connection is made.
@@ -401,9 +412,11 @@ try_connect([],_) ->
{error,"connect failed"}.
do_connect(Host, Data, Opts) when Data#eldap.ldaps == false ->
- gen_tcp:connect(Host, Data#eldap.port, Opts, Data#eldap.timeout);
+ gen_tcp:connect(Host, Data#eldap.port, Opts ++ Data#eldap.tcp_opts,
+ Data#eldap.timeout);
do_connect(Host, Data, Opts) when Data#eldap.ldaps == true ->
- ssl:connect(Host, Data#eldap.port, Opts++Data#eldap.tls_opts).
+ ssl:connect(Host, Data#eldap.port,
+ Opts ++ Data#eldap.tls_opts ++ Data#eldap.tcp_opts).
loop(Cpid, Data) ->
receive
@@ -743,7 +756,7 @@ request(S, Data, ID, Request) ->
send_request(S, Data, ID, Request) ->
Message = #'LDAPMessage'{messageID = ID,
protocolOp = Request},
- {ok,Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message),
+ {ok,Bytes} = 'ELDAPv3':encode('LDAPMessage', Message),
case do_send(S, Data, Bytes) of
{error,Reason} -> throw({gen_tcp_error,Reason});
Else -> Else
@@ -762,7 +775,7 @@ do_recv(S, #eldap{using_tls=true, timeout=Timeout}, Len) ->
recv_response(S, Data) ->
case do_recv(S, Data, 0) of
{ok, Packet} ->
- case asn1rt:decode('ELDAPv3', 'LDAPMessage', Packet) of
+ case 'ELDAPv3':decode('LDAPMessage', Packet) of
{ok,Resp} -> {ok,Resp};
Error -> throw(Error)
end;
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index bf5fa83c3c..6c3d303da0 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -84,6 +84,7 @@ end_per_testcase(_TestCase, Config) ->
all() ->
[app,
+ appup,
api,
ssl_api,
start_tls,
@@ -95,7 +96,11 @@ all() ->
app(doc) -> "Test that the eldap app file is ok";
app(suite) -> [];
app(Config) when is_list(Config) ->
- ok = test_server:app_test(public_key).
+ ok = test_server:app_test(eldap).
+
+%% Test that the eldap appup file is ok
+appup(Config) when is_list(Config) ->
+ ok = test_server:appup_test(eldap).
api(doc) -> "Basic test that all api functions works as expected";
api(suite) -> [];
diff --git a/lib/eldap/test/eldap_misc_SUITE.erl b/lib/eldap/test/eldap_misc_SUITE.erl
new file mode 100644
index 0000000000..ca810ee33c
--- /dev/null
+++ b/lib/eldap/test/eldap_misc_SUITE.erl
@@ -0,0 +1,51 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2014. 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(eldap_misc_SUITE).
+
+-compile(export_all). %% Use this only in test suites...
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eldap/include/eldap.hrl").
+-include_lib("eldap/ebin/ELDAPv3.hrl").
+
+all() ->
+ [
+ encode,
+ decode
+ ].
+
+
+encode(_Config) ->
+ {ok,Bin} = 'ELDAPv3':encode('AddRequest', #'AddRequest'{entry="hejHopp" ,attributes=[]} ),
+ Expected = <<104,11,4,7,104,101,106,72,111,112,112,48,0>>,
+ Expected = Bin.
+
+decode(_Config) ->
+ {ok,Res} = 'ELDAPv3':decode('AddRequest', <<104,11,4,7,104,101,106,72,111,112,112,48,0>>),
+ ct:log("Res = ~p", [Res]),
+ Expected = #'AddRequest'{entry = "hejHopp",attributes = []},
+ case Res of
+ Expected -> ok;
+ #'AddRequest'{entry= <<"hejHopp">>, attributes=[]} ->
+ {fail, "decoded to (correct) binary!!"};
+ _ ->
+ {fail, "Bad decode"}
+ end.
+
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
index dd1f50653c..efdc30b476 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1 +1,2 @@
-ELDAP_VSN = 1.0.2
+ELDAP_VSN = 1.0.3
+
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index e3cc354206..e6bf9ce20e 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -1014,6 +1014,8 @@ t_type([#xmlElement{name = nil}]) ->
t_nil();
t_type([#xmlElement{name = list, content = Es}]) ->
t_list(Es);
+t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ t_nonempty_list(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
@@ -1023,7 +1025,11 @@ t_type([#xmlElement{name = abstype, content = Es}]) ->
t_type([#xmlElement{name = union, content = Es}]) ->
t_union(Es);
t_type([#xmlElement{name = record, content = Es}]) ->
- t_record(Es).
+ t_record(Es);
+t_type([#xmlElement{name = map, content = Es}]) ->
+ t_map(Es);
+t_type([#xmlElement{name = map_field, content = Es}]) ->
+ t_map_field(Es).
t_var(E) ->
[get_attrval(name, E)].
@@ -1046,6 +1052,9 @@ t_nil() ->
t_list(Es) ->
["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+t_nonempty_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ [", ...]"].
+
t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
@@ -1058,6 +1067,12 @@ t_record([E|Es]) ->
t_field(#xmlElement{name=field, content=[Atom,Type]}) ->
[get_attrval(value, Atom), "="] ++ t_utype_elem(Type).
+t_map(Es) ->
+ ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_map_field([K,V]) ->
+ [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
+
t_abstype(Es) ->
case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of
{Mod,Type} ->
diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl
index 3240edd68e..886194598f 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -433,7 +433,7 @@ t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
t_map() ->
- ["#{}"].
+ ["map()"].
t_fun(Es) ->
["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
diff --git a/lib/erl_docgen/src/erl_docgen.app.src b/lib/erl_docgen/src/erl_docgen.app.src
index daad172106..e2830b2692 100644
--- a/lib/erl_docgen/src/erl_docgen.app.src
+++ b/lib/erl_docgen/src/erl_docgen.app.src
@@ -8,7 +8,7 @@
},
{registered,[]},
{applications, [kernel,stdlib]},
- {env, []
- }
+ {env, []},
+ {runtime_dependencies, ["xmerl-1.3.7","stdlib-2.0","edoc-0.7.13","erts-6.0"]}
]
}.
diff --git a/lib/erl_docgen/src/erl_docgen.appup.src b/lib/erl_docgen/src/erl_docgen.appup.src
index 54a63833e6..972a881c41 100644
--- a/lib/erl_docgen/src/erl_docgen.appup.src
+++ b/lib/erl_docgen/src/erl_docgen.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, erl_docgen}]}],
+ [{<<".*">>,[{restart_application, erl_docgen}]}]
+}.
diff --git a/lib/erl_docgen/test/Makefile b/lib/erl_docgen/test/Makefile
new file mode 100644
index 0000000000..9f4cc04105
--- /dev/null
+++ b/lib/erl_docgen/test/Makefile
@@ -0,0 +1,65 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ erl_docgen_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/erl_docgen_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) erl_docgen.spec "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+
+release_docs_spec:
diff --git a/lib/erl_docgen/test/erl_docgen.spec b/lib/erl_docgen/test/erl_docgen.spec
new file mode 100644
index 0000000000..98833614df
--- /dev/null
+++ b/lib/erl_docgen/test/erl_docgen.spec
@@ -0,0 +1 @@
+{suites,"../erl_docgen_test",all}.
diff --git a/lib/erl_docgen/test/erl_docgen_SUITE.erl b/lib/erl_docgen/test/erl_docgen_SUITE.erl
new file mode 100644
index 0000000000..20fbc1229b
--- /dev/null
+++ b/lib/erl_docgen/test/erl_docgen_SUITE.erl
@@ -0,0 +1,50 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(erl_docgen_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [app, appup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the erl_docgen app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(erl_docgen).
+
+appup() ->
+ [{doc, "Test that the erl_docgen appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(erl_docgen).
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index cda8671cfd..0f89922275 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.3.4.1
+ERL_DOCGEN_VSN = 0.3.5
diff --git a/lib/erl_interface/aclocal.m4 b/lib/erl_interface/aclocal.m4
index 46b30a16b3..2b47f7c4bc 100644
--- a/lib/erl_interface/aclocal.m4
+++ b/lib/erl_interface/aclocal.m4
@@ -74,6 +74,21 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re
AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)])
+dnl Cross compilation variables for OSE
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
+
])
AC_DEFUN(ERL_XCOMP_SYSROOT_INIT,
@@ -488,6 +503,8 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support,
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -500,6 +517,8 @@ else
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -728,6 +747,12 @@ if test "X$host_os" = "Xwin32"; then
THR_LIBS=
THR_LIB_NAME=win32_threads
THR_LIB_TYPE=win32_threads
+elif test "X$host_os" = "Xose"; then
+ AC_MSG_RESULT(yes)
+ THR_DEFS="-DOSE_THREADS"
+ THR_LIBS=
+ THR_LIB_NAME=ose_threads
+ THR_LIB_TYPE=ose_threads
else
AC_MSG_RESULT(no)
THR_DEFS=
@@ -1078,9 +1103,22 @@ case "$THR_LIB_NAME" in
test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes
;;
- pthread)
- ETHR_THR_LIB_BASE_DIR=pthread
- AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ pthread|ose_threads)
+ case "$THR_LIB_NAME" in
+ pthread)
+ ETHR_THR_LIB_BASE_DIR=pthread
+ AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ ;;
+ ose_threads)
+ AC_DEFINE(ETHR_OSE_THREADS, 1,
+ [Define if you have OSE style threads])
+ ETHR_THR_LIB_BASE_DIR=ose
+ AC_CHECK_HEADER(ose_spi/ose_spi.h,
+ AC_DEFINE(HAVE_OSE_SPI_H, 1,
+ [Define if you have the "ose_spi/ose_spi.h" header file.]))
+ ;;
+ esac
+ if test "x$THR_LIB_NAME" == "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1139,6 +1177,7 @@ case "$THR_LIB_NAME" in
*) ;;
esac
+ fi
dnl We sometimes need ETHR_DEFS in order to find certain headers
dnl (at least for pthread.h on osf1).
saved_cppflags="$CPPFLAGS"
@@ -1151,7 +1190,6 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for headers
dnl
-
AC_CHECK_HEADER(pthread.h, \
AC_DEFINE(ETHR_HAVE_PTHREAD_H, 1, \
[Define if you have the <pthread.h> header file.]))
@@ -1184,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
-
+ if test "x$THR_LIB_NAME" == "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
@@ -1311,6 +1349,8 @@ case "$THR_LIB_NAME" in
AC_MSG_RESULT([$linux_futex])
test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.])
+ fi
+
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(long long)
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index ab185c9179..90495eebd6 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2013</year>
+ <year>2001</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -417,6 +417,26 @@ ei_x_encode_empty_list(&amp;x);
</desc>
</func>
<func>
+ <name><ret>int</ret><nametext>ei_encode_map_header(char *buf, int *index, int arity)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_map_header(ei_x_buff* x, int arity)</nametext></name>
+ <fsummary>Encode a map</fsummary>
+ <desc>
+ <p>This function encodes a map header, with a specified arity. The next
+ <c>arity*2</c> terms encoded will be the keys and values of the map
+ encoded in the following order: <c>K1, V1, K2, V2, ..., Kn, Vn</c>.
+ </p>
+ <p>E.g. to encode the map <c>#{a => "Apple", b => "Banana"}</c>:</p>
+ <pre>
+ei_x_encode_map_header(&amp;x, 2);
+ei_x_encode_atom(&amp;x, "a");
+ei_x_encode_string(&amp;x, "Apple");
+ei_x_encode_atom(&amp;x, "b");
+ei_x_encode_string(&amp;x, "Banana");
+ </pre>
+ <p>A correctly encoded map can not have duplicate keys.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name>
<fsummary>Fetch the type and size of an encoded term</fsummary>
<desc>
@@ -638,6 +658,18 @@ ei_x_encode_empty_list(&amp;x);
</desc>
</func>
<func>
+ <name><ret>int</ret><nametext>ei_decode_map_header(const char *buf, int *index, int *arity)</nametext></name>
+ <fsummary>Decode a map</fsummary>
+ <desc>
+ <p>This function decodes a map header from the binary
+ format. The number of key-value pairs is returned in
+ <c>*arity</c>. Keys and values follow in the following order:
+ <c>K1, V1, K2, V2, ..., Kn, Vn</c>. This makes a total of
+ <c>arity*2</c> terms. If <c>arity</c> is zero, it's an empty map.
+ A correctly encoded map does not have duplicate keys.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>ei_decode_ei_term(const char* buf, int* index, ei_term* term)</nametext></name>
<fsummary>Decode a term, without prior knowledge of type</fsummary>
<desc>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index 9b83385a46..a3eb437f88 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2014. 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
@@ -131,6 +131,7 @@
#define ERL_SMALL_BIG_EXT 'n'
#define ERL_LARGE_BIG_EXT 'o'
#define ERL_NEW_FUN_EXT 'p'
+#define ERL_MAP_EXT 't'
#define ERL_FUN_EXT 'u'
#define ERL_NEW_CACHE 'N' /* c nodes don't know these two */
@@ -467,6 +468,8 @@ int ei_encode_list_header(char *buf, int *index, int arity);
int ei_x_encode_list_header(ei_x_buff* x, long n);
#define ei_encode_empty_list(buf,i) ei_encode_list_header(buf,i,0)
int ei_x_encode_empty_list(ei_x_buff* x);
+int ei_encode_map_header(char *buf, int *index, int arity);
+int ei_x_encode_map_header(ei_x_buff* x, long n);
/*
* ei_get_type() returns the type and "size" of the item at
@@ -507,6 +510,7 @@ int ei_decode_term(const char *buf, int *index, void *t); /* ETERM** actually */
int ei_decode_trace(const char *buf, int *index, erlang_trace *p);
int ei_decode_tuple_header(const char *buf, int *index, int *arity);
int ei_decode_list_header(const char *buf, int *index, int *arity);
+int ei_decode_map_header(const char *buf, int *index, int *arity);
/*
* ei_decode_ei_term() returns 1 if term is decoded, 0 if term is OK,
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index c9aa28812c..2e8418d61e 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2014. 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
@@ -1166,7 +1166,11 @@ static unsigned int gen_challenge(void)
uname(&s.name);
s.cpu = clock();
s.pid = getpid();
+#ifndef __ANDROID__
s.hid = gethostid();
+#else
+ s.hid = 0;
+#endif
s.uid = getuid();
s.gid = getgid();
@@ -1336,7 +1340,8 @@ static int send_name_or_challenge(int fd, char *nodename,
| DFLAG_NEW_FUN_TAGS
| DFLAG_NEW_FLOATS
| DFLAG_SMALL_ATOM_TAGS
- | DFLAG_UTF8_ATOMS));
+ | DFLAG_UTF8_ATOMS
+ | DFLAG_MAP_TAG));
if (f_chall)
put32be(s, challenge);
memcpy(s, nodename, strlen(nodename));
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index 42ab9b58d7..8fab47a787 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. 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
@@ -104,6 +104,7 @@ extern int h_errno;
#define DFLAG_NEW_FLOATS 0x800
#define DFLAG_SMALL_ATOM_TAGS 0x4000
#define DFLAG_UTF8_ATOMS 0x10000
+#define DFLAG_MAP_TAG 0x20000
ei_cnode *ei_fd_to_cnode(int fd);
int ei_distversion(int fd);
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
index 74dcba61a7..cffcac801c 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -642,7 +642,7 @@ struct hostent *ei_gethostbyname_r(const char *name,
#ifndef HAVE_GETHOSTBYNAME_R
return my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
#else
-#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__))
+#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__) || defined(__ANDROID__))
struct hostent *result;
gethostbyname_r(name, hostp, buffer, buflen, &result, h_errnop);
diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c
index 553266471c..2260394da1 100644
--- a/lib/erl_interface/src/decode/decode_skip.c
+++ b/lib/erl_interface/src/decode/decode_skip.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2014. 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
@@ -61,7 +61,13 @@ int ei_skip_term(const char* buf, int* index)
break;
case ERL_SMALL_TUPLE_EXT:
case ERL_LARGE_TUPLE_EXT:
- if (ei_decode_tuple_header(buf, index, &n) < 0) return -1;
+ if (ei_decode_tuple_header(buf, index, &n) < 0) return -1;
+ for (i = 0; i < n; ++i)
+ ei_skip_term(buf, index);
+ break;
+ case ERL_MAP_EXT:
+ if (ei_decode_map_header(buf, index, &n) < 0) return -1;
+ n *= 2;
for (i = 0; i < n; ++i)
ei_skip_term(buf, index);
break;
diff --git a/lib/erl_interface/src/decode/decode_tuple_header.c b/lib/erl_interface/src/decode/decode_tuple_header.c
index c0ba14ea47..698be1b97a 100644
--- a/lib/erl_interface/src/decode/decode_tuple_header.c
+++ b/lib/erl_interface/src/decode/decode_tuple_header.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2014. 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
@@ -45,3 +45,24 @@ int ei_decode_tuple_header(const char *buf, int *index, int *arity)
return 0;
}
+
+int ei_decode_map_header(const char *buf, int *index, int *arity)
+{
+ const char *s = buf + *index;
+ const char *s0 = s;
+ int i;
+
+ switch ((i=get8(s))) {
+ case ERL_MAP_EXT:
+ if (arity) *arity = get32be(s);
+ else s += 4;
+ break;
+
+ default:
+ return -1;
+ }
+
+ *index += s-s0;
+
+ return 0;
+}
diff --git a/lib/erl_interface/src/encode/encode_tuple_header.c b/lib/erl_interface/src/encode/encode_tuple_header.c
index 97a3d1f808..5b11e60447 100644
--- a/lib/erl_interface/src/encode/encode_tuple_header.c
+++ b/lib/erl_interface/src/encode/encode_tuple_header.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2014. 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
@@ -47,3 +47,20 @@ int ei_encode_tuple_header(char *buf, int *index, int arity)
return 0;
}
+int ei_encode_map_header(char *buf, int *index, int arity)
+{
+ char *s = buf + *index;
+ char *s0 = s;
+
+ if (arity < 0) return -1;
+
+ if (!buf) s += 5;
+ else {
+ put8(s,ERL_MAP_EXT);
+ put32be(s,arity);
+ }
+
+ *index += s-s0;
+
+ return 0;
+}
diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c
index ce5ae5b19d..2e7317f781 100644
--- a/lib/erl_interface/src/misc/ei_decode_term.c
+++ b/lib/erl_interface/src/misc/ei_decode_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. 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
@@ -100,6 +100,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
term->size = get16be(s);
return 0;
case ERL_LIST_EXT:
+ case ERL_MAP_EXT:
term->arity = get32be(s);
break;
case ERL_BINARY_EXT:
diff --git a/lib/erl_interface/src/misc/ei_x_encode.c b/lib/erl_interface/src/misc/ei_x_encode.c
index 14d0b56b8f..10542c88a5 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. 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
@@ -206,6 +206,16 @@ int ei_x_encode_tuple_header(ei_x_buff* x, long n)
return ei_encode_tuple_header(x->buff, &x->index, n);
}
+int ei_x_encode_map_header(ei_x_buff* x, long n)
+{
+ int i = x->index;
+ if (ei_encode_map_header(NULL, &i, n) == -1)
+ return -1;
+ if (!x_fix_buff(x, i))
+ return -1;
+ return ei_encode_map_header(x->buff, &x->index, n);
+}
+
int ei_x_encode_atom(ei_x_buff* x, const char* s)
{
return ei_x_encode_atom_len_as(x, s, strlen(s), ERLANG_LATIN1, ERLANG_LATIN1);
diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.c b/lib/erl_interface/test/all_SUITE_data/ei_runner.c
index cdf32b48c4..196a77dce5 100644
--- a/lib/erl_interface/test/all_SUITE_data/ei_runner.c
+++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. 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
@@ -182,6 +182,10 @@ char *read_packet(int *len)
return io_buf;
}
+void free_packet(char* packet)
+{
+ free(packet);
+}
/***********************************************************************
* S e n d i n g r e p l i e s
diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.h b/lib/erl_interface/test/all_SUITE_data/ei_runner.h
index 96d6a1cbf7..a037341d57 100644
--- a/lib/erl_interface/test/all_SUITE_data/ei_runner.h
+++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. 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
@@ -44,6 +44,7 @@ void run_tests(char* argv0, TestCase cases[], unsigned number);
int get_bin_term(ei_x_buff* x, ei_term* term);
char *read_packet(int *len);
+void free_packet(char*);
/*
* Sending replies.
diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index a676e59470..3f6cbbe186 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -603,9 +603,9 @@ TESTCASE(test_ei_decode_misc)
/*
EI_DECODE_0(decode_version);
*/
- EI_DECODE_2(decode_double, 32, double, 0.0);
- EI_DECODE_2(decode_double, 32, double, -1.0);
- EI_DECODE_2(decode_double, 32, double, 1.0);
+ EI_DECODE_2(decode_double, 9, double, 0.0);
+ EI_DECODE_2(decode_double, 9, double, -1.0);
+ EI_DECODE_2(decode_double, 9, double, 1.0);
EI_DECODE_2(decode_boolean, 8, int, 0);
EI_DECODE_2(decode_boolean, 7, int, 1);
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
index c7830f58f2..7caec6ac04 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -127,6 +127,15 @@ test_ei_decode_encode(Config) when is_list(Config) ->
send_rec(P, mk_ref({Atom,1}, [262143, 8723648, 24097245])),
void
end || Atom <- unicode_atom_data()],
+
+ send_rec(P, {}),
+ send_rec(P, {atom, Pid, Port, Ref}),
+ send_rec(P, [atom, Pid, Port, Ref]),
+ send_rec(P, [atom | Fun]),
+ send_rec(P, #{}),
+ send_rec(P, #{key => value}),
+ send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})),
+
?line runner:recv_eot(P),
ok.
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
index 317e5edecd..fcf546105b 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2014. 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
@@ -32,9 +32,33 @@
/*#define MESSAGE(FMT,A1,A2) message(FMT,A1,A2)*/
#define MESSAGE(FMT,A1,A2)
-typedef int decodeFT(const char *buf, int *index, void*);
-typedef int encodeFT(char *buf, int *index, void*);
-typedef int x_encodeFT(ei_x_buff*, void*);
+
+typedef struct
+{
+ char name[MAXATOMLEN_UTF8];
+ erlang_char_encoding enc;
+}my_atom;
+
+struct my_obj {
+ union {
+ erlang_fun fun;
+ erlang_pid pid;
+ erlang_port port;
+ erlang_ref ref;
+ erlang_trace trace;
+ erlang_big big;
+ my_atom atom;
+
+ int arity;
+ }u;
+
+ int nterms; /* 0 for non-containers */
+ char* startp; /* container start position in decode buffer */
+};
+
+typedef int decodeFT(const char *buf, int *index, struct my_obj*);
+typedef int encodeFT(char *buf, int *index, struct my_obj*);
+typedef int x_encodeFT(ei_x_buff*, struct my_obj*);
struct Type {
char* name;
@@ -44,11 +68,36 @@ struct Type {
x_encodeFT* ei_x_encode_fp;
};
-typedef struct
-{
- char name[MAXATOMLEN_UTF8];
- erlang_char_encoding enc;
-}my_atom;
+
+struct Type fun_type = {
+ "fun", "erlang_fun", (decodeFT*)ei_decode_fun,
+ (encodeFT*)ei_encode_fun, (x_encodeFT*)ei_x_encode_fun
+};
+
+struct Type pid_type = {
+ "pid", "erlang_pid", (decodeFT*)ei_decode_pid,
+ (encodeFT*)ei_encode_pid, (x_encodeFT*)ei_x_encode_pid
+};
+
+struct Type port_type = {
+ "port", "erlang_port", (decodeFT*)ei_decode_port,
+ (encodeFT*)ei_encode_port, (x_encodeFT*)ei_x_encode_port
+};
+
+struct Type ref_type = {
+ "ref", "erlang_ref", (decodeFT*)ei_decode_ref,
+ (encodeFT*)ei_encode_ref, (x_encodeFT*)ei_x_encode_ref
+};
+
+struct Type trace_type = {
+ "trace", "erlang_trace", (decodeFT*)ei_decode_trace,
+ (encodeFT*)ei_encode_trace, (x_encodeFT*)ei_x_encode_trace
+};
+
+struct Type big_type = {
+ "big", "erlang_big", (decodeFT*)ei_decode_big,
+ (encodeFT*)ei_encode_big, (x_encodeFT*)ei_x_encode_big
+};
int ei_decode_my_atom(const char *buf, int *index, my_atom* a)
{
@@ -64,130 +113,274 @@ int ei_x_encode_my_atom(ei_x_buff* x, my_atom* a)
return ei_x_encode_atom_as(x, a->name, ERLANG_UTF8, a->enc);
}
+struct Type my_atom_type = {
+ "atom", "my_atom", (decodeFT*)ei_decode_my_atom,
+ (encodeFT*)ei_encode_my_atom, (x_encodeFT*)ei_x_encode_my_atom
+};
+
+
+int my_decode_tuple_header(const char *buf, int *index, struct my_obj* obj)
+{
+ int ret = ei_decode_tuple_header(buf, index, &obj->u.arity);
+ if (ret == 0 && obj)
+ obj->nterms = obj->u.arity;
+ return ret;
+}
+
+int my_encode_tuple_header(char *buf, int *index, struct my_obj* obj)
+{
+ return ei_encode_tuple_header(buf, index, obj->u.arity);
+}
+int my_x_encode_tuple_header(ei_x_buff* x, struct my_obj* obj)
+{
+ return ei_x_encode_tuple_header(x, (long)obj->u.arity);
+}
+
+struct Type tuple_type = {
+ "tuple_header", "arity", my_decode_tuple_header,
+ my_encode_tuple_header, my_x_encode_tuple_header
+};
+
+
+int my_decode_list_header(const char *buf, int *index, struct my_obj* obj)
+{
+ int ret = ei_decode_list_header(buf, index, &obj->u.arity);
+ if (ret == 0 && obj) {
+ obj->nterms = obj->u.arity + 1;
+ }
+ return ret;
+}
+int my_encode_list_header(char *buf, int *index, struct my_obj* obj)
+{
+ return ei_encode_list_header(buf, index, obj->u.arity);
+}
+int my_x_encode_list_header(ei_x_buff* x, struct my_obj* obj)
+{
+ return ei_x_encode_list_header(x, (long)obj->u.arity);
+}
+
+struct Type list_type = {
+ "list_header", "arity", my_decode_list_header,
+ my_encode_list_header, my_x_encode_list_header
+};
+
+
+int my_decode_nil(const char *buf, int *index, struct my_obj* dummy)
+{
+ int type, size, ret;
+ ret = ei_get_type(buf, index, &type, &size);
+ (*index)++;
+ return ret ? ret : !(type == ERL_NIL_EXT);
+
+}
+int my_encode_nil(char *buf, int *index, struct my_obj* dummy)
+{
+ return ei_encode_empty_list(buf, index);
+}
+
+int my_x_encode_nil(ei_x_buff* x, struct my_obj* dummy)
+{
+ return ei_x_encode_empty_list(x);
+}
+
+struct Type nil_type = {
+ "empty_list", "nil", my_decode_nil,
+ my_encode_nil, my_x_encode_nil
+};
+
+int my_decode_map_header(const char *buf, int *index, struct my_obj* obj)
+{
+ int ret = ei_decode_map_header(buf, index, &obj->u.arity);
+ if (ret == 0 && obj)
+ obj->nterms = obj->u.arity * 2;
+ return ret;
+}
+int my_encode_map_header(char *buf, int *index, struct my_obj* obj)
+{
+ return ei_encode_map_header(buf, index, obj->u.arity);
+}
+int my_x_encode_map_header(ei_x_buff* x, struct my_obj* obj)
+{
+ return ei_x_encode_map_header(x, (long)obj->u.arity);
+}
+
+struct Type map_type = {
+ "map_header", "arity", my_decode_map_header,
+ my_encode_map_header, my_x_encode_map_header
+};
+
+
#define BUFSZ 2000
-void decode_encode(struct Type* t, void* obj)
+void decode_encode(struct Type** tv, int nobj)
{
- char *buf;
- char buf2[BUFSZ];
- int size1 = 0;
- int size2 = 0;
- int size3 = 0;
- int err;
+ struct my_obj objv[10];
+ int oix = 0;
+ char* packet;
+ char* inp;
+ char* outp;
+ char out_buf[BUFSZ];
+ int size1, size2, size3;
+ int err, i;
ei_x_buff arg;
- MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
- buf = read_packet(NULL);
- err = t->ei_decode_fp(buf+1, &size1, NULL);
- if (err != 0) {
- if (err != -1) {
- fail("decode returned non zero but not -1");
- } else {
- fail("decode returned non zero");
+ packet = read_packet(NULL);
+ inp = packet+1;
+ outp = out_buf;
+ ei_x_new(&arg);
+ for (i=0; i<nobj; i++) {
+ struct Type* t = tv[i];
+
+ MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
+
+ size1 = 0;
+ err = t->ei_decode_fp(inp, &size1, NULL);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 < 1) {
+ fail("size is < 1");
+ return;
}
- return;
- }
- if (size1 < 1) {
- fail("size is < 1");
- return;
- }
- if (size1 > BUFSZ) {
- fail("size is > BUFSZ");
- return;
- }
+ if (size1 > BUFSZ) {
+ fail("size is > BUFSZ");
+ return;
+ }
- err = t->ei_decode_fp(buf+1, &size2, obj);
- if (err != 0) {
- if (err != -1) {
- fail("decode returned non zero but not -1");
- } else {
- fail("decode returned non zero");
+ size2 = 0;
+ objv[oix].nterms = 0;
+ objv[oix].startp = inp;
+ err = t->ei_decode_fp(inp, &size2, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode sizes differs");
+ return;
}
- return;
- }
- if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("decode sizes differs");
- return;
- }
- size2 = 0;
- err = ei_skip_term(buf+1, &size2);
- if (err != 0) {
- fail("ei_skip_term returned non zero");
- return;
- }
- if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("skip size differs");
- return;
- }
+ if (!objv[oix].nterms) {
+ size2 = 0;
+ err = ei_skip_term(inp, &size2);
+ if (err != 0) {
+ fail("ei_skip_term returned non zero");
+ return;
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("skip size differs");
+ return;
+ }
+ }
- MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
- size2 = 0;
- err = t->ei_encode_fp(NULL, &size2, obj);
- if (err != 0) {
- if (err != -1) {
- fail("size calculation returned non zero but not -1");
+ MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
+ size2 = 0;
+ err = t->ei_encode_fp(NULL, &size2, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("size calculation returned non zero but not -1");
+ return;
+ } else {
+ fail("size calculation returned non zero");
+ return;
+ }
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode and encode size differs when buf is NULL");
return;
- } else {
- fail("size calculation returned non zero");
+ }
+ MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
+ size3 = 0;
+ err = t->ei_encode_fp(outp, &size3, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
return;
}
- }
- if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("decode and encode size differs when buf is NULL");
- return;
- }
- MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
- err = t->ei_encode_fp(buf2, &size3, obj);
- if (err != 0) {
- if (err != -1) {
- fail("returned non zero but not -1");
- } else {
- fail("returned non zero");
+ if (size1 != size3) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
+ fail("decode and encode size differs");
+ return;
}
- return;
- }
- if (size1 != size3) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
- fail("decode and encode size differs");
- return;
- }
- send_buffer(buf2, size1);
- MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
- ei_x_new(&arg);
- err = t->ei_x_encode_fp(&arg, obj);
- if (err != 0) {
- if (err != -1) {
- fail("returned non zero but not -1");
- } else {
- fail("returned non zero");
+ MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
+ err = t->ei_x_encode_fp(&arg, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
+ ei_x_free(&arg);
+ return;
}
- ei_x_free(&arg);
- return;
+ if (arg.index < 1) {
+ fail("size is < 1");
+ ei_x_free(&arg);
+ return;
+ }
+
+ inp += size1;
+ outp += size1;
+
+ if (objv[oix].nterms) { /* container term */
+ if (++oix >= sizeof(objv)/sizeof(*objv))
+ fail("Term too deep");
+ }
+ else { /* "leaf" term */
+ while (oix > 0) {
+ if (--(objv[oix - 1].nterms) == 0) {
+ /* last element in container */
+ --oix;
+
+ size2 = 0;
+ err = ei_skip_term(objv[oix].startp, &size2);
+ if (err != 0) {
+ fail("ei_skip_term returned non zero");
+ return;
+ }
+ if (objv[oix].startp + size2 != inp) {
+ MESSAGE("size1 = %d, size2 = %d\n", size1, size2);
+ fail("container skip size differs");
+ return;
+ }
+ }
+ else
+ break; /* more elements in container */
+ }
+ }
+
}
- if (arg.index < 1) {
- fail("size is < 1");
- ei_x_free(&arg);
- return;
+ if (oix > 0) {
+ fail("Container not complete");
}
+ send_buffer(out_buf, outp - out_buf);
send_buffer(arg.buff, arg.index);
ei_x_free(&arg);
+ free_packet(packet);
}
+void decode_encode_one(struct Type* t)
+{
+ decode_encode(&t, 1);
+}
-#define EI_DECODE_ENCODE(TYPE, ERLANG_TYPE) { \
- struct Type type_struct = {#TYPE, #ERLANG_TYPE, \
- (decodeFT*)ei_decode_##TYPE, \
- (encodeFT*)ei_encode_##TYPE, \
- (x_encodeFT*)ei_x_encode_##TYPE }; \
- ERLANG_TYPE type_obj; \
- decode_encode(&type_struct, &type_obj); \
- }
void decode_encode_big(struct Type* t)
@@ -274,14 +467,6 @@ void decode_encode_big(struct Type* t)
ei_free_big(p);
}
-#define EI_DECODE_ENCODE_BIG(TYPE, ERLANG_TYPE) { \
- struct Type type_struct = {#TYPE, #ERLANG_TYPE, \
- (decodeFT*)ei_decode_##TYPE, \
- (encodeFT*)ei_encode_##TYPE, \
- (x_encodeFT*)ei_x_encode_##TYPE }; \
- decode_encode_big(&type_struct); \
- }
-
/* ******************************************************************** */
@@ -290,34 +475,63 @@ TESTCASE(test_ei_decode_encode)
{
int i;
- EI_DECODE_ENCODE(fun , erlang_fun);
- EI_DECODE_ENCODE(pid , erlang_pid);
- EI_DECODE_ENCODE(port , erlang_port);
- EI_DECODE_ENCODE(ref , erlang_ref);
- EI_DECODE_ENCODE(trace, erlang_trace);
+ decode_encode_one(&fun_type);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
+ decode_encode_one(&trace_type);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
/* Test large node containers... */
- EI_DECODE_ENCODE(pid , erlang_pid);
- EI_DECODE_ENCODE(port , erlang_port);
- EI_DECODE_ENCODE(ref , erlang_ref);
- EI_DECODE_ENCODE(pid , erlang_pid);
- EI_DECODE_ENCODE(port , erlang_port);
- EI_DECODE_ENCODE(ref , erlang_ref);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
/* Unicode atoms */
for (i=0; i<24; i++) {
- EI_DECODE_ENCODE(my_atom, my_atom);
- EI_DECODE_ENCODE(pid, erlang_pid);
- EI_DECODE_ENCODE(port, erlang_port);
- EI_DECODE_ENCODE(ref, erlang_ref);
+ decode_encode_one(&my_atom_type);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
+ }
+
+ decode_encode_one(&tuple_type); /* {} */
+ {
+ struct Type* tpl[] = { &tuple_type, &my_atom_type, &pid_type, &port_type, &ref_type };
+ decode_encode(tpl, 5);
+ }
+
+ {
+ struct Type* list[] = { &list_type, &my_atom_type, &pid_type, &port_type, &ref_type, &nil_type };
+ decode_encode(list, 6);
+ }
+ {
+ struct Type* list[] = { &list_type, &my_atom_type, &fun_type };
+ decode_encode(list, 3);
+ }
+ decode_encode_one(&map_type); /* #{} */
+ { /* #{atom => atom}*/
+ struct Type* map[] = { &map_type, &my_atom_type, &my_atom_type };
+ decode_encode(map, 3);
+ }
+
+ { /* #{atom => atom, atom => pid, port => ref }*/
+ struct Type* map[] = { &map_type,
+ &my_atom_type, &my_atom_type,
+ &my_atom_type, &pid_type,
+ &port_type, &ref_type
+ };
+ decode_encode(map, 7);
}
report(1);
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index f386ce09a8..8731283265 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1,2 @@
-EI_VSN = 3.7.15
+EI_VSN = 3.7.16
+ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/et/src/et.app.src b/lib/et/src/et.app.src
index f7189a4197..c26d9320d8 100644
--- a/lib/et/src/et.app.src
+++ b/lib/et/src/et.app.src
@@ -31,5 +31,7 @@
]},
{registered, [et_collector]},
{applications, [stdlib, kernel]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.8.14",
+ "kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/et/src/et.appup.src b/lib/et/src/et.appup.src
index b6344a9387..5f15b00386 100644
--- a/lib/et/src/et.appup.src
+++ b/lib/et/src/et.appup.src
@@ -1,8 +1,7 @@
-%% This is an -*- erlang -*- file.
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -16,8 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-
{"%VSN%",
- [ ]
+ [{<<".*">>,[{restart_application, et}]}],
+ [{<<".*">>,[{restart_application, et}]}]
}.
-
diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile
index 3817079b5f..1be5aac3fd 100644
--- a/lib/et/test/Makefile
+++ b/lib/et/test/Makefile
@@ -25,6 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
ett \
+ et_SUITE \
et_wx_SUITE \
et_test_lib
diff --git a/lib/et/test/et_SUITE.erl b/lib/et/test/et_SUITE.erl
new file mode 100644
index 0000000000..e0bce41e15
--- /dev/null
+++ b/lib/et/test/et_SUITE.erl
@@ -0,0 +1,50 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(et_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [app, appup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the et app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(et).
+
+appup() ->
+ [{doc, "Test that the et appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(et).
diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk
index 282991aa49..a47be678ca 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1 +1 @@
-ET_VSN = 1.4.4.5
+ET_VSN = 1.5
diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src
index 431abac98b..7a3978e200 100644
--- a/lib/eunit/src/eunit.app.src
+++ b/lib/eunit/src/eunit.app.src
@@ -14,7 +14,9 @@
eunit_striptests,
eunit_surefire,
eunit_test,
+ eunit_tests,
eunit_tty]},
{registered,[]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/eunit/src/eunit.appup.src b/lib/eunit/src/eunit.appup.src
index 54a63833e6..18934d44c2 100644
--- a/lib/eunit/src/eunit.appup.src
+++ b/lib/eunit/src/eunit.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, eunit}]}],
+ [{<<".*">>,[{restart_application, eunit}]}]
+}.
diff --git a/lib/eunit/src/eunit_serial.erl b/lib/eunit/src/eunit_serial.erl
index 80e79116e3..1a0179a5df 100644
--- a/lib/eunit/src/eunit_serial.erl
+++ b/lib/eunit/src/eunit_serial.erl
@@ -56,9 +56,9 @@
%% future use, and/or cancel the current item and possibly one or more
%% of its parent groups.
--record(state, {listeners :: set(),
- cancelled = eunit_lib:trie_new() :: gb_tree(),
- messages = dict:new() :: dict()}).
+-record(state, {listeners :: sets:set(),
+ cancelled = eunit_lib:trie_new() :: gb_trees:tree(),
+ messages = dict:new() :: dict:dict()}).
start(Pids) ->
spawn(fun () -> serializer(Pids) end).
diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl
index 47c2435d63..d13dc73923 100644
--- a/lib/eunit/test/eunit_SUITE.erl
+++ b/lib/eunit/test/eunit_SUITE.erl
@@ -19,14 +19,15 @@
-module(eunit_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,eunit_test/1]).
+ init_per_group/2,end_per_group/2,
+ app_test/1,appup_test/1,eunit_test/1]).
-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [eunit_test].
+ [app_test, appup_test, eunit_test].
groups() ->
[].
@@ -43,6 +44,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+app_test(Config) when is_list(Config) ->
+ ok = ?t:app_test(eunit).
+
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(eunit).
eunit_test(Config) when is_list(Config) ->
ok = file:set_cwd(code:lib_dir(eunit)),
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 8f816b3b94..f04c0536fe 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.6
+EUNIT_VSN = 2.2.7
diff --git a/lib/gs/doc/src/gs.xml b/lib/gs/doc/src/gs.xml
index e0d7fb20da..7b589d002d 100644
--- a/lib/gs/doc/src/gs.xml
+++ b/lib/gs/doc/src/gs.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -39,7 +39,7 @@
graphical user interface.
</p>
<p>
- GS is deprecated and will be removed in the R16 release.
+ GS is deprecated and will be removed in the 18.0 release.
</p>
</warning>
<p>The Graphics System, GS, is easy to learn and
diff --git a/lib/gs/src/Makefile b/lib/gs/src/Makefile
index 0a63d5466e..f0200caf01 100644
--- a/lib/gs/src/Makefile
+++ b/lib/gs/src/Makefile
@@ -72,7 +72,7 @@ IMAGES=../priv/bitmap/fup.bm
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_obsolete_guard
+ERL_COMPILE_FLAGS += +warn_obsolete_guard -Werror
# ----------------------------------------------------
# Targets
diff --git a/lib/gs/src/gs.app.src b/lib/gs/src/gs.app.src
index c83c9b54d7..c6f88e5144 100644
--- a/lib/gs/src/gs.app.src
+++ b/lib/gs/src/gs.app.src
@@ -10,4 +10,5 @@
gstk_window,tcl2erl,tool_file_dialog,tool_utils,
gs_packer,gse]},
{registered, [gs_frontend]},
- {applications, [kernel, stdlib]}]}.
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/gs/src/gs.appup.src b/lib/gs/src/gs.appup.src
index 54a63833e6..2cb3b547e8 100644
--- a/lib/gs/src/gs.appup.src
+++ b/lib/gs/src/gs.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, gs}]}],
+ [{<<".*">>,[{restart_application, gs}]}]
+}.
diff --git a/lib/gs/src/gstk_generic.erl b/lib/gs/src/gstk_generic.erl
index 9b0efd07c1..573b4e05bb 100644
--- a/lib/gs/src/gstk_generic.erl
+++ b/lib/gs/src/gstk_generic.erl
@@ -323,11 +323,11 @@ handle_external_opt_call([Opt|Options],Gstkid,TkW,DB,ExtraArg,ExtRes,S,P,C) ->
end.
handle_external_read(Res) ->
- _ = case Res of
- {bad_result,{Objtype,Reason,Option}} ->
- {error,{Objtype,Reason,Option}};
- _ -> ok
- end,
+ %% We have removed dead code here that attempted to translate
+ %% a bad return value from {bad_result,{A,B,C}} to {error,{A,B,C}}.
+ %% Since the gs application is deprecated, we don't want to introduce
+ %% a potential incompatibility; thus we have removed the dead code
+ %% instead of correcting it.
Res.
%%----------------------------------------------------------------------
diff --git a/lib/gs/test/Makefile b/lib/gs/test/Makefile
new file mode 100644
index 0000000000..493770745f
--- /dev/null
+++ b/lib/gs/test/Makefile
@@ -0,0 +1,65 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ gs_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/gs_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) gs.spec "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+
+release_docs_spec:
diff --git a/lib/gs/test/gs.spec b/lib/gs/test/gs.spec
new file mode 100644
index 0000000000..46e6b2061e
--- /dev/null
+++ b/lib/gs/test/gs.spec
@@ -0,0 +1 @@
+{suites,"../gs_test",all}.
diff --git a/lib/gs/test/gs_SUITE.erl b/lib/gs/test/gs_SUITE.erl
new file mode 100644
index 0000000000..01ce90df0b
--- /dev/null
+++ b/lib/gs/test/gs_SUITE.erl
@@ -0,0 +1,50 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(gs_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [app, appup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the gs app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(gs, tolerant).
+
+appup() ->
+ [{doc, "Test that the gs appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(gs).
diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk
index 5c18153c34..96786b300c 100644
--- a/lib/gs/vsn.mk
+++ b/lib/gs/vsn.mk
@@ -1,2 +1,2 @@
-GS_VSN = 1.5.15.2
+GS_VSN = 1.5.16
diff --git a/lib/hipe/Makefile b/lib/hipe/Makefile
index a9e24f4d17..46cbc33ae2 100644
--- a/lib/hipe/Makefile
+++ b/lib/hipe/Makefile
@@ -22,7 +22,7 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
ifdef HIPE_ENABLED
-HIPE_SUBDIRS = regalloc sparc ppc x86 amd64 arm opt tools
+HIPE_SUBDIRS = regalloc sparc ppc x86 amd64 arm opt tools llvm
else
HIPE_SUBDIRS =
endif
diff --git a/lib/hipe/arm/hipe_arm_assemble.erl b/lib/hipe/arm/hipe_arm_assemble.erl
index 2af786994e..e9de96a927 100644
--- a/lib/hipe/arm/hipe_arm_assemble.erl
+++ b/lib/hipe/arm/hipe_arm_assemble.erl
@@ -44,8 +44,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
print("Total num bytes=~w\n", [CodeSize], Options),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -320,7 +320,7 @@ do_pseudo_li(I, MFA, ConstMap, Address, PrevImms, PendImms) ->
Atom when is_atom(Atom) ->
{load_atom, Atom};
{Label,constant} ->
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{load_address, {constant,ConstNo}};
{Label,closure} ->
{load_address, {closure,Label}};
@@ -518,37 +518,6 @@ fix_pc_refs(I, InsnAddress, FunAddress, LabelMap) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({_MFA,_L} = MFAL, LabelMap) ->
- gb_trees:get(MFAL, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -594,17 +563,6 @@ fill_spaces(N) when N > 0 ->
fill_spaces(0) ->
[].
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N,[_|R]) ->
- find_const(N,R);
-find_const(C,[]) ->
- ?EXIT({constant_not_found,C}).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile
index 506e993ff4..d13dfb33c2 100644
--- a/lib/hipe/cerl/Makefile
+++ b/lib/hipe/cerl/Makefile
@@ -42,8 +42,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES = cerl_cconv cerl_closurean cerl_hipeify \
- cerl_lib cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
+MODULES = cerl_cconv cerl_closurean cerl_hipeify cerl_lib \
+ cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
cerl_typean erl_bif_types erl_types
HRL_FILES= cerl_hipe_primops.hrl
@@ -65,7 +65,7 @@ DOC_FILES= $(MODULES:%=$(DOCS)/%.html)
include ../native.mk
-ERL_COMPILE_FLAGS += +inline +warn_exported_vars +warn_unused_import +warn_missing_spec# +warn_untyped_record
+ERL_COMPILE_FLAGS += -Werror +inline +warn_exported_vars +warn_unused_import +warn_missing_spec #+warn_untyped_record
# ----------------------------------------------------
# Targets
@@ -107,7 +107,6 @@ release_spec: opt
release_docs_spec:
-$(EBIN)/cerl_to_icode.beam: cerl_hipe_primops.hrl ../icode/hipe_icode_primops.hrl
+$(EBIN)/cerl_cconv.beam: cerl_hipe_primops.hrl
$(EBIN)/cerl_hipeify.beam: cerl_hipe_primops.hrl
-$(EBIN)/cerl_lambdalift.beam: cerl_hipe_primops.hrl
-$(EBIN)/erl_bif_types.beam: ../icode/hipe_icode_primops.hrl
+$(EBIN)/cerl_to_icode.beam: cerl_hipe_primops.hrl ../icode/hipe_icode_primops.hrl
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl
index 021acd5b35..1b325703ae 100644
--- a/lib/hipe/cerl/cerl_closurean.erl
+++ b/lib/hipe/cerl/cerl_closurean.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -78,7 +78,8 @@
%% function; see `analyze' for details.
-spec annotate(cerl:cerl()) ->
- {cerl:cerl(), outlist(), dict(), escapes(), dict(), dict()}.
+ {cerl:cerl(), outlist(), dict:dict(),
+ escapes(), dict:dict(), dict:dict()}.
annotate(Tree) ->
{Xs, Out, Esc, Deps, Par} = analyze(Tree),
@@ -206,7 +207,8 @@ append_ann(Tag, Val, []) ->
%% variable labeled `escape', which will hold the set of escaped labels.
%% initially it contains `top' and `external'.
--spec analyze(cerl:cerl()) -> {outlist(), dict(), escapes(), dict(), dict()}.
+-spec analyze(cerl:cerl()) ->
+ {outlist(), dict:dict(), escapes(), dict:dict(), dict:dict()}.
analyze(Tree) ->
%% Note that we use different name spaces for variable labels and
diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl
index ca812a0f0d..7911b875a9 100644
--- a/lib/hipe/cerl/cerl_messagean.erl
+++ b/lib/hipe/cerl/cerl_messagean.erl
@@ -1,7 +1,7 @@
%% =====================================================================
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -182,7 +182,7 @@
-type label() :: integer() | 'external' | 'top'.
-type ordset(X) :: [X]. % XXX: TAKE ME OUT
--spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict()}.
+-spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict:dict()}.
annotate(Tree) ->
{Esc0, Vars} = analyze(Tree),
diff --git a/lib/hipe/cerl/cerl_to_icode.erl b/lib/hipe/cerl/cerl_to_icode.erl
index 1c1c10d9b0..2645056be1 100644
--- a/lib/hipe/cerl/cerl_to_icode.erl
+++ b/lib/hipe/cerl/cerl_to_icode.erl
@@ -29,9 +29,9 @@
-define(NO_UNUSED, true).
--export([module/2]).
+-export([module/1, module/2]).
-ifndef(NO_UNUSED).
--export([function/3, function/4, module/1]).
+-export([function/3, function/4]).
-endif.
%% Added in an attempt to suppress message by Dialyzer, but I run into
@@ -102,36 +102,32 @@
%% Record definitions
--record(ctxt, {final = false :: boolean(),
- effect = false,
- fail = [], % [] or fail-to label
- class = expr, % expr | guard
- line = 0, % current line number
- 'receive' % undefined | #receive{}
- }).
-
-record('receive', {loop}).
-record(cerl_to_icode__var, {name}).
-record('fun', {label, vars}).
+-record(ctxt, {final = false :: boolean(),
+ effect = false :: boolean(),
+ fail = [], % [] or fail-to label
+ class = expr :: 'expr' | 'guard',
+ line = 0 :: erl_scan:line(), % current line number
+ 'receive' :: 'undefined' | #'receive'{}
+ }).
%% ---------------------------------------------------------------------
%% Code
-
-%% @spec module(Module::cerl()) -> [icode()]
+%% @spec module(Module::cerl()) -> [{mfa(), icode()}]
%% @equiv module(Module, [])
--ifndef(NO_UNUSED).
+-spec module(cerl:c_module()) -> [{mfa(), hipe_icode:icode()}].
+
module(E) ->
module(E, []).
--endif.
-%% @clear
-
-%% @spec module(Module::cerl(), Options::[term()]) -> [icode()]
+%% @spec module(Module::cerl(), Options::[term()]) -> [{mfa(), icode()}]
%%
-%% cerl() = cerl:cerl()
+%% cerl() = cerl:c_module()
%% icode() = hipe_icode:icode()
%%
%% @doc Transforms a Core Erlang module to linear HiPE Icode. The result
@@ -149,7 +145,7 @@ module(E) ->
%% @see function/4
%% @see cerl_hipeify:transform/1
-%% -spec module(cerl:c_module(), [term()]) -> [{mfa(), hipe_icode:icode()}].
+-spec module(cerl:c_module(), [term()]) -> [{mfa(), hipe_icode:icode()}].
module(E, Options) ->
module_1(cerl_hipeify:transform(E, Options), Options).
@@ -163,8 +159,8 @@ module_1(E, Options) ->
throw(error)
end,
S0 = init(M),
- S1 = s__set_pmatch(proplists:get_value(pmatch, Options), S0),
- S2 = s__set_bitlevel_binaries(proplists:get_value(
+ S1 = s__set_pmatch(proplists:get_value(pmatch, Options), S0),
+ S2 = s__set_bitlevel_binaries(proplists:get_value(
bitlevel_binaries, Options), S1),
{Icode, _} = lists:mapfoldl(fun function_definition/2,
S2, cerl:module_defs(E)),
diff --git a/lib/hipe/cerl/cerl_typean.erl b/lib/hipe/cerl/cerl_typean.erl
index ccd8903658..f694c07c82 100644
--- a/lib/hipe/cerl/cerl_typean.erl
+++ b/lib/hipe/cerl/cerl_typean.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -242,7 +242,7 @@ delete_ann(_, []) ->
-type labelset() :: ordset(label()).
-type outlist() :: [labelset()] | 'none'.
--spec analyze(cerl:cerl()) -> {outlist(), dict(), dict()}.
+-spec analyze(cerl:cerl()) -> {outlist(), dict:dict(), dict:dict()}.
analyze(Tree) ->
analyze(Tree, ?DEF_LIMIT).
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 8b610ac893..a460f16272 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -912,7 +912,8 @@ type(erlang, system_info, 1, Xs, Opaques) ->
t_list(t_pid());
['os_type'] ->
t_tuple([t_sup([t_atom('unix'),
- t_atom('win32')]),
+ t_atom('win32'),
+ t_atom('ose')]),
t_atom()]);
['os_version'] ->
t_sup(t_tuple([t_non_neg_fixnum(),
@@ -1072,14 +1073,14 @@ type(hipe_bifs, fun_to_address, 1, Xs, Opaques) ->
%% type(hipe_bifs, get_emu_address, 1, Xs, Opaques) ->
%% strict(hipe_bifs, get_emu_address, 1, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
+type(hipe_bifs, get_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, get_fe, 2, Xs, fun (_) -> t_integer() end, Opaques);
type(hipe_bifs, get_rts_param, 1, Xs, Opaques) ->
strict(hipe_bifs, get_rts_param, 1, Xs,
fun (_) -> t_sup(t_integer(), t_nil()) end, Opaques);
type(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs, Opaques) ->
strict(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs,
fun (_) -> t_nil() end, Opaques);
-type(hipe_bifs, make_fe, 3, Xs, Opaques) ->
- strict(hipe_bifs, make_fe, 3, Xs, fun (_) -> t_integer() end, Opaques);
%% type(hipe_bifs, make_native_stub, 2, Xs, Opaques) ->
%% strict(hipe_bifs, make_native_stub, 2, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
@@ -1115,6 +1116,9 @@ type(hipe_bifs, set_funinfo_native_address, 3, Xs, Opaques) ->
type(hipe_bifs, set_native_address, 3, Xs, Opaques) ->
strict(hipe_bifs, set_native_address, 3, Xs,
fun (_) -> t_nil() end, Opaques);
+type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, set_native_address_in_fe, 2, Xs,
+ fun (_) -> t_atom('true') end, Opaques);
type(hipe_bifs, system_crc, 1, Xs, Opaques) ->
strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques);
type(hipe_bifs, term_to_word, 1, Xs, Opaques) ->
@@ -2450,12 +2454,12 @@ arg_types(hipe_bifs, fun_to_address, 1) ->
[t_mfa()];
%% arg_types(hipe_bifs, get_emu_address, 1) ->
%% [t_mfa()];
+arg_types(hipe_bifs, get_fe, 2) ->
+ [t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
arg_types(hipe_bifs, get_rts_param, 1) ->
[t_fixnum()];
arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1) ->
[t_list(t_mfa())];
-arg_types(hipe_bifs, make_fe, 3) ->
- [t_integer(), t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
%% arg_types(hipe_bifs, make_native_stub, 2) ->
%% [t_integer(), t_arity()];
arg_types(hipe_bifs, mark_referred_from, 1) ->
@@ -2484,6 +2488,8 @@ arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
+arg_types(hipe_bifs, set_native_address_in_fe, 2) ->
+ [t_integer(), t_integer()];
arg_types(hipe_bifs, system_crc, 1) ->
[t_crc32()];
arg_types(hipe_bifs, term_to_word, 1) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index af34e355bb..47b8dc766a 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -126,6 +126,8 @@
t_is_instance/2,
t_is_integer/1, t_is_integer/2,
t_is_list/1,
+ t_is_map/1,
+ t_is_map/2,
t_is_matchstate/1,
t_is_nil/1, t_is_nil/2,
t_is_non_neg_integer/1,
@@ -148,6 +150,8 @@
t_list/1,
t_list_elements/1, t_list_elements/2,
t_list_termination/1,
+ t_map/0,
+ t_map/1,
t_matchstate/0,
t_matchstate/2,
t_matchstate_present/1,
@@ -208,11 +212,7 @@
lift_list_to_pos_empty/1,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1,
-
- t_is_map/2,
- t_map/1,
- t_map/0
+ atom_to_string/1
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -228,7 +228,7 @@
-export([t_is_identifier/1]).
-endif.
--export_type([erl_type/0]).
+-export_type([erl_type/0, type_table/0, var_table/0]).
%%-define(DEBUG, true).
@@ -363,6 +363,15 @@
-type opaques() :: [erl_type()] | 'universe'.
+-type record_key() :: {'record', atom()}.
+-type type_key() :: {'type' | 'opaque', atom(), arity()}.
+-type record_value() :: orddict:orddict(). % XXX. To be refined
+-type type_value() :: {module(), erl_type(), atom()}.
+-type type_table() :: dict:dict(record_key(), record_value())
+ | dict:dict(type_key(), type_value()).
+
+-type var_table() :: dict:dict(atom(), erl_type()).
+
%%-----------------------------------------------------------------------------
%% Unions
%%
@@ -489,9 +498,9 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
-t_contains_opaque(?map(Pairs), Opaques) ->
- list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse
- list_contains_opaque([K||{K,_}<-Pairs], Opaques);
+t_contains_opaque(?map(_) = Map, Opaques) ->
+ list_contains_opaque(map_values(Map), Opaques) orelse
+ list_contains_opaque(map_keys(Map), Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -605,7 +614,7 @@ t_decorate_with_opaque(T1, T2, Opaques) ->
end
end.
-decorate(?none=Type, _, _Opaques) -> Type;
+decorate(Type, ?none, _Opaques) -> Type;
decorate(?function(Domain, Range), ?function(D, R), Opaques) ->
?function(decorate(Domain, D, Opaques), decorate(Range, R, Opaques));
decorate(?list(Types, Tail, Size), ?list(Ts, Tl, _Sz), Opaques) ->
@@ -645,15 +654,16 @@ decorate_with_opaque(Type, ?opaque(Set2), Opaques) ->
end.
decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques,
- NewOpaqueTypes, All) ->
+ NewOpaqueTypes0, All) ->
IsOpaque = is_opaque_type2(Opaque, Opaques),
I = t_inf(Type, S),
- case not IsOpaque orelse t_is_none(I = t_inf(Type, S)) of
- true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, All);
+ case not IsOpaque orelse t_is_none(I) of
+ true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes0, All);
false ->
NewOpaque = Opaque#opaque{struct = decorate(I, S, Opaques)},
NewAll = All orelse t_is_equal(I, Type),
- decoration(OpaqueTypes, Type, Opaques, [NewOpaque|NewOpaqueTypes], NewAll)
+ NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0],
+ decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll)
end;
decoration([], _Type, _Opaques, NewOpaqueTypes, All) ->
{NewOpaqueTypes, All}.
@@ -670,6 +680,7 @@ union_decorate(U1, U2, Opaques) ->
List = [A,B,F,I,L,N,T,M,Map],
DecList = [Dec ||
E <- List,
+ not t_is_none(E),
not t_is_none(Dec = decorate(E, Opaque, Opaques))],
t_sup([Union|DecList]).
@@ -722,7 +733,7 @@ decorate_tuples_in_sets([T1|Tuples], L2, Opaques, Acc) ->
decorate_tuples_in_sets([], _L, _Opaques, Acc) ->
lists:reverse(Acc).
--spec t_opaque_from_records(dict()) -> [erl_type()].
+-spec t_opaque_from_records(type_table()) -> [erl_type()].
t_opaque_from_records(RecDict) ->
OpaqueRecDict =
@@ -733,13 +744,14 @@ t_opaque_from_records(RecDict) ->
end
end, RecDict),
OpaqueTypeDict =
- dict:map(fun({opaque, Name, _Arity}, {Module, Type, ArgNames}) ->
- case ArgNames of
- [] ->
- t_opaque(Module, Name, [], t_from_form(Type, RecDict));
- _ ->
- throw({error,"Polymorphic opaque types not supported yet"})
- end
+ dict:map(fun({opaque, Name, _Arity}, {Module, _Type, ArgNames}) ->
+ %% Args = args_to_types(ArgNames),
+ %% List = lists:zip(ArgNames, Args),
+ %% TmpVarDict = dict:from_list(List),
+ %% Rep = t_from_form(Type, RecDict, TmpVarDict),
+ Rep = t_none(), % not used for anything right now
+ Args = [t_any() || _ <- ArgNames],
+ skip_opaque_alias(Rep, Module, Name, Args)
end, OpaqueRecDict),
[OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)].
@@ -796,7 +808,9 @@ t_is_remote(Type) ->
is_remote(?remote(_)) -> true;
is_remote(_) -> false.
--spec t_solve_remote(erl_type(), set(), dict()) -> erl_type().
+-type mod_records() :: dict:dict(module(), type_table()).
+
+-spec t_solve_remote(erl_type(), sets:set(mfa()), mod_records()) -> erl_type().
t_solve_remote(Type, ExpTypes, Records) ->
{RT, _RR} = t_solve_remote(Type, ExpTypes, Records, []),
@@ -833,8 +847,12 @@ t_solve_remote(?union(List), ET, R, C) ->
{t_sup(RL), RR};
t_solve_remote(T, _ET, _R, _C) -> {T, []}.
-t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
+t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args0} = RemType,
ET, R, C) ->
+ Args = lists:map(fun(A) ->
+ {Arg, _} = t_solve_remote(A, ET, R, C),
+ Arg
+ end, Args0),
ArgsLen = length(Args),
case dict:find(RemMod, R) of
error ->
@@ -880,9 +898,7 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
true -> t_limit(NewRep, ?REC_TYPE_LIMIT);
false -> NewRep
end,
- {t_from_form({opaque, -1, Name, {Mod, Args, RT1}},
- RemDict, TmpVarDict),
- RetRR};
+ {skip_opaque_alias(RT1, Mod, Name, Args), RetRR};
error ->
Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
[RemMod, Name]),
@@ -1713,11 +1729,16 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) ->
t_map() ->
?map([]).
--spec t_map([{erl_type(),erl_type()}]) -> erl_type().
+-spec t_map([{erl_type(), erl_type()}]) -> erl_type().
t_map(_) ->
?map([]).
+-spec t_is_map(erl_type()) -> boolean().
+
+t_is_map(Type) ->
+ t_is_map(Type, 'universe').
+
-spec t_is_map(erl_type(), opaques()) -> boolean().
t_is_map(Type, Opaques) ->
@@ -1726,7 +1747,6 @@ t_is_map(Type, Opaques) ->
is_map1(?map(_)) -> true;
is_map1(_) -> false.
-
%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -1958,20 +1978,24 @@ t_timeout() ->
-spec t_array() -> erl_type().
t_array() ->
- t_opaque(array, array, [],
+ t_opaque(array, array, [t_any()],
t_tuple([t_atom('array'),
t_sup([t_atom('undefined'), t_non_neg_integer()]),
t_sup([t_atom('undefined'), t_non_neg_integer()]),
- t_any(), t_any()])).
+ t_any(),
+ t_any()])).
-spec t_dict() -> erl_type().
t_dict() ->
- t_opaque(dict, dict, [],
+ t_opaque(dict, dict, [t_any(), t_any()],
t_tuple([t_atom('dict'),
- t_non_neg_integer(), t_non_neg_integer(),
- t_non_neg_integer(), t_non_neg_integer(),
- t_non_neg_integer(), t_non_neg_integer(),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
t_sup([t_atom('undefined'), t_tuple()]),
t_sup([t_atom('undefined'), t_tuple()])])).
@@ -2000,12 +2024,12 @@ t_gb_tree() ->
-spec t_queue() -> erl_type().
t_queue() ->
- t_opaque(queue, queue, [], t_tuple([t_list(), t_list()])).
+ t_opaque(queue, queue, [t_any()], t_tuple([t_list(), t_list()])).
-spec t_set() -> erl_type().
t_set() ->
- t_opaque(sets, set, [],
+ t_opaque(sets, set, [t_any()],
t_tuple([t_atom('set'), t_non_neg_integer(), t_non_neg_integer(),
t_pos_integer(), t_non_neg_integer(), t_non_neg_integer(),
t_non_neg_integer(),
@@ -2023,18 +2047,6 @@ all_opaque_builtins() ->
[t_array(), t_dict(), t_digraph(), t_gb_set(),
t_gb_tree(), t_queue(), t_set(), t_tid()].
--spec is_opaque_builtin(atom(), atom()) -> boolean().
-
-is_opaque_builtin(array, array) -> true;
-is_opaque_builtin(dict, dict) -> true;
-is_opaque_builtin(digraph, digraph) -> true;
-is_opaque_builtin(gb_sets, gb_set) -> true;
-is_opaque_builtin(gb_trees, gb_tree) -> true;
-is_opaque_builtin(queue, queue) -> true;
-is_opaque_builtin(sets, set) -> true;
-is_opaque_builtin(ets, tid) -> true;
-is_opaque_builtin(_, _) -> false.
-
%%------------------------------------
%% ?none is allowed in products. A product of size 1 is not a product.
@@ -2082,11 +2094,13 @@ t_has_var(?tuple(Elements, _, _)) ->
t_has_var_list(Elements);
t_has_var(?tuple_set(_) = T) ->
t_has_var_list(t_tuple_subtypes(T));
-%% t_has_var(?opaque(_)=T) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% t_has_var(t_opaque_structure(T));
-%% t_has_var(?union(_) = U) ->
-%% exit(flat_format("Union happens in t_has_var/1 ~p\n",[U]));
+t_has_var(?map(_)= Map) ->
+ t_has_var_list(map_keys(Map)) orelse t_has_var_list(map_values(Map));
+t_has_var(?opaque(Set)) ->
+ %% Assume variables in 'args' are also present i 'struct'
+ t_has_var_list([O#opaque.struct || O <- set_to_list(Set)]);
+t_has_var(?union(List)) ->
+ t_has_var_list(List);
t_has_var(_) -> false.
-spec t_has_var_list([erl_type()]) -> boolean().
@@ -2109,20 +2123,28 @@ t_collect_vars(?function(Domain, Range), Acc) ->
t_collect_vars(?list(Contents, Termination, _), Acc) ->
ordsets:union(t_collect_vars(Contents, Acc), t_collect_vars(Termination, []));
t_collect_vars(?product(Types), Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, Types);
+ t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple(?any, ?any, ?any), Acc) ->
Acc;
t_collect_vars(?tuple(Types, _, _), Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, Types);
+ t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple_set(_) = TS, Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc,
- t_tuple_subtypes(TS));
-%% t_collect_vars(?opaque(_)=T, Acc) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% t_collect_vars(t_opaque_structure(T), Acc);
+ t_collect_vars_list(t_tuple_subtypes(TS), Acc);
+t_collect_vars(?map(_) = Map, Acc0) ->
+ Acc = t_collect_vars_list(map_keys(Map), Acc0),
+ t_collect_vars_list(map_values(Map), Acc);
+t_collect_vars(?opaque(Set), Acc) ->
+ %% Assume variables in 'args' are also present i 'struct'
+ t_collect_vars_list([O#opaque.struct || O <- set_to_list(Set)], Acc);
+t_collect_vars(?union(List), Acc) ->
+ t_collect_vars_list(List, Acc);
t_collect_vars(_, Acc) ->
Acc.
+t_collect_vars_list([T|Ts], Acc0) ->
+ Acc = t_collect_vars(T, Acc0),
+ t_collect_vars_list(Ts, Acc);
+t_collect_vars_list([], Acc) -> Acc.
%%=============================================================================
%%
@@ -2145,6 +2167,7 @@ t_from_term(T) when is_function(T) ->
{arity, Arity} = erlang:fun_info(T, arity),
t_fun(Arity, t_any());
t_from_term(T) when is_integer(T) -> t_integer(T);
+t_from_term(T) when is_map(T) -> t_map();
t_from_term(T) when is_pid(T) -> t_pid();
t_from_term(T) when is_port(T) -> t_port();
t_from_term(T) when is_reference(T) -> t_reference();
@@ -2442,8 +2465,8 @@ sup_opaque(List) ->
?opaque(ordsets:from_list(L)).
sup_opaq(L0) ->
- L1 = [{{Mod,Name}, T} ||
- #opaque{mod = Mod, name = Name}=T <- L0],
+ L1 = [{{Mod,Name,Args}, T} ||
+ #opaque{mod = Mod, name = Name, args = Args}=T <- L0],
F = family(L1),
[supl(Ts) || {_, Ts} <- F].
@@ -2547,7 +2570,7 @@ force_union(T = ?function(_, _)) -> ?function_union(T);
force_union(T = ?identifier(_)) -> ?identifier_union(T);
force_union(T = ?list(_, _, _)) -> ?list_union(T);
force_union(T = ?nil) -> ?list_union(T);
-force_union(T = ?number(_,_)) -> ?number_union(T);
+force_union(T = ?number(_, _)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
force_union(T = ?remote(_)) -> ?remote_union(T);
force_union(T = ?map(_)) -> ?map_union(T);
@@ -2809,31 +2832,35 @@ inf_collect(_T1, [], _Opaques, OpL) ->
OpL.
combine(S, T1, T2) ->
- #opaque{mod = Mod1, name = Name1} = T1,
- #opaque{mod = Mod2, name = Name2} = T2,
- case {Mod1, Name1} =:= {Mod2, Name2} of
- true -> [comb(Mod1, Name1, S, T1)];
- false -> [comb(Mod1, Name1, S, T1), comb(Mod2, Name2, S, T2)]
+ #opaque{mod = Mod1, name = Name1, args = Args1} = T1,
+ #opaque{mod = Mod2, name = Name2, args = Args2} = T2,
+ case is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of
+ true -> [comb(Mod1, Name1, Args1, S, T1)];
+ false -> [comb(Mod1, Name1, Args1, S, T1), comb(Mod2, Name2, Args2, S, T2)]
end.
-comb(Mod, Name, S, T) ->
- case is_same_name(Mod, Name, S) of
+comb(Mod, Name, Args, S, T) ->
+ case is_same_name(Mod, Name, Args, S) of
true -> S;
false -> T#opaque{struct = S}
end.
-is_same_name(Mod, Name, ?opaque([#opaque{mod = Mod, name = Name}])) -> true;
-is_same_name(_Mod, _Name, _Opaque) -> false.
+is_same_name(Mod1, Name1, Args1,
+ ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) ->
+ is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2});
+is_same_name(_, _, _, _) -> false.
%% Combining two lists this way can be very time consuming...
+%% Note: two parameterized opaque types are not the same if their
+%% actual parameters differ
inf_opaque(Set1, Set2, Opaques) ->
List1 = inf_look_up(Set1, 1, Opaques),
List2 = inf_look_up(Set2, 2, Opaques),
List0 = [combine(Inf, T1, T2) ||
- {Is1, ModName1, T1} <- List1,
- {Is2, ModName2, T2} <- List2,
- not t_is_none(Inf = inf_opaque_types(Is1, ModName1, T1,
- Is2, ModName2, T2,
+ {Is1, ModNameArgs1, T1} <- List1,
+ {Is2, ModNameArgs2, T2} <- List2,
+ not t_is_none(Inf = inf_opaque_types(Is1, ModNameArgs1, T1,
+ Is2, ModNameArgs2, T2,
Opaques))],
List = lists:sort(lists:append(List0)),
sup_opaque(List).
@@ -2841,18 +2868,22 @@ inf_opaque(Set1, Set2, Opaques) ->
%% Optimization: do just one lookup.
inf_look_up(Set, Pos, Opaques) ->
[{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Pos, Opaques),
- {M, N}, T} ||
- #opaque{mod = M, name = N} = T <- set_to_list(Set)].
+ {M, N, Args}, T} ||
+ #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)].
inf_is_opaque_type2(T, Pos, {match, Opaques}) ->
is_opaque_type2(T, Opaques) orelse throw(Pos);
inf_is_opaque_type2(T, _Pos, Opaques) ->
is_opaque_type2(T, Opaques).
-inf_opaque_types(IsOpaque1, ModName1, T1, IsOpaque2, ModName2, T2, Opaques) ->
+inf_opaque_types(IsOpaque1, ModNameArgs1, T1,
+ IsOpaque2, ModNameArgs2, T2, Opaques) ->
#opaque{struct = S1}=T1,
#opaque{struct = S2}=T2,
- case Opaques =:= 'universe' orelse ModName1 =:= ModName2 of
+ case
+ Opaques =:= 'universe' orelse
+ is_same_type_name(ModNameArgs1, ModNameArgs2)
+ of
true -> t_inf(S1, S2, Opaques);
false ->
case {IsOpaque1, IsOpaque2} of
@@ -3018,13 +3049,13 @@ findfirst(N1, N2, U1, B1, U2, B2) ->
%% to types. Hans Bolinder suggested the use of lists of Key-Value pairs for
%% this data structure and measurements showed a non-trivial speedup when using
%% them for operations within this module (e.g. in t_unify/2). However, there
-%% is code outside erl_types that still passes a dict() in the 2nd argument.
+%% is code outside erl_types that still passes a dict:dict() in the 2nd argument.
%% So, for the time being, this module provides a t_subst/2 function for these
%% external calls and a clone of it (t_subst_kv/2) which is used from all calls
%% from within this module. This code duplication needs to be eliminated at
%% some point.
--spec t_subst(erl_type(), dict()) -> erl_type().
+-spec t_subst(erl_type(), dict:dict(atom(), erl_type())) -> erl_type().
t_subst(T, Dict) ->
case t_has_var(T) of
@@ -3060,11 +3091,16 @@ t_subst_dict(?tuple(Elements, _Arity, _Tag), Dict) ->
t_tuple([t_subst_dict(E, Dict) || E <- Elements]);
t_subst_dict(?tuple_set(_) = TS, Dict) ->
t_sup([t_subst_dict(T, Dict) || T <- t_tuple_subtypes(TS)]);
-%% t_subst_dict(?opaque(Es), Dict) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% List = [Opaque#opaque{struct = t_subst_dict(S, Dict)} ||
-%% Opaque = #opaque{struct = S} <- set_to_list(Es)],
-%% ?opaque(ordsets:from_list(List));
+t_subst_dict(?map(Pairs), Dict) ->
+ ?map([{t_subst_dict(K, Dict), t_subst_dict(V, Dict)} ||
+ {K, V} <- Pairs]);
+t_subst_dict(?opaque(Es), Dict) ->
+ List = [Opaque#opaque{args = [t_subst_dict(Arg, Dict) || Arg <- Args],
+ struct = t_subst_dict(S, Dict)} ||
+ Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)],
+ ?opaque(ordsets:from_list(List));
+t_subst_dict(?union(List), Dict) ->
+ ?union([t_subst_dict(E, Dict) || E <- List]);
t_subst_dict(T, _Dict) ->
T.
@@ -3107,11 +3143,16 @@ t_subst_aux(?tuple(Elements, _Arity, _Tag), VarMap) ->
t_tuple([t_subst_aux(E, VarMap) || E <- Elements]);
t_subst_aux(?tuple_set(_) = TS, VarMap) ->
t_sup([t_subst_aux(T, VarMap) || T <- t_tuple_subtypes(TS)]);
-%% t_subst_aux(?opaque(Es), VarMap) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% List = [Opaque#opaque{struct = t_subst_aux(S, VarMap)} ||
-%% Opaque = #opaque{struct = S} <- set_to_list(Es)],
-%% ?opaque(ordsets:from_list(List));
+t_subst_aux(?map(Pairs), VarMap) ->
+ ?map([{t_subst_aux(K, VarMap), t_subst_aux(V, VarMap)} ||
+ {K, V} <- Pairs]);
+t_subst_aux(?opaque(Es), VarMap) ->
+ List = [Opaque#opaque{args = [t_subst_aux(Arg, VarMap) || Arg <- Args],
+ struct = t_subst_aux(S, VarMap)} ||
+ Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)],
+ ?opaque(ordsets:from_list(List));
+t_subst_aux(?union(List), VarMap) ->
+ ?union([t_subst_aux(E, VarMap) || E <- List]);
t_subst_aux(T, _VarMap) ->
T.
@@ -3232,15 +3273,20 @@ unify_union(List) ->
is_opaque_type(?opaque(Elements), Opaques) ->
lists:any(fun(Opaque) -> is_opaque_type2(Opaque, Opaques) end, Elements).
-is_opaque_type2(#opaque{mod = Mod1, name = Name1}, Opaques) ->
+is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) ->
F1 = fun(?opaque(Es)) ->
- F2 = fun(#opaque{mod = Mod, name = Name}) ->
- Mod1 =:= Mod andalso Name1 =:= Name
+ F2 = fun(#opaque{mod = Mod, name = Name, args = Args}) ->
+ is_type_name(Mod1, Name1, Args1, Mod, Name, Args)
end,
lists:any(F2, Es)
end,
lists:any(F1, Opaques).
+is_type_name(Mod, Name, Args1, Mod, Name, Args2) ->
+ length(Args1) =:= length(Args2);
+is_type_name(Mod1, Name1, Args1, Mod2, Name2, Args2) ->
+ is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2).
+
%% Two functions since t_unify is not symmetric.
unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]),
?tuple(Elements2, Arity, _), VarMap) ->
@@ -3535,6 +3581,8 @@ t_subtract(?product(Elements1) = T1, ?product(Elements2)) ->
_ -> T1
end
end;
+t_subtract(?map(_) = T, _) -> % XXX: very crude; will probably need refinement
+ T;
t_subtract(?product(P1), _) ->
?product(P1);
t_subtract(T, ?product(_)) ->
@@ -3675,7 +3723,7 @@ t_unopaque(T) ->
t_unopaque(?opaque(_) = T, Opaques) ->
case Opaques =:= 'universe' orelse is_opaque_type(T, Opaques) of
true -> t_unopaque(t_opaque_structure(T), Opaques);
- false -> T % XXX: needs revision for parametric opaque data types
+ false -> T
end;
t_unopaque(?list(ElemT, Termination, Sz), Opaques) ->
?list(t_unopaque(ElemT, Opaques), t_unopaque(Termination, Opaques), Sz);
@@ -3695,11 +3743,12 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
+ UMap = t_unopaque(Map, Opaques),
{OF,UO} = case t_unopaque(O, Opaques) of
?opaque(_) = O1 -> {O1, []};
Type -> {?none, [Type]}
end,
- t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,UMap])|UO]);
t_unopaque(T, _) ->
T.
@@ -3759,7 +3808,7 @@ t_limit_k(T, _K) -> T.
%%
%%============================================================================
--spec t_abstract_records(erl_type(), dict()) -> erl_type().
+-spec t_abstract_records(erl_type(), type_table()) -> erl_type().
t_abstract_records(?list(Contents, Termination, Size), RecDict) ->
case t_abstract_records(Contents, RecDict) of
@@ -3839,7 +3888,7 @@ t_map(Fun, T) ->
t_to_string(T) ->
t_to_string(T, dict:new()).
--spec t_to_string(erl_type(), dict()) -> string().
+-spec t_to_string(erl_type(), type_table()) -> string().
t_to_string(?any, _RecDict) ->
"any()";
@@ -3885,8 +3934,8 @@ t_to_string(?identifier(Set), _RecDict) ->
string:join([flat_format("~w()", [T]) || T <- set_to_list(Set)], " | ")
end;
t_to_string(?opaque(Set), RecDict) ->
- string:join([opaque_type(Mod, Name, S, RecDict) ||
- #opaque{mod = Mod, name = Name, struct = S}
+ string:join([opaque_type(Mod, Name, Args, S, RecDict) ||
+ #opaque{mod = Mod, name = Name, struct = S, args = Args}
<- set_to_list(Set)],
" | ");
t_to_string(?matchstate(Pres, Slots), RecDict) ->
@@ -4025,7 +4074,7 @@ record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) ->
record_fields_to_string([], [], _RecDict, Acc) ->
lists:reverse(Acc).
--spec record_field_diffs_to_string(erl_type(), dict()) -> string().
+-spec record_field_diffs_to_string(erl_type(), type_table()) -> string().
record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) ->
[TagAtom] = atom_vals(Tag),
@@ -4059,11 +4108,14 @@ union_sequence(Types, RecDict) ->
string:join(List, " | ").
-ifdef(DEBUG).
-opaque_type(Mod, Name, S, RecDict) ->
- opaque_name(Mod, Name, t_to_string(S, RecDict)).
+opaque_type(Mod, Name, _Args, S, RecDict) ->
+ ArgsString = comma_sequence(_Args, RecDict),
+ String = t_to_string(S, RecDict),
+ opaque_name(Mod, Name, ArgsString) ++ "[" ++ String ++ "]".
-else.
-opaque_type(Mod, Name, _S, _RecDict) ->
- opaque_name(Mod, Name, "").
+opaque_type(Mod, Name, Args, _S, RecDict) ->
+ ArgsString = comma_sequence(Args, RecDict),
+ opaque_name(Mod, Name, ArgsString).
-endif.
opaque_name(Mod, Name, Extra) ->
@@ -4071,11 +4123,16 @@ opaque_name(Mod, Name, Extra) ->
flat_format("~s(~s)", [S, Extra]).
mod_name(Mod, Name) ->
- case is_opaque_builtin(Mod, Name) of
+ case is_obsolete_opaque_builtin(Mod, Name) of
true -> flat_format("~w", [Name]);
false -> flat_format("~w:~w", [Mod, Name])
end.
+is_obsolete_opaque_builtin(digraph, digraph) -> true;
+is_obsolete_opaque_builtin(gb_sets, gb_set) -> true;
+is_obsolete_opaque_builtin(gb_trees, gb_tree) -> true;
+is_obsolete_opaque_builtin(_, _) -> false.
+
%%=============================================================================
%%
%% Build a type from parse forms.
@@ -4087,19 +4144,20 @@ mod_name(Mod, Name) ->
t_from_form(Form) ->
t_from_form(Form, dict:new()).
--spec t_from_form(parse_form(), dict()) -> erl_type().
+-spec t_from_form(parse_form(), type_table()) -> erl_type().
t_from_form(Form, RecDict) ->
t_from_form(Form, RecDict, dict:new()).
--spec t_from_form(parse_form(), dict(), dict()) -> erl_type().
+-spec t_from_form(parse_form(), type_table(), var_table()) -> erl_type().
t_from_form(Form, RecDict, VarDict) ->
{T, _R} = t_from_form(Form, [], RecDict, VarDict),
T.
--type type_names() :: [{'type' | 'opaque' | 'record', atom()}].
--spec t_from_form(parse_form(), type_names(), dict(), dict()) ->
+-type type_names() :: [type_key() | record_key()].
+
+-spec t_from_form(parse_form(), type_names(), type_table(), var_table()) ->
{erl_type(), type_names()}.
t_from_form({var, _L, '_'}, _TypeNames, _RecDict, _VarDict) ->
@@ -4138,8 +4196,8 @@ t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) ->
{t_any(), []};
t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) ->
{t_arity(), []};
-t_from_form({type, _L, array, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_array(), []};
+t_from_form({type, _L, array, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(array, t_array(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) ->
{t_atom(), []};
t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4161,10 +4219,10 @@ t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) ->
{t_byte(), []};
t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) ->
{t_char(), []};
-t_from_form({type, _L, dict, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_dict(), []};
-t_from_form({type, _L, digraph, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_digraph(), []};
+t_from_form({type, _L, dict, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(dict, t_dict(), TypeNames, RecDict, VarDict);
+t_from_form({type, _L, digraph, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(digraph, t_digraph(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) ->
{t_float(), []};
t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4180,10 +4238,10 @@ t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
{L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict),
{T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict),
{t_fun(L, T), R1 ++ R2};
-t_from_form({type, _L, gb_set, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_gb_set(), []};
-t_from_form({type, _L, gb_tree, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_gb_tree(), []};
+t_from_form({type, _L, gb_set, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(gb_set, t_gb_set(), TypeNames, RecDict, VarDict);
+t_from_form({type, _L, gb_tree, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(gb_tree, t_gb_tree(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) ->
{t_identifier(), []};
t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4197,8 +4255,8 @@ t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) ->
t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) ->
{T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
{t_list(T), R};
-t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) ->
- {t_map([]), []};
+t_from_form({type, _L, map, _}, TypeNames, RecDict, VarDict) ->
+ builtin_type(map, t_map([]), TypeNames, RecDict, VarDict);
t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) ->
{t_mfa(), []};
t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4256,8 +4314,8 @@ t_from_form({type, _L, maybe_improper_list, [Content, Termination]},
t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) ->
{L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict),
{t_product(L), R};
-t_from_form({type, _L, queue, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_queue(), []};
+t_from_form({type, _L, queue, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(queue, t_queue(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, range, [From, To]} = Type,
_TypeNames, _RecDict, _VarDict) ->
case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
@@ -4269,14 +4327,14 @@ t_from_form({type, _L, record, [Name|Fields]}, TypeNames, RecDict, VarDict) ->
record_from_form(Name, Fields, TypeNames, RecDict, VarDict);
t_from_form({type, _L, reference, []}, _TypeNames, _RecDict, _VarDict) ->
{t_reference(), []};
-t_from_form({type, _L, set, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_set(), []};
+t_from_form({type, _L, set, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(set, t_set(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) ->
{t_string(), []};
t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) ->
{t_any(), []};
-t_from_form({type, _L, tid, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_tid(), []};
+t_from_form({type, _L, tid, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(tid, t_tid(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) ->
{t_timeout(), []};
t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) ->
@@ -4288,61 +4346,67 @@ t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) ->
{L, R} = list_from_form(Args, TypeNames, RecDict, VarDict),
{t_sup(L), R};
t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) ->
+ type_from_form(Name, Args, TypeNames, RecDict, VarDict);
+t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames,
+ _RecDict, _VarDict) ->
+ {t_opaque(Mod, Name, Args, Rep), []}.
+
+builtin_type(Name, Type, TypeNames, RecDict, VarDict) ->
+ case lookup_type(Name, 0, RecDict) of
+ {_, {_M, _T, _A}} ->
+ type_from_form(Name, [], TypeNames, RecDict, VarDict);
+ error ->
+ {Type, []}
+ end.
+
+type_from_form(Name, Args, TypeNames, RecDict, VarDict) ->
ArgsLen = length(Args),
+ ArgTypes = forms_to_types(Args, TypeNames, RecDict, VarDict),
case lookup_type(Name, ArgsLen, RecDict) of
{type, {_Module, Type, ArgNames}} ->
- case can_unfold_more({type, Name}, TypeNames) of
+ TypeName = {type, Name, ArgsLen},
+ case can_unfold_more(TypeName, TypeNames) of
true ->
- List = lists:zipwith(
- fun(ArgName, ArgType) ->
- {Ttemp, _R} = t_from_form(ArgType, TypeNames,
- RecDict, VarDict),
- {ArgName, Ttemp}
- end,
- ArgNames, Args),
+ List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
- {T, R} = t_from_form(Type, [{type, Name}|TypeNames],
+ {T, R} = t_from_form(Type, [TypeName|TypeNames],
RecDict, TmpVarDict),
- case lists:member({type, Name}, R) of
+ case lists:member(TypeName, R) of
true -> {t_limit(T, ?REC_TYPE_LIMIT), R};
false -> {T, R}
end;
- false -> {t_any(), [{type, Name}]}
+ false -> {t_any(), [TypeName]}
end;
{opaque, {Module, Type, ArgNames}} ->
+ TypeName = {opaque, Name, ArgsLen},
{Rep, Rret} =
- case can_unfold_more({opaque, Name}, TypeNames) of
+ case can_unfold_more(TypeName, TypeNames) of
true ->
- List = lists:zipwith(
- fun(ArgName, ArgType) ->
- {Ttemp, _R} = t_from_form(ArgType, TypeNames,
- RecDict, VarDict),
- {ArgName, Ttemp}
- end,
- ArgNames, Args),
+ List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
- {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames],
+ {T, R} = t_from_form(Type, [TypeName|TypeNames],
RecDict, TmpVarDict),
- case lists:member({opaque, Name}, R) of
+ case lists:member(TypeName, R) of
true -> {t_limit(T, ?REC_TYPE_LIMIT), R};
false -> {T, R}
end;
- false -> {t_any(), [{opaque, Name}]}
+ false -> {t_any(), [TypeName]}
end,
- Tret = t_from_form({opaque, -1, Name, {Module, Args, Rep}},
- RecDict, VarDict),
- {Tret, Rret};
+ Args2 = [subst_all_vars_to_any(ArgType) || ArgType <- ArgTypes],
+ {skip_opaque_alias(Rep, Module, Name, Args2), Rret};
error ->
Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]),
throw({error, Msg})
- end;
-t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames,
- _RecDict, _VarDict) ->
- case Args of
- [] -> {t_opaque(Mod, Name, Args, Rep), []};
- _ -> throw({error, "Polymorphic opaque types not supported yet"})
end.
+forms_to_types(Forms, TypeNames, RecDict, VarDict) ->
+ {Types, _} = list_from_form(Forms, TypeNames, RecDict, VarDict),
+ Types.
+
+skip_opaque_alias(?opaque(_) = T, _Mod, _Name, _Args) -> T;
+skip_opaque_alias(T, Module, Name, Args) ->
+ t_opaque(Module, Name, Args, T).
+
record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) ->
case can_unfold_more({record, Name}, TypeNames) of
true ->
@@ -4403,8 +4467,10 @@ build_field_dict([], _TypeNames, _RecDict, _VarDict, Acc) ->
get_mod_record([{FieldName, DeclType}|Left1],
[{FieldName, ModType}|Left2], Acc) ->
- case t_is_var(ModType) orelse t_is_remote(ModType) orelse
- t_is_subtype(ModType, DeclType) of
+ ModTypeNoVars = subst_all_vars_to_any(ModType),
+ case
+ t_is_remote(ModTypeNoVars) orelse t_is_subtype(ModTypeNoVars, DeclType)
+ of
false -> {error, FieldName};
true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc])
end;
@@ -4561,7 +4627,7 @@ is_erl_type(?unit) -> true;
is_erl_type(#c{}) -> true;
is_erl_type(_) -> false.
--spec lookup_record(atom(), dict()) ->
+-spec lookup_record(atom(), type_table()) ->
'error' | {'ok', [{atom(), parse_form() | erl_type()}]}.
lookup_record(Tag, RecDict) when is_atom(Tag) ->
@@ -4576,7 +4642,8 @@ lookup_record(Tag, RecDict) when is_atom(Tag) ->
error
end.
--spec lookup_record(atom(), arity(), dict()) -> 'error' | {'ok', [{atom(), erl_type()}]}.
+-spec lookup_record(atom(), arity(), type_table()) ->
+ 'error' | {'ok', [{atom(), erl_type()}]}.
lookup_record(Tag, Arity, RecDict) when is_atom(Tag) ->
case dict:find({record, Tag}, RecDict) of
@@ -4595,7 +4662,8 @@ lookup_type(Name, Arity, RecDict) ->
{ok, Found} -> {type, Found}
end.
--spec type_is_defined('type' | 'opaque', atom(), arity(), dict()) -> boolean().
+-spec type_is_defined('type' | 'opaque', atom(), arity(), type_table()) ->
+ boolean().
type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
@@ -4627,6 +4695,36 @@ do_opaque(?union(List) = Type, Opaques, Pred) ->
do_opaque(Type, _Opaques, Pred) ->
Pred(Type).
+is_same_type_name(ModNameArgs, ModNameArgs) -> true;
+is_same_type_name({Mod, Name, Args1}, {Mod, Name, Args2}) ->
+ all_any(Args1) orelse all_any(Args2);
+is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) ->
+ is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2).
+
+all_any([]) -> true;
+all_any([T|L]) ->
+ t_is_any(T) andalso all_any(L);
+all_any(_) -> false.
+
+%% Compatibility. In Erlang/OTP 17 the pre-defined opaque types
+%% digraph() and so on can be used, but there are also new types such
+%% as digraph:graph() with the exact same meaning. In Erlang/OTP R18.0
+%% all but the last clause can be removed.
+
+is_same_type_name2(digraph, digraph, [], digraph, graph, []) -> true;
+is_same_type_name2(digraph, graph, [], digraph, digraph, []) -> true;
+is_same_type_name2(gb_sets, gb_set, [], gb_sets, set, [_]) -> true;
+is_same_type_name2(gb_sets, set, [_], gb_sets, gb_set, []) -> true;
+is_same_type_name2(gb_trees, gb_tree, [], gb_trees, tree, [_, _]) -> true;
+is_same_type_name2(gb_trees, tree, [_, _], gb_trees, gb_tree, []) -> true;
+is_same_type_name2(_, _, _, _, _, _) -> false.
+
+map_keys(?map(Pairs)) ->
+ [K || {K, _} <- Pairs].
+
+map_values(?map(Pairs)) ->
+ [V || {_, V} <- Pairs].
+
%% -----------------------------------
%% Set
%%
diff --git a/lib/hipe/flow/cfg.hrl b/lib/hipe/flow/cfg.hrl
index 95bf5f7194..1f7a162f27 100644
--- a/lib/hipe/flow/cfg.hrl
+++ b/lib/hipe/flow/cfg.hrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -42,12 +42,12 @@
%%
%% Data is a triple with a dict of constants, a list of labels and an integer
%%
--type cfg_data() :: {dict(), [cfg_lbl()], non_neg_integer()}.
+-type cfg_data() :: {dict:dict(), [cfg_lbl()], non_neg_integer()}.
%%
%% The following is to be used by other modules
%%
--record(cfg, {table = gb_trees:empty() :: gb_tree(),
+-record(cfg, {table = gb_trees:empty() :: gb_trees:tree(),
info :: #cfg_info{},
data :: cfg_data()}).
-type cfg() :: #cfg{}.
diff --git a/lib/hipe/flow/cfg.inc b/lib/hipe/flow/cfg.inc
index 62f399a81c..f0b1a75737 100644
--- a/lib/hipe/flow/cfg.inc
+++ b/lib/hipe/flow/cfg.inc
@@ -4,7 +4,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -534,7 +534,7 @@ breadth_list([], Vis, _CFG, BO) ->
{Vis, BO}.
-endif.
--spec none_visited() -> gb_set().
+-spec none_visited() -> gb_sets:set().
none_visited() ->
gb_sets:empty().
diff --git a/lib/hipe/flow/hipe_dominators.erl b/lib/hipe/flow/hipe_dominators.erl
index 113a32c3b7..50d45c7c72 100644
--- a/lib/hipe/flow/hipe_dominators.erl
+++ b/lib/hipe/flow/hipe_dominators.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -57,7 +57,7 @@
-record(domTree, {root :: cfg_lbl(),
size = 0 :: non_neg_integer(),
- nodes = gb_trees:empty() :: gb_tree()}).
+ nodes = gb_trees:empty() :: gb_trees:tree()}).
-type domTree() :: #domTree{}.
%%>----------------------------------------------------------------------<
@@ -590,7 +590,7 @@ domTree_pp_children([], _) ->
%%
%%========================================================================
--type domFrontier() :: gb_tree().
+-type domFrontier() :: gb_trees:tree().
%%>----------------------------------------------------------------------<
%% Procedure : domFrontier_create
diff --git a/lib/hipe/flow/liveness.inc b/lib/hipe/flow/liveness.inc
index 9c5eaf3e68..6f161fb269 100644
--- a/lib/hipe/flow/liveness.inc
+++ b/lib/hipe/flow/liveness.inc
@@ -3,7 +3,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -71,7 +71,7 @@
%% The generic liveness analysis
%%
--spec analyze(cfg()) -> gb_tree().
+-spec analyze(cfg()) -> gb_trees:tree().
-ifdef(HIPE_LIVENESS_CALC_LARGEST_LIVESET).
analyze(CFG) ->
@@ -209,7 +209,7 @@ successors(L, Liveness) ->
{_GK, _LiveIn, Successors} = liveness_lookup(L, Liveness),
Successors.
--spec livein(gb_tree(), _) -> [_].
+-spec livein(gb_trees:tree(), _) -> [_].
livein(Liveness, L) ->
{_GK, LiveIn, _Successors} = liveness_lookup(L, Liveness),
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index 81249c958e..dcd547fd5f 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -509,6 +509,10 @@ trans_fun([{test,test_arity,{f,Lbl},[Reg,N]}|Instructions], Env) ->
I = hipe_icode:mk_type([trans_arg(Reg)],{tuple,N},
hipe_icode:label_name(True),map_label(Lbl)),
[I,True | trans_fun(Instructions,Env)];
+%%--- is_map ---
+trans_fun([{test,is_map,{f,Lbl},[Arg]}|Instructions], Env) ->
+ {Code,Env1} = trans_type_test(map,Lbl,Arg,Env),
+ [Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
%%--- select_val ---
trans_fun([{select_val,Reg,{f,Lbl},{list,Cases}}|Instructions], Env) ->
diff --git a/lib/hipe/icode/hipe_icode.erl b/lib/hipe/icode/hipe_icode.erl
index 0e651a351c..7b3d087e2d 100644
--- a/lib/hipe/icode/hipe_icode.erl
+++ b/lib/hipe/icode/hipe_icode.erl
@@ -503,7 +503,6 @@
enter_args_update/2,
enter_type/1,
is_enter/1,
-
mk_return/1, %% mk_return(Vars)
%% mk_fail/1, %% mk_fail(Args) class = exit
@@ -606,6 +605,12 @@
-export([highest_var/1, highest_label/1]).
+%%
+%% Exported types
+%%
+
+-export_type([icode/0]).
+
%%---------------------------------------------------------------------
%%
%% Icode
@@ -614,7 +619,7 @@
-spec mk_icode(mfa(), [icode_var()], boolean(), boolean(), [icode_instr()],
{non_neg_integer(),non_neg_integer()},
- {icode_lbl(),icode_lbl()}) -> #icode{}.
+ {icode_lbl(),icode_lbl()}) -> icode().
mk_icode(Fun, Params, IsClosure, IsLeaf, Code, VarRange, LabelRange) ->
#icode{'fun'=Fun, params=Params, code=Code,
is_closure=IsClosure,
@@ -1434,8 +1439,8 @@ subst1([_|Pairs], I) -> subst1(Pairs, I).
%%
%% @doc Returns the successors of an Icode instruction.
%% In CFG form only branch instructions have successors,
-%% but in linear form other instructions like e.g. moves and
-%% others might be the last instruction of some basic block.
+%% but in linear form other instructions like e.g. moves
+%% might be the last instruction of some basic block.
%%
-spec successors(icode_instr()) -> [icode_lbl()].
diff --git a/lib/hipe/icode/hipe_icode.hrl b/lib/hipe/icode/hipe_icode.hrl
index 060493e61e..46c04beb40 100644
--- a/lib/hipe/icode/hipe_icode.hrl
+++ b/lib/hipe/icode/hipe_icode.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -61,8 +61,8 @@
| 'op_exact_eqeq_2' | 'suspend_msg_timeout'.
-type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitstr' | 'boolean'
- | 'cons' | 'fixnum' | 'float'
- | 'function' | 'function2' | 'integer' | 'list' | 'nil'
+ | 'cons' | 'fixnum' | 'float' | 'function'
+ | 'function2' | 'integer' | 'list' | 'map' | 'nil'
| 'number' | 'pid' | 'port' | 'reference' | 'tuple'
| {'atom', atom()} | {'integer', integer()}
| {'record', atom(), non_neg_integer()}
@@ -108,7 +108,6 @@
length :: non_neg_integer(),
cases :: [icode_switch_case()]}).
-
-record(icode_type, {test :: icode_type_test(),
args :: [icode_term_arg()],
true_label :: icode_lbl(),
@@ -178,5 +177,6 @@
var_range :: {non_neg_integer(), non_neg_integer()},
label_range :: {icode_lbl(), icode_lbl()},
info = [] :: icode_info()}).
+-type icode() :: #icode{}.
%%---------------------------------------------------------------------
diff --git a/lib/hipe/icode/hipe_icode_callgraph.erl b/lib/hipe/icode/hipe_icode_callgraph.erl
index 5789328f47..ccf97ecc17 100644
--- a/lib/hipe/icode/hipe_icode_callgraph.erl
+++ b/lib/hipe/icode/hipe_icode_callgraph.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -46,7 +46,7 @@
-type mfa_icode() :: {mfa(), #icode{}}.
--record(icode_callgraph, {codedict :: dict(), ordered_sccs :: [[mfa()]]}).
+-record(icode_callgraph, {codedict :: dict:dict(), ordered_sccs :: [[mfa()]]}).
%%------------------------------------------------------------------------
%% Exported functions
diff --git a/lib/hipe/icode/hipe_icode_cfg.erl b/lib/hipe/icode/hipe_icode_cfg.erl
index 9b4a10e273..f6c2b0600b 100644
--- a/lib/hipe/icode/hipe_icode_cfg.erl
+++ b/lib/hipe/icode/hipe_icode_cfg.erl
@@ -3,7 +3,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -54,8 +54,8 @@
-spec postorder(cfg()) -> [icode_lbl()].
-spec reverse_postorder(cfg()) -> [icode_lbl()].
--spec is_visited(icode_lbl(), gb_set()) -> boolean().
--spec visit(icode_lbl(), gb_set()) -> gb_set().
+-spec is_visited(icode_lbl(), gb_sets:set()) -> boolean().
+-spec visit(icode_lbl(), gb_sets:set()) -> gb_sets:set().
-spec bb(cfg(), icode_lbl()) -> 'not_found' | bb().
-spec bb_add(cfg(), icode_lbl(), bb()) -> cfg().
diff --git a/lib/hipe/icode/hipe_icode_coordinator.erl b/lib/hipe/icode/hipe_icode_coordinator.erl
index 79e3304e6f..c69db9afa9 100644
--- a/lib/hipe/icode/hipe_icode_coordinator.erl
+++ b/lib/hipe/icode/hipe_icode_coordinator.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -49,9 +49,9 @@ coordinate(CG, Escaping, NonEscaping, Mod) ->
-type mfalists() :: {[mfa()], [mfa()]}.
--spec coordinate(mfalists(), hipe_digraph:hdg(), gb_tree(),
- fun((mfalists(), gb_tree()) -> mfalists()),
- fun((gb_tree()) -> 'ok'), pid()) -> no_return().
+-spec coordinate(mfalists(), hipe_digraph:hdg(), gb_trees:tree(),
+ fun((mfalists(), gb_trees:tree()) -> mfalists()),
+ fun((gb_trees:tree()) -> 'ok'), pid()) -> no_return().
coordinate(MFALists, CG, PM, Restart, LastAction, ServerPid) ->
case MFALists of
diff --git a/lib/hipe/icode/hipe_icode_exceptions.erl b/lib/hipe/icode/hipe_icode_exceptions.erl
index 00caffb24b..6191c536ad 100644
--- a/lib/hipe/icode/hipe_icode_exceptions.erl
+++ b/lib/hipe/icode/hipe_icode_exceptions.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -397,9 +397,9 @@ get_renaming(C, Map) ->
succ :: #cfg{},
pred :: #cfg{},
start_labels :: [icode_lbl(),...],
- visited = hipe_icode_cfg:none_visited() :: gb_set(),
- out = gb_trees:empty() :: gb_tree(),
- in = gb_trees:empty() :: gb_tree()
+ visited = hipe_icode_cfg:none_visited() :: gb_sets:set(),
+ out = gb_trees:empty() :: gb_trees:tree(),
+ in = gb_trees:empty() :: gb_trees:tree()
}).
init_state(CFG) ->
diff --git a/lib/hipe/icode/hipe_icode_fp.erl b/lib/hipe/icode/hipe_icode_fp.erl
index a2ca6132d1..38b3881a77 100644
--- a/lib/hipe/icode/hipe_icode_fp.erl
+++ b/lib/hipe/icode/hipe_icode_fp.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -33,8 +33,8 @@
-include("hipe_icode.hrl").
-include("../flow/cfg.hrl").
--record(state, {edge_map = gb_trees:empty() :: gb_tree(),
- fp_ebb_map = gb_trees:empty() :: gb_tree(),
+-record(state, {edge_map = gb_trees:empty() :: gb_trees:tree(),
+ fp_ebb_map = gb_trees:empty() :: gb_trees:tree(),
cfg :: #cfg{}}).
%%--------------------------------------------------------------------
@@ -424,7 +424,7 @@ redirect_phis([I|Is] = Code, OldFrom, NewFrom, Acc) ->
NewI = hipe_icode:phi_redirect_pred(I, OldFrom, NewFrom),
redirect_phis(Is, OldFrom, NewFrom, [NewI|Acc]);
_ ->
- lists:reverse(Acc) ++ Code
+ lists:reverse(Acc, Code)
end;
redirect_phis([], _OldFrom, _NewFrom, Acc) ->
lists:reverse(Acc).
diff --git a/lib/hipe/icode/hipe_icode_instruction_counter.erl b/lib/hipe/icode/hipe_icode_instruction_counter.erl
index 92658d294a..f44adfe149 100644
--- a/lib/hipe/icode/hipe_icode_instruction_counter.erl
+++ b/lib/hipe/icode/hipe_icode_instruction_counter.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -64,7 +64,8 @@ walktrough_bb(BB, Info) ->
%% The counter specific functions
%%-------------------------------------------------------------------
--spec compare(gb_tree(), gb_tree(), gb_tree()) -> gb_tree().
+-spec compare(gb_trees:tree(), gb_trees:tree(), gb_trees:tree()) ->
+ gb_trees:tree().
compare(Name, Old, New) ->
NewList = gb_trees:to_list(New),
diff --git a/lib/hipe/icode/hipe_icode_mulret.erl b/lib/hipe/icode/hipe_icode_mulret.erl
index 2402bad42c..99522f6430 100644
--- a/lib/hipe/icode/hipe_icode_mulret.erl
+++ b/lib/hipe/icode/hipe_icode_mulret.erl
@@ -1166,9 +1166,9 @@ printCallList([]) -> io:format("~n").
%% removeUnElems([#icode_call{'fun'={unsafe_element,_}, args=Var}|List], Var, Res) ->
%% removeUnElems(List, Var, Res);
%% removeUnElems([I=#icode_move{dst=Var}|List], [Var], Res) ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% removeUnElems([I=#icode_call{dstlist=Var}|List], Var, Res) ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% removeUnElems([I|List], Var, Res) ->
%% removeUnElems(List, Var, [I|Res]);
%% removeUnElems([], _, Res) -> lists:reverse(Res).
@@ -1187,7 +1187,7 @@ printCallList([]) -> io:format("~n").
%% false ->
%% case lists:member(Var, Defs) of
%% true ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% false ->
%% removeUnElems(List, Var, [I|Res])
%% end
@@ -1195,7 +1195,7 @@ printCallList([]) -> io:format("~n").
%% false ->
%% case lists:member(Var, Defs) of
%% true ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% false ->
%% removeUnElems(List, Var, [I|Res])
%% end
@@ -1203,7 +1203,7 @@ printCallList([]) -> io:format("~n").
%% false ->
%% case lists:member(Var, Defs) of
%% true ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% false ->
%% removeUnElems(List, Var, [I|Res])
%% end
@@ -1248,16 +1248,16 @@ printCallList([]) -> io:format("~n").
%% modifyCode([I|Code], Var, Res) ->
%% case scanInstr(I, Var) of
%% {move, Arity, VarLst} ->
-%% Code2 = [#icode_return{vars=VarLst}, I |lists:reverse(Res) ++ Code],
+%% Code2 = [#icode_return{vars=VarLst}, I |lists:reverse(Res, Code)],
%% {Arity, lists:reverse(Code2)};
%% {mktuple, Arity, VarLst} ->
-%% Code2 = [#icode_return{vars=VarLst}|lists:reverse(Res) ++ Code],
+%% Code2 = [#icode_return{vars=VarLst}|lists:reverse(Res, Code)],
%% {Arity, lists:reverse(Code2)};
%% other ->
%% modifyCode(Code, Var, [I|Res])
%% end;
%% modifyCode([], Var, Res) ->
-%% {1, lists:reverse(Res) ++ [#icode_return{vars=Var}]}.
+%% {1, lists:reverse(Res, [#icode_return{vars=Var}]}.
%% scanInstr(#icode_call{dstlist=Var, 'fun'=mktuple, args=Lst}, Var) ->
%% {mktuple, length(Lst), Lst};
diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl
index 1a2cbfae31..fbc58f3568 100644
--- a/lib/hipe/icode/hipe_icode_range.erl
+++ b/lib/hipe/icode/hipe_icode_range.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -72,8 +72,8 @@
-type final_fun() :: fun((mfa(), [range()]) -> 'ok').
-type data() :: {mfa(), args_fun(), call_fun(), final_fun()}.
-type label() :: non_neg_integer().
--type info() :: gb_tree().
--type work_list() :: {[label()], [label()], set()}.
+-type info() :: gb_trees:tree().
+-type work_list() :: {[label()], [label()], sets:set()}.
-type variable() :: #icode_variable{}.
-type annotated_variable() :: #icode_variable{}.
-type argument() :: #icode_const{} | variable().
@@ -82,9 +82,9 @@
-type last_instr_return() :: {instr_split_info(), range()}.
-record(state, {info_map = gb_trees:empty() :: info(),
- counter = dict:new() :: dict(),
+ counter = dict:new() :: dict:dict(),
cfg :: cfg(),
- liveness = gb_trees:empty() :: gb_tree(),
+ liveness = gb_trees:empty() :: gb_trees:tree(),
ret_type :: range(),
lookup_fun :: call_fun(),
result_action :: final_fun()}).
diff --git a/lib/hipe/icode/hipe_icode_ssa.erl b/lib/hipe/icode/hipe_icode_ssa.erl
index 4607a96dda..2c4b6d9409 100644
--- a/lib/hipe/icode/hipe_icode_ssa.erl
+++ b/lib/hipe/icode/hipe_icode_ssa.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -37,7 +37,7 @@
-include("../ssa/hipe_ssa.inc").
%% Declarations for exported functions which are Icode-specific.
--spec ssa_liveness__analyze(#cfg{}) -> gb_tree().
+-spec ssa_liveness__analyze(#cfg{}) -> gb_trees:tree().
-spec ssa_liveness__livein(_, icode_lbl()) -> [#icode_variable{}].
%% -spec ssa_liveness__livein(_, icode_lbl(), _) -> [#icode_var{}].
diff --git a/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl b/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl
index 2337ef9323..772e30eada 100644
--- a/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl
+++ b/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -70,9 +70,9 @@
%% var - maps variables to expression value numbers. These variables are
%% defined or used by the structure expressions.
--record(maps, {var = gb_trees:empty() :: gb_tree(),
- instr = gb_trees:empty() :: gb_tree(),
- expr = gb_trees:empty() :: gb_tree()}).
+-record(maps, {var = gb_trees:empty() :: gb_trees:tree(),
+ instr = gb_trees:empty() :: gb_trees:tree(),
+ expr = gb_trees:empty() :: gb_trees:tree()}).
maps_var(#maps{var = Out}) -> Out.
maps_instr(#maps{instr = Out}) -> Out.
@@ -211,10 +211,10 @@ varinfo_use_add(#varinfo{use = UseSet} = I, Use) ->
pred = none :: 'none' | [icode_lbl()],
succ = none :: 'none' | [icode_lbl()],
code = [] :: [tuple()], % [illegal_icode_instr()]
- phi = gb_trees:empty() :: gb_tree(),
+ phi = gb_trees:empty() :: gb_trees:tree(),
varmap = [] :: [{icode_var(), icode_var()}],
pre_loop = false :: boolean(),
- non_struct_defs = gb_sets:new() :: gb_set(),
+ non_struct_defs = gb_sets:new() :: gb_sets:set(),
up_expr = none :: 'none' | ?SETS:?SET(_),
killed_expr = none :: 'none' | ?SETS:?SET(_),
sub_inserts = ?SETS:new() :: ?SETS:?SET(_),
@@ -319,7 +319,7 @@ node_create(Label, Pred, Succ) ->
start_label = none :: 'none' | icode_lbl(),
rev_postorder = none :: 'none' | [icode_lbl()],
all_expr = none :: 'none' | [non_neg_integer()],
- tree = gb_trees:empty() :: gb_tree()}).
+ tree = gb_trees:empty() :: gb_trees:tree()}).
nodes_postorder(#nodes{postorder = Out}) -> Out.
nodes_rev_postorder(#nodes{rev_postorder = Out}) -> Out.
@@ -356,7 +356,7 @@ nodes_create() -> #nodes{}.
%% del_red_test - flag that is set to true when the reduction test
%% has been inserted is used to move the reduction test.
--record(update, {inserted = gb_trees:empty() :: gb_tree(),
+-record(update, {inserted = gb_trees:empty() :: gb_trees:tree(),
del_red_test = false :: boolean()}).
update_inserted_lookup(#update{inserted = Inserted}, ExprId) ->
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index 046949d2f2..ebeb5e2c10 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -84,22 +84,22 @@
t_fun/0, t_fun/1, t_fun/2, t_fun_args/1, t_fun_arity/1,
t_inf/2, t_inf_lists/2, t_integer/0,
t_integer/1, t_is_atom/1, t_is_any/1,
- t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1, t_is_boolean/1,
- t_is_fixnum/1, t_is_cons/1,
+ t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1,
+ t_is_boolean/1, t_is_fixnum/1, t_is_cons/1, t_is_map/1,
t_is_maybe_improper_list/1, t_is_equal/2, t_is_float/1,
t_is_fun/1, t_is_integer/1, t_is_non_neg_integer/1,
t_is_number/1, t_is_matchstate/1,
- t_is_nil/1, t_is_none/1, t_is_port/1, t_is_pid/1,
+ t_is_none/1, t_is_port/1, t_is_pid/1,
t_is_reference/1, t_is_subtype/2, t_is_tuple/1,
t_limit/2, t_matchstate_present/1, t_matchstate/0,
- t_matchstate_slots/1, t_maybe_improper_list/0,
+ t_matchstate_slots/1, t_maybe_improper_list/0, t_map/0,
t_nil/0, t_none/0, t_number/0, t_number/1, t_number_vals/1,
t_pid/0, t_port/0, t_reference/0, t_subtract/2, t_sup/2,
t_to_tlist/1, t_tuple/0, t_tuple/1, t_tuple_sizes/1]).
--record(state, {info_map = gb_trees:empty() :: gb_tree(),
+-record(state, {info_map = gb_trees:empty() :: gb_trees:tree(),
cfg :: cfg(),
- liveness = gb_trees:empty() :: gb_tree(),
+ liveness = gb_trees:empty() :: gb_trees:tree(),
arg_types :: [erl_types:erl_type()],
ret_type = [t_none()] :: [erl_types:erl_type()],
lookupfun :: call_fun(),
@@ -213,7 +213,7 @@ analyse_blocks(Work, State, MFA) ->
{NewState, NewLabels} =
try analyse_block(Label, Info, State)
catch throw:none_type ->
- %% io:format("received none type at label: ~p~n",[Label]),
+ %% io:format("received none type at label: ~p~n", [Label]),
{State,[]}
end,
NewWork2 = add_work(NewWork, NewLabels),
@@ -265,7 +265,7 @@ analyse_insn(I, Info, LookupFun) ->
do_move(I, Info);
#icode_call{} ->
NewInfo = do_call(I, Info, LookupFun),
- %%io:format("Analysing Call: ~w~n~w~n", [I,NewInfo]),
+ %% io:format("Analysing Call: ~w~n~w~n", [I, NewInfo]),
update_call_arguments(I, NewInfo);
#icode_phi{} ->
Type = t_limit(join_list(hipe_icode:args(I), Info), ?TYPE_DEPTH),
@@ -788,16 +788,16 @@ test_record(Atom, Size, Var, VarInfo, TrueLab, FalseLab, Info) ->
end.
test_type(Test, Type) ->
- %%io:format("Test is: ~w\n", [Test]),
- %%io:format("Type is: ~s\n", [format_type(Type)]),
+ %% io:format("Test is: ~w\n", [Test]),
+ %% io:format("Type is: ~s\n", [format_type(Type)]),
Ans =
case t_is_any(Type) of
true -> maybe;
false ->
TrueTest = true_branch_info(Test),
Inf = t_inf(TrueTest, Type),
- %%io:format("TrueTest is: ~s\n", [format_type(TrueTest)]),
- %%io:format("Inf is: ~s\n", [format_type(Inf)]),
+ %% io:format("TrueTest is: ~s\n", [format_type(TrueTest)]),
+ %% io:format("Inf is: ~s\n", [format_type(Inf)]),
case t_is_equal(Type, Inf) of
true ->
not t_is_none(Type);
@@ -895,11 +895,12 @@ test_type0(boolean, T) ->
t_is_boolean(T);
test_type0(list, T) ->
t_is_maybe_improper_list(T);
-test_type0(cons, T) ->
- t_is_cons(T);
-test_type0(nil, T) ->
- t_is_nil(T).
-
+%% test_type0(cons, T) ->
+%% t_is_cons(T);
+%% test_type0(nil, T) ->
+%% t_is_nil(T).
+test_type0(map, T) ->
+ t_is_map(T).
true_branch_info(integer) ->
t_integer();
@@ -931,22 +932,24 @@ true_branch_info(reference) ->
t_reference();
true_branch_info(function) ->
t_fun();
-true_branch_info(cons) ->
- t_cons();
-true_branch_info(nil) ->
- t_nil();
+%% true_branch_info(cons) ->
+%% t_cons();
+%% true_branch_info(nil) ->
+%% t_nil();
true_branch_info(boolean) ->
t_boolean();
+true_branch_info(map) ->
+ t_map();
true_branch_info(T) ->
- exit({?MODULE,unknown_typetest,T}).
+ exit({?MODULE, unknown_typetest, T}).
%% _________________________________________________________________
%%
%% Remove the redundant type tests. If a test is removed, the trace
-%% that isn't taken is explicitly removed from the CFG to simpilify
+%% that isn't taken is explicitly removed from the CFG to simplify
%% the handling of Phi nodes. If a Phi node is left and at least one
-%% branch into it has disappeared, the SSA propagation pass can't
+%% branch into it has disappeared, the SSA propagation pass cannot
%% handle it.
%%
%% If the CFG has changed at the end of this pass, the analysis is
diff --git a/lib/hipe/llvm/Makefile b/lib/hipe/llvm/Makefile
new file mode 100644
index 0000000000..92f378924a
--- /dev/null
+++ b/lib/hipe/llvm/Makefile
@@ -0,0 +1,109 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2014. 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%
+#
+
+ifndef EBIN
+EBIN = ../ebin
+endif
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(HIPE_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+ifdef HIPE_ENABLED
+HIPE_MODULES = hipe_rtl_to_llvm \
+ hipe_llvm \
+ elf_format \
+ hipe_llvm_main \
+ hipe_llvm_merge \
+ hipe_llvm_liveness
+else
+HIPE_MODULES =
+endif
+
+MODULES = $(HIPE_MODULES)
+
+HRL_FILES= elf_format.hrl elf32_format.hrl elf64_format.hrl \
+ hipe_llvm_arch.hrl
+ERL_FILES= $(MODULES:%=%.erl)
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+# APP_FILE=
+# App_SRC= $(APP_FILE).src
+# APP_TARGET= $(EBIN)/$(APP_FILE)
+#
+# APPUP_FILE=
+# APPUP_SRC= $(APPUP_FILE).src
+# APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS: Please keep +inline below
+# ----------------------------------------------------
+
+include ../native.mk
+
+ERL_COMPILE_FLAGS += +inline #+warn_missing_spec
+
+# if in 32 bit backend define BIT32 symbol
+ARCH = $(shell echo $(TARGET) | sed 's/^\(x86_64\)-.*/64bit/')
+ifneq ($(ARCH), 64bit)
+ERL_COMPILE_FLAGS += -DBIT32
+endif
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+docs:
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core erl_crash.dump
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/llvm
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/llvm
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
diff --git a/lib/hipe/llvm/elf32_format.hrl b/lib/hipe/llvm/elf32_format.hrl
new file mode 100644
index 0000000000..af1d95bf5b
--- /dev/null
+++ b/lib/hipe/llvm/elf32_format.hrl
@@ -0,0 +1,59 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%% @doc This header file contains very very useful macros for handling
+%%% various segments of an ELF-32 formated object file, such as sizes,
+%%% offsets and predefined constants. For further information about
+%%% each field take a quick look at
+%%% "[http://www.sco.com/developers/gabi/latest/contents.html]"
+%%% that contain the current HP/Intel definition of the ELF object
+%%% file format.
+
+%%------------------------------------------------------------------------------
+%% ELF-32 Data Types (in bytes)
+%%------------------------------------------------------------------------------
+-define(ELF_ADDR_SIZE, 4).
+-define(ELF_OFF_SIZE, 4).
+-define(ELF_HALF_SIZE, 2).
+-define(ELF_WORD_SIZE, 4).
+-define(ELF_SWORD_SIZE, 4).
+-define(ELF_XWORD_SIZE, ?ELF_WORD_SIZE). % for compatibility
+-define(ELF_SXWORD_SIZE, ?ELF_WORD_SIZE).
+-define(ELF_UNSIGNED_CHAR_SIZE, 1).
+
+%%------------------------------------------------------------------------------
+%% ELF-32 Symbol Table Entries
+%%------------------------------------------------------------------------------
+%% Precomputed offset for Symbol Table entries in SymTab binary (needed because
+%% of the different offsets in 32 and 64 bit formats).
+-define(ST_NAME_OFFSET, 0).
+-define(ST_VALUE_OFFSET, (?ST_NAME_OFFSET + ?ST_NAME_SIZE) ).
+-define(ST_SIZE_OFFSET, (?ST_VALUE_OFFSET + ?ST_VALUE_SIZE) ).
+-define(ST_INFO_OFFSET, (?ST_SIZE_OFFSET + ?ST_SIZE_SIZE) ).
+-define(ST_OTHER_OFFSET, (?ST_INFO_OFFSET + ?ST_INFO_SIZE) ).
+-define(ST_SHNDX_OFFSET, (?ST_OTHER_OFFSET + ?ST_OTHER_SIZE) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Relocation Entries
+%%------------------------------------------------------------------------------
+%% Useful macros to extract information from r_info field
+-define(ELF_R_SYM(I), (I bsr 8) ).
+-define(ELF_R_TYPE(I), (I band 16#ff) ).
+-define(ELF_R_INFO(S, T), ((S bsl 8) + (T band 16#ff)) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Program Header Table
+%%------------------------------------------------------------------------------
+%% Offsets of various fields in a Program Header Table entry binary.
+-define(P_TYPE_OFFSET, 0).
+-define(P_OFFSET_OFFSET, (?P_FLAGS_OFFSET + ?P_FLAGS_SIZE) ).
+-define(P_VADDR_OFFSET, (?P_OFFSET_OFFSET + ?P_OFFSET_SIZE) ).
+-define(P_PADDR_OFFSET, (?P_VADDR_OFFSET + ?P_VADDR_SIZE) ).
+-define(P_FILESZ_OFFSET, (?P_PVADDR_OFFSET + ?P_PVADDR_SIZE) ).
+-define(P_MEMSZ_OFFSET, (?P_FILESZ_OFFSET + ?P_FILESZ_SIZE) ).
+-define(P_FLAGS_OFFSET, (?P_TYPE_OFFSET + ?P_TYPE_SIZE) ).
+-define(P_ALIGN_OFFSET, (?P_MEMSZ_OFFSET + ?P_MEMSZ_SIZE) ).
diff --git a/lib/hipe/llvm/elf64_format.hrl b/lib/hipe/llvm/elf64_format.hrl
new file mode 100644
index 0000000000..794746ffdc
--- /dev/null
+++ b/lib/hipe/llvm/elf64_format.hrl
@@ -0,0 +1,58 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%% @doc This header file contains very very useful macros for handling
+%%% various segments of an ELF-64 formated object file, such as sizes,
+%%% offsets and predefined constants. For further information about
+%%% each field take a quick look at
+%%% "[http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf]"
+%%% that contain the current HP/Intel definition of the ELF object
+%%% file format.
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Data Types (in bytes)
+%%------------------------------------------------------------------------------
+-define(ELF_ADDR_SIZE, 8).
+-define(ELF_OFF_SIZE, 8).
+-define(ELF_HALF_SIZE, 2).
+-define(ELF_WORD_SIZE, 4).
+-define(ELF_SWORD_SIZE, 4).
+-define(ELF_XWORD_SIZE, 8).
+-define(ELF_SXWORD_SIZE, 8).
+-define(ELF_UNSIGNED_CHAR_SIZE, 1).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Symbol Table Entries
+%%------------------------------------------------------------------------------
+%% Precomputed offset for Symbol Table entries in SymTab binary
+-define(ST_NAME_OFFSET, 0).
+-define(ST_INFO_OFFSET, (?ST_NAME_OFFSET + ?ST_NAME_SIZE) ).
+-define(ST_OTHER_OFFSET, (?ST_INFO_OFFSET + ?ST_INFO_SIZE) ).
+-define(ST_SHNDX_OFFSET, (?ST_OTHER_OFFSET + ?ST_OTHER_SIZE) ).
+-define(ST_VALUE_OFFSET, (?ST_SHNDX_OFFSET + ?ST_SHNDX_SIZE) ).
+-define(ST_SIZE_OFFSET, (?ST_VALUE_OFFSET + ?ST_VALUE_SIZE) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Relocation Entries
+%%------------------------------------------------------------------------------
+%% Useful macros to extract information from r_info field
+-define(ELF_R_SYM(I), (I bsr 32) ).
+-define(ELF_R_TYPE(I), (I band 16#ffffffff) ).
+-define(ELF_R_INFO(S, T), ((S bsl 32) + (T band 16#ffffffff)) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Program Header Table
+%%------------------------------------------------------------------------------
+%% Offsets of various fields in a Program Header Table entry binary.
+-define(P_TYPE_OFFSET, 0).
+-define(P_FLAGS_OFFSET, (?P_TYPE_OFFSET + ?P_TYPE_SIZE) ).
+-define(P_OFFSET_OFFSET, (?P_FLAGS_OFFSET + ?P_FLAGS_SIZE) ).
+-define(P_VADDR_OFFSET, (?P_OFFSET_OFFSET + ?P_OFFSET_SIZE) ).
+-define(P_PADDR_OFFSET, (?P_VADDR_OFFSET + ?P_VADDR_SIZE) ).
+-define(P_FILESZ_OFFSET, (?P_PVADDR_OFFSET + ?P_PVADDR_SIZE) ).
+-define(P_MEMSZ_OFFSET, (?P_FILESZ_OFFSET + ?P_FILESZ_SIZE) ).
+-define(P_ALIGN_OFFSET, (?P_MEMSZ_OFFSET + ?P_MEMSZ_SIZE) ).
diff --git a/lib/hipe/llvm/elf_format.erl b/lib/hipe/llvm/elf_format.erl
new file mode 100644
index 0000000000..260da9b5e6
--- /dev/null
+++ b/lib/hipe/llvm/elf_format.erl
@@ -0,0 +1,790 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>,
+%%% Kostis Sagonas <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%% @doc This module contains functions for extracting various pieces of
+%%% information from an ELF formated Object file. To fully understand
+%%% the ELF format and the use of these functions please read
+%%% "[http://www.linuxjournal.com/article/1060?page=0,0]" carefully.
+
+-module(elf_format).
+
+-export([get_tab_entries/1,
+ %% Relocations
+ get_rodata_relocs/1,
+ get_text_relocs/1,
+ extract_rela/2,
+ get_rela_addends/1,
+ %% Note
+ extract_note/2,
+ %% Executable code
+ extract_text/1,
+ %% GCC Exception Table
+ get_exn_handlers/1,
+ %% Misc.
+ set_architecture_flag/1,
+ is64bit/0
+ ]).
+
+-include("elf_format.hrl").
+
+%%------------------------------------------------------------------------------
+%% Types
+%%------------------------------------------------------------------------------
+
+-type elf() :: binary().
+
+-type lp() :: non_neg_integer(). % landing pad
+-type num() :: non_neg_integer().
+-type index() :: non_neg_integer().
+-type offset() :: non_neg_integer().
+-type size() :: non_neg_integer().
+-type start() :: non_neg_integer().
+
+-type info() :: index().
+-type nameoff() :: offset().
+-type valueoff() :: offset().
+
+-type name() :: string().
+-type name_size() :: {name(), size()}.
+-type name_sizes() :: [name_size()].
+
+%%------------------------------------------------------------------------------
+%% Abstract Data Types and Accessors for ELF Structures.
+%%------------------------------------------------------------------------------
+
+%% File header
+-record(elf_ehdr, {ident, % ELF identification
+ type, % Object file type
+ machine, % Machine Type
+ version, % Object file version
+ entry, % Entry point address
+ phoff, % Program header offset
+ shoff :: offset(), % Section header offset
+ flags, % Processor-specific flags
+ ehsize :: size(), % ELF header size
+ phentsize :: size(), % Size of program header entry
+ phnum :: num(), % Number of program header entries
+ shentsize :: size(), % Size of section header entry
+ shnum :: num(), % Number of section header entries
+ shstrndx :: index() % Section name string table index
+ }).
+-type elf_ehdr() :: #elf_ehdr{}.
+
+-record(elf_ehdr_ident, {class, % File class
+ data, % Data encoding
+ version, % File version
+ osabi, % OS/ABI identification
+ abiversion, % ABI version
+ pad, % Start of padding bytes
+ nident % Size of e_ident[]
+ }).
+%% -type elf_ehdr_ident() :: #elf_ehdr_ident{}.
+
+%% Section header entries
+-record(elf_shdr, {name, % Section name
+ type, % Section type
+ flags, % Section attributes
+ addr, % Virtual address in memory
+ offset :: offset(), % Offset in file
+ size :: size(), % Size of section
+ link, % Link to other section
+ info, % Miscellaneous information
+ addralign, % Address align boundary
+ entsize % Size of entries, if section has table
+ }).
+%% -type elf_shdr() :: #elf_shdr{}.
+
+%% Symbol table entries
+-record(elf_sym, {name :: nameoff(), % Symbol name
+ info, % Type and Binding attributes
+ other, % Reserved
+ shndx, % Section table index
+ value :: valueoff(), % Symbol value
+ size :: size() % Size of object
+ }).
+-type elf_sym() :: #elf_sym{}.
+
+%% Relocations
+-record(elf_rel, {r_offset :: offset(), % Address of reference
+ r_info :: info() % Symbol index and type of relocation
+ }).
+-type elf_rel() :: #elf_rel{}.
+
+-record(elf_rela, {r_offset :: offset(), % Address of reference
+ r_info :: info(), % Symbol index and type of relocation
+ r_addend :: offset() % Constant part of expression
+ }).
+-type elf_rela() :: #elf_rela{}.
+
+%% %% Program header table
+%% -record(elf_phdr, {type, % Type of segment
+%% flags, % Segment attributes
+%% offset, % Offset in file
+%% vaddr, % Virtual address in memory
+%% paddr, % Reserved
+%% filesz, % Size of segment in file
+%% memsz, % Size of segment in memory
+%% align % Alignment of segment
+%% }).
+
+%% %% GCC exception table
+%% -record(elf_gccexntab, {lpbenc, % Landing pad base encoding
+%% lpbase, % Landing pad base
+%% ttenc, % Type table encoding
+%% ttoff, % Type table offset
+%% csenc, % Call-site table encoding
+%% cstabsize, % Call-site table size
+%% cstab :: cstab() % Call-site table
+%% }).
+%% -type elf_gccexntab() :: #elf_gccexntab{}.
+
+-record(elf_gccexntab_callsite, {start :: start(), % Call-site start
+ size :: size(), % Call-site size
+ lp :: lp(), % Call-site landing pad
+ % (exception handler)
+ onaction % On action (e.g. cleanup)
+ }).
+%% -type elf_gccexntab_callsite() :: #elf_gccexntab_callsite{}.
+
+%%------------------------------------------------------------------------------
+%% Accessor Functions
+%%------------------------------------------------------------------------------
+
+%% File header
+%% -spec mk_ehdr(...) -> elf_ehrd().
+mk_ehdr(Ident, Type, Machine, Version, Entry, Phoff, Shoff, Flags, Ehsize,
+ Phentsize, Phnum, Shentsize, Shnum, Shstrndx) ->
+ #elf_ehdr{ident = Ident, type = Type, machine = Machine, version = Version,
+ entry = Entry, phoff = Phoff, shoff = Shoff, flags = Flags,
+ ehsize = Ehsize, phentsize = Phentsize, phnum = Phnum,
+ shentsize = Shentsize, shnum = Shnum, shstrndx = Shstrndx}.
+
+%% -spec ehdr_shoff(elf_ehdr()) -> offset().
+%% ehdr_shoff(#elf_ehdr{shoff = Offset}) -> Offset.
+%%
+%% -spec ehdr_shentsize(elf_ehdr()) -> size().
+%% ehdr_shentsize(#elf_ehdr{shentsize = Size}) -> Size.
+%%
+%% -spec ehdr_shnum(elf_ehdr()) -> num().
+%% ehdr_shnum(#elf_ehdr{shnum = Num}) -> Num.
+%%
+%% -spec ehdr_shstrndx(elf_ehdr()) -> index().
+%% ehdr_shstrndx(#elf_ehdr{shstrndx = Index}) -> Index.
+
+
+%%-spec mk_ehdr_ident(...) -> elf_ehdr_ident().
+mk_ehdr_ident(Class, Data, Version, OsABI, AbiVersion, Pad, Nident) ->
+ #elf_ehdr_ident{class = Class, data = Data, version = Version, osabi = OsABI,
+ abiversion = AbiVersion, pad = Pad, nident = Nident}.
+
+%%%-------------------------
+%%% Section header entries
+%%%-------------------------
+mk_shdr(Name, Type, Flags, Addr, Offset, Size, Link, Info, AddrAlign, EntSize) ->
+ #elf_shdr{name = Name, type = Type, flags = Flags, addr = Addr,
+ offset = Offset, size = Size, link = Link, info = Info,
+ addralign = AddrAlign, entsize = EntSize}.
+
+%% -spec shdr_offset(elf_shdr()) -> offset().
+%% shdr_offset(#elf_shdr{offset = Offset}) -> Offset.
+%%
+%% -spec shdr_size(elf_shdr()) -> size().
+%% shdr_size(#elf_shdr{size = Size}) -> Size.
+
+%%%-------------------------
+%%% Symbol Table Entries
+%%%-------------------------
+mk_sym(Name, Info, Other, Shndx, Value, Size) ->
+ #elf_sym{name = Name, info = Info, other = Other,
+ shndx = Shndx, value = Value, size = Size}.
+
+-spec sym_name(elf_sym()) -> nameoff().
+sym_name(#elf_sym{name = Name}) -> Name.
+
+%% -spec sym_value(elf_sym()) -> valueoff().
+%% sym_value(#elf_sym{value = Value}) -> Value.
+%%
+%% -spec sym_size(elf_sym()) -> size().
+%% sym_size(#elf_sym{size = Size}) -> Size.
+
+%%%-------------------------
+%%% Relocations
+%%%-------------------------
+-spec mk_rel(offset(), info()) -> elf_rel().
+mk_rel(Offset, Info) ->
+ #elf_rel{r_offset = Offset, r_info = Info}.
+
+%% The following two functions capitalize on the fact that the two kinds of
+%% relocation records (for 32- and 64-bit architectures have similar structure.
+
+-spec r_offset(elf_rel() | elf_rela()) -> offset().
+r_offset(#elf_rel{r_offset = Offset}) -> Offset;
+r_offset(#elf_rela{r_offset = Offset}) -> Offset.
+
+-spec r_info(elf_rel() | elf_rela()) -> info().
+r_info(#elf_rel{r_info = Info}) -> Info;
+r_info(#elf_rela{r_info = Info}) -> Info.
+
+-spec mk_rela(offset(), info(), offset()) -> elf_rela().
+mk_rela(Offset, Info, Addend) ->
+ #elf_rela{r_offset = Offset, r_info = Info, r_addend = Addend}.
+
+-spec rela_addend(elf_rela()) -> offset().
+rela_addend(#elf_rela{r_addend = Addend}) -> Addend.
+
+%% %%%-------------------------
+%% %%% GCC exception table
+%% %%%-------------------------
+%% -type cstab() :: [elf_gccexntab_callsite()].
+%%
+%% mk_gccexntab(LPbenc, LPbase, TTenc, TToff, CSenc, CStabsize, CStab) ->
+%% #elf_gccexntab{lpbenc = LPbenc, lpbase = LPbase, ttenc = TTenc,
+%% ttoff = TToff, csenc = CSenc, cstabsize = CStabsize,
+%% cstab = CStab}.
+%%
+%% -spec gccexntab_cstab(elf_gccexntab()) -> cstab().
+%% gccexntab_cstab(#elf_gccexntab{cstab = CSTab}) -> CSTab.
+
+mk_gccexntab_callsite(Start, Size, LP, Action) ->
+ #elf_gccexntab_callsite{start = Start, size=Size, lp=LP, onaction=Action}.
+
+%% -spec gccexntab_callsite_start(elf_gccexntab_callsite()) -> start().
+%% gccexntab_callsite_start(#elf_gccexntab_callsite{start = Start}) -> Start.
+%%
+%% -spec gccexntab_callsite_size(elf_gccexntab_callsite()) -> size().
+%% gccexntab_callsite_size(#elf_gccexntab_callsite{size = Size}) -> Size.
+%%
+%% -spec gccexntab_callsite_lp(elf_gccexntab_callsite()) -> lp().
+%% gccexntab_callsite_lp(#elf_gccexntab_callsite{lp = LP}) -> LP.
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate the ELF File Header
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts the File Header from an ELF formatted object file. Also sets
+%% the ELF class variable in the process dictionary (used by many functions
+%% in this and hipe_llvm_main modules).
+-spec extract_header(elf()) -> elf_ehdr().
+extract_header(Elf) ->
+ Ehdr_bin = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
+ << %% Structural pattern matching on fields.
+ Ident_bin:?E_IDENT_SIZE/binary,
+ Type:?bits(?E_TYPE_SIZE)/integer-little,
+ Machine:?bits(?E_MACHINE_SIZE)/integer-little,
+ Version:?bits(?E_VERSION_SIZE)/integer-little,
+ Entry:?bits(?E_ENTRY_SIZE)/integer-little,
+ Phoff:?bits(?E_PHOFF_SIZE)/integer-little,
+ Shoff:?bits(?E_SHOFF_SIZE)/integer-little,
+ Flags:?bits(?E_FLAGS_SIZE)/integer-little,
+ Ehsize:?bits(?E_EHSIZE_SIZE)/integer-little,
+ Phentsize:?bits(?E_PHENTSIZE_SIZE)/integer-little,
+ Phnum:?bits(?E_PHNUM_SIZE)/integer-little,
+ Shentsize:?bits(?E_SHENTSIZE_SIZE)/integer-little,
+ Shnum:?bits(?E_SHENTSIZE_SIZE)/integer-little,
+ Shstrndx:?bits(?E_SHSTRNDX_SIZE)/integer-little
+ >> = Ehdr_bin,
+ <<16#7f, $E, $L, $F, Class, Data, Version, Osabi, Abiversion,
+ Pad:6/binary, Nident
+ >> = Ident_bin,
+ Ident = mk_ehdr_ident(Class, Data, Version, Osabi,
+ Abiversion, Pad, Nident),
+ mk_ehdr(Ident, Type, Machine, Version, Entry, Phoff, Shoff, Flags,
+ Ehsize, Phentsize, Phnum, Shentsize, Shnum, Shstrndx).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Section Header Entries
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts the Section Header Table from an ELF formated Object File.
+extract_shdrtab(Elf) ->
+ %% Extract File Header to get info about Section Header Offset (in bytes),
+ %% Entry Size (in bytes) and Number of entries
+ #elf_ehdr{shoff = ShOff, shentsize = ShEntsize, shnum = ShNum} =
+ extract_header(Elf),
+ %% Get actual Section header table (binary)
+ ShdrBin = get_binary_segment(Elf, ShOff, ShNum * ShEntsize),
+ get_shdrtab_entries(ShdrBin, []).
+
+get_shdrtab_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_shdrtab_entries(ShdrBin, Acc) ->
+ <<%% Structural pattern matching on fields.
+ Name:?bits(?SH_NAME_SIZE)/integer-little,
+ Type:?bits(?SH_TYPE_SIZE)/integer-little,
+ Flags:?bits(?SH_FLAGS_SIZE)/integer-little,
+ Addr:?bits(?SH_ADDR_SIZE)/integer-little,
+ Offset:?bits(?SH_OFFSET_SIZE)/integer-little,
+ Size:?bits(?SH_SIZE_SIZE)/integer-little,
+ Link:?bits(?SH_LINK_SIZE)/integer-little,
+ Info:?bits(?SH_INFO_SIZE)/integer-little,
+ Addralign:?bits(?SH_ADDRALIGN_SIZE)/integer-little,
+ Entsize:?bits(?SH_ENTSIZE_SIZE)/integer-little,
+ MoreShdrE/binary
+ >> = ShdrBin,
+ ShdrE = mk_shdr(Name, Type, Flags, Addr, Offset,
+ Size, Link, Info, Addralign, Entsize),
+ get_shdrtab_entries(MoreShdrE, [ShdrE | Acc]).
+
+%% @doc Extracts a specific Entry of a Section Header Table. This function
+%% takes as argument the Section Header Table (`SHdrTab') and the entry's
+%% serial number (`EntryNum') and returns the entry (`shdr').
+get_shdrtab_entry(SHdrTab, EntryNum) ->
+ lists:nth(EntryNum + 1, SHdrTab).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Section Header String Table
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts the Section Header String Table. This section is not a known
+%% ELF Object File section. It is just a "hidden" table storing the
+%% names of all sections that exist in current object file.
+-spec extract_shstrtab(elf()) -> [name()].
+extract_shstrtab(Elf) ->
+ %% Extract Section Name String Table Index
+ #elf_ehdr{shstrndx = ShStrNdx} = extract_header(Elf),
+ ShHdrTab = extract_shdrtab(Elf),
+ %% Extract Section header entry and get actual Section-header String Table
+ #elf_shdr{offset = ShStrOffset, size = ShStrSize} =
+ get_shdrtab_entry(ShHdrTab, ShStrNdx),
+ case get_binary_segment(Elf, ShStrOffset, ShStrSize) of
+ <<>> -> %% Segment empty
+ [];
+ ShStrTab -> %% Convert to string table
+ [Name || {Name, _Size} <- get_names(ShStrTab)]
+ end.
+
+%%------------------------------------------------------------------------------
+
+-spec get_tab_entries(elf()) -> [{name(), valueoff(), size()}].
+get_tab_entries(Elf) ->
+ SymTab = extract_symtab(Elf),
+ Ts = [{Name, Value, Size div ?ELF_XWORD_SIZE}
+ || #elf_sym{name = Name, value = Value, size = Size} <- SymTab,
+ Name =/= 0],
+ {NameIndices, ValueOffs, Sizes} = lists:unzip3(Ts),
+ %% Find the names of the symbols.
+ %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
+ %% relevant entries:
+ StrTab = extract_strtab(Elf),
+ Relevant = [get_strtab_entry(StrTab, Off) || Off <- NameIndices],
+ %% Zip back to {Name, ValueOff, Size}
+ lists:zip3(Relevant, ValueOffs, Sizes).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Symbol Table
+%%------------------------------------------------------------------------------
+
+%% @doc Function that extracts Symbol Table from an ELF Object file.
+extract_symtab(Elf) ->
+ Symtab_bin = extract_segment_by_name(Elf, ?SYMTAB),
+ get_symtab_entries(Symtab_bin, []).
+
+get_symtab_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_symtab_entries(Symtab_bin, Acc) ->
+ <<SymE_bin:?ELF_SYM_SIZE/binary, MoreSymE/binary>> = Symtab_bin,
+ case is64bit() of
+ true ->
+ <<%% Structural pattern matching on fields.
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little
+ >> = SymE_bin;
+ false ->
+ << %% Same fields in different order:
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little
+ >> = SymE_bin
+ end,
+ SymE = mk_sym(Name, Info, Other, Shndx, Value, Size),
+ get_symtab_entries(MoreSymE, [SymE | Acc]).
+
+%% @doc Extracts a specific entry from the Symbol Table (as binary).
+%% This function takes as arguments the Symbol Table (`SymTab')
+%% and the entry's serial number and returns that entry (`sym').
+get_symtab_entry(SymTab, EntryNum) ->
+ lists:nth(EntryNum + 1, SymTab).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate String Table
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts String Table from an ELF formated Object File.
+-spec extract_strtab(elf()) -> [{string(), offset()}].
+extract_strtab(Elf) ->
+ Strtab_bin = extract_segment_by_name(Elf, ?STRTAB),
+ NamesSizes = get_names(Strtab_bin),
+ make_offsets(NamesSizes).
+
+%% @doc Returns the name of the symbol at the given offset. The string table
+%% contains entries of the form {Name, Offset}. If no such offset exists
+%% returns the empty string (`""').
+%% XXX: There might be a bug here because of the "compact" saving the ELF
+%% format uses: e.g. only stores ".rela.text" for ".rela.text" and ".text".
+get_strtab_entry(Strtab, Offset) ->
+ case lists:keyfind(Offset, 2, Strtab) of
+ {Name, Offset} -> Name;
+ false -> ""
+ end.
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Relocations
+%%------------------------------------------------------------------------------
+
+%% @doc This function gets as argument an ELF binary file and returns a list
+%% with all .rela.rodata labels (i.e. constants and literals in code)
+%% or an empty list if no ".rela.rodata" section exists in code.
+-spec get_rodata_relocs(elf()) -> [offset()].
+get_rodata_relocs(Elf) ->
+ case is64bit() of
+ true ->
+ %% Only care about the addends (== offsets):
+ get_rela_addends(extract_rela(Elf, ?RODATA));
+ false ->
+ %% Find offsets hardcoded in ".rodata" entry
+ %%XXX: Treat all 0s as padding and skip them!
+ [SkipPadding || SkipPadding <- extract_rodata(Elf), SkipPadding =/= 0]
+ end.
+
+-spec get_rela_addends([elf_rela()]) -> [offset()].
+get_rela_addends(RelaEntries) ->
+ [rela_addend(E) || E <- RelaEntries].
+
+%% @doc Extract a list of the form `[{SymbolName, Offset}]' with all relocatable
+%% symbols and their offsets in the code from the ".text" section.
+-spec get_text_relocs(elf()) -> [{name(), offset()}].
+get_text_relocs(Elf) ->
+ %% Only care about the symbol table index and the offset:
+ NameOffsetTemp = [{?ELF_R_SYM(r_info(E)), r_offset(E)}
+ || E <- extract_rela(Elf, ?TEXT)],
+ {NameIndices, ActualOffsets} = lists:unzip(NameOffsetTemp),
+ %% Find the names of the symbols:
+ %%
+ %% Get those symbol table entries that are related to Text relocs:
+ Symtab = extract_symtab(Elf),
+ SymtabEs = [get_symtab_entry(Symtab, Index) || Index <- NameIndices],
+ %XXX: not zero-indexed!
+ %% Symbol table entries contain the offset of the name of the symbol in
+ %% String Table:
+ SymtabEs2 = [sym_name(E) || E <- SymtabEs], %XXX: Do we need to sort SymtabE?
+ %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
+ %% relevant entries:
+ Strtab = extract_strtab(Elf),
+ Relevant = [get_strtab_entry(Strtab, Off) || Off <- SymtabEs2],
+ %% Zip back with actual offsets:
+ lists:zip(Relevant, ActualOffsets).
+
+%% @doc Extract the Relocations segment for section `Name' (that is passed
+%% as second argument) from an ELF formated Object file binary.
+-spec extract_rela(elf(), name()) -> [elf_rel() | elf_rela()].
+extract_rela(Elf, Name) ->
+ SegName =
+ case is64bit() of
+ true -> ?RELA(Name); % ELF-64 uses ".rela"
+ false -> ?REL(Name) % ...while ELF-32 uses ".rel"
+ end,
+ Rela_bin = extract_segment_by_name(Elf, SegName),
+ get_rela_entries(Rela_bin, []).
+
+get_rela_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_rela_entries(Bin, Acc) ->
+ E = case is64bit() of
+ true ->
+ <<%% Structural pattern matching on fields of a Rela Entry.
+ Offset:?bits(?R_OFFSET_SIZE)/integer-little,
+ Info:?bits(?R_INFO_SIZE)/integer-little,
+ Addend:?bits(?R_ADDEND_SIZE)/integer-little,
+ Rest/binary
+ >> = Bin,
+ mk_rela(Offset, Info, Addend);
+ false ->
+ <<%% Structural pattern matching on fields of a Rel Entry.
+ Offset:?bits(?R_OFFSET_SIZE)/integer-little,
+ Info:?bits(?R_INFO_SIZE)/integer-little,
+ Rest/binary
+ >> = Bin,
+ mk_rel(Offset, Info)
+ end,
+ get_rela_entries(Rest, [E | Acc]).
+
+%% %% @doc Extract the `EntryNum' (serial number) Relocation Entry.
+%% get_rela_entry(Rela, EntryNum) ->
+%% lists:nth(EntryNum + 1, Rela).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Executable Code segment
+%%------------------------------------------------------------------------------
+
+%% @doc This function gets as arguments an ELF formated binary file and
+%% returns the Executable Code (".text" segment) or an empty binary if it
+%% is not found.
+-spec extract_text(elf()) -> binary().
+extract_text(Elf) ->
+ extract_segment_by_name(Elf, ?TEXT).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Note Section
+%%------------------------------------------------------------------------------
+
+%% @doc Extract specific Note Section from an ELF Object file. The function
+%% takes as first argument the object file (`Elf') and the `Name' of the
+%% wanted Note Section (<b>without</b> the ".note" prefix!). It returns
+%% the specified binary segment or an empty binary if no such section
+%% exists.
+-spec extract_note(elf(), string()) -> binary().
+extract_note(Elf, Name) ->
+ extract_segment_by_name(Elf, ?NOTE(Name)).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate GCC Exception Table segment
+%%------------------------------------------------------------------------------
+
+%% A description for the C++ exception table formats can be found at Exception
+%% Handling Tables (http://www.codesourcery.com/cxx-abi/exceptions.pdf).
+
+%% A list with `{Start, End, HandlerOffset}' for all call sites in the code
+-spec get_exn_handlers(elf()) -> [{start(), start(), lp()}].
+get_exn_handlers(Elf) ->
+ CallSites = extract_gccexntab_callsites(Elf),
+ [{Start, Start + Size, LP}
+ || #elf_gccexntab_callsite{start = Start, size = Size, lp = LP} <- CallSites].
+
+%% @doc This function gets as argument an ELF binary file and returns
+%% the table (list) of call sites which is stored in GCC
+%% Exception Table (".gcc_except_table") section.
+%% It returns an empty list if the Exception Table is not found.
+%% XXX: Assumes there is *no* Action Record Table.
+extract_gccexntab_callsites(Elf) ->
+ case extract_segment_by_name(Elf, ?GCC_EXN_TAB) of
+ <<>> ->
+ [];
+ ExnTab ->
+ %% First byte of LSDA is Landing Pad base encoding.
+ <<LBenc:8, More/binary>> = ExnTab,
+ %% Second byte is the Landing Pad base (if its encoding is not
+ %% DW_EH_PE_omit) (optional).
+ {_LPBase, LSDACont} =
+ case LBenc =:= ?DW_EH_PE_omit of
+ true -> % No landing pad base byte. (-1 denotes that)
+ {-1, More};
+ false -> % Landing pad base.
+ <<Base:8, More2/binary>> = More,
+ {Base, More2}
+ end,
+ %% Next byte of LSDA is the encoding of the Type Table.
+ <<TTenc:8, More3/binary>> = LSDACont,
+ %% Next byte is the Types Table offset encoded in U-LEB128 (optional).
+ {_TTOff, LSDACont2} =
+ case TTenc =:= ?DW_EH_PE_omit of
+ true -> % There is no Types Table pointer. (-1 denotes that)
+ {-1, More3};
+ false -> % The byte offset from this field to the start of the Types
+ % Table used for exception matching.
+ leb128_decode(More3)
+ end,
+ %% Next byte of LSDA is the encoding of the fields in the Call-site Table.
+ <<_CSenc:8, More4/binary>> = LSDACont2,
+ %% Sixth byte is the size (in bytes) of the Call-site Table encoded in
+ %% U-LEB128.
+ {_CSTabSize, CSTab} = leb128_decode(More4),
+ %% Extract all call site information
+ get_gccexntab_callsites(CSTab, [])
+ end.
+
+get_gccexntab_callsites(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_gccexntab_callsites(CSTab, Acc) ->
+ %% We are only interested in the Landing Pad of every entry.
+ <<Start:32/integer-little, Size:32/integer-little,
+ LP:32/integer-little, OnAction:8, More/binary
+ >> = CSTab,
+ GccCS = mk_gccexntab_callsite(Start, Size, LP, OnAction),
+ get_gccexntab_callsites(More, [GccCS | Acc]).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Read-only Data (.rodata)
+%%------------------------------------------------------------------------------
+extract_rodata(Elf) ->
+ Rodata_bin = extract_segment_by_name(Elf, ?RODATA),
+ get_rodata_entries(Rodata_bin, []).
+
+get_rodata_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_rodata_entries(Rodata_bin, Acc) ->
+ <<Num:?bits(?ELF_ADDR_SIZE)/integer-little, More/binary>> = Rodata_bin,
+ get_rodata_entries(More, [Num | Acc]).
+
+%%------------------------------------------------------------------------------
+%% Helper functions
+%%------------------------------------------------------------------------------
+
+%% @doc Returns the binary segment starting at `Offset' with length `Size'
+%% (bytes) from a binary file. If `Offset' is bigger than the byte size of
+%% the binary, an empty binary (`<<>>') is returned.
+-spec get_binary_segment(binary(), offset(), size()) -> binary().
+get_binary_segment(Bin, Offset, _Size) when Offset > byte_size(Bin) ->
+ <<>>;
+get_binary_segment(Bin, Offset, Size) ->
+ <<_Hdr:Offset/binary, BinSeg:Size/binary, _More/binary>> = Bin,
+ BinSeg.
+
+%% @doc This function gets as arguments an ELF formated binary object and
+%% a string with the segments' name and returns the specified segment or
+%% an empty binary (`<<>>') if there exists no segment with that name.
+%% There are handy macros defined in elf_format.hrl for all Standard
+%% Section Names.
+-spec extract_segment_by_name(elf(), string()) -> binary().
+extract_segment_by_name(Elf, SectionName) ->
+ %% Extract Section Header Table and Section Header String Table from binary
+ SHdrTable = extract_shdrtab(Elf),
+ Names = extract_shstrtab(Elf),
+ %% Zip to a list of (Name,ShdrE)
+ [_Zero | ShdrEs] = lists:keysort(2, SHdrTable), % Skip first entry (zeros).
+ L = lists:zip(Names, ShdrEs),
+ %% Find Section Header Table entry by name
+ case lists:keyfind(SectionName, 1, L) of
+ {SectionName, ShdrE} -> %% Note: Same name.
+ #elf_shdr{offset = Offset, size = Size} = ShdrE,
+ get_binary_segment(Elf, Offset, Size);
+ false -> %% Not found.
+ <<>>
+ end.
+
+%% @doc Extracts a list of strings with (zero-separated) names from a binary.
+%% Returns tuples of `{Name, Size}'.
+%% XXX: Skip trailing 0.
+-spec get_names(<<_:8,_:_*8>>) -> name_sizes().
+get_names(<<0, Bin/binary>>) ->
+ NamesSizes = get_names(Bin, []),
+ fix_names(NamesSizes, []).
+
+get_names(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_names(Bin, Acc) ->
+ {Name, MoreNames} = bin_get_string(Bin),
+ get_names(MoreNames, [{Name, length(Name)} | Acc]).
+
+%% @doc Fix names:
+%% e.g. If ".rela.text" exists, ".text" does not. Same goes for
+%% ".rel.text". In that way, the Section Header String Table is more
+%% compact. Add ".text" just *before* the corresponding rela-field,
+%% etc.
+-spec fix_names(name_sizes(), name_sizes()) -> name_sizes().
+fix_names([], Acc) ->
+ lists:reverse(Acc);
+fix_names([{Name, Size}=T | Names], Acc) ->
+ case is64bit() of
+ true ->
+ case string:str(Name, ".rela") =:= 1 of
+ true -> %% Name starts with ".rela":
+ Section = string:substr(Name, 6),
+ fix_names(Names, [{Section, Size - 5}
+ | [T | Acc]]); % XXX: Is order ok? (".text"
+ % always before ".rela.text")
+ false -> %% Name does not start with ".rela":
+ fix_names(Names, [T | Acc])
+ end;
+ false ->
+ case string:str(Name, ".rel") =:= 1 of
+ true -> %% Name starts with ".rel":
+ Section = string:substr(Name, 5),
+ fix_names(Names, [{Section, Size - 4}
+ | [T | Acc]]); % XXX: Is order ok? (".text"
+ % always before ".rela.text")
+ false -> %% Name does not start with ".rel":
+ fix_names(Names, [T | Acc])
+ end
+ end.
+
+
+%% @doc A function that byte-reverses a binary. This might be needed because of
+%% little (fucking!) endianess.
+-spec bin_reverse(binary()) -> binary().
+bin_reverse(Bin) when is_binary(Bin) ->
+ bin_reverse(Bin, <<>>).
+
+-spec bin_reverse(binary(), binary()) -> binary().
+bin_reverse(<<>>, Acc) ->
+ Acc;
+bin_reverse(<<Head, More/binary>>, Acc) ->
+ bin_reverse(More, <<Head, Acc/binary>>).
+
+%% @doc A function that extracts a null-terminated string from a binary. It
+%% returns the found string along with the rest of the binary.
+-spec bin_get_string(binary()) -> {string(), binary()}.
+bin_get_string(Bin) ->
+ bin_get_string(Bin, <<>>).
+
+bin_get_string(<<>>, BinAcc) ->
+ Bin = bin_reverse(BinAcc), % little endian!
+ {binary_to_list(Bin), <<>>};
+bin_get_string(<<0, MoreBin/binary>>, BinAcc) ->
+ Bin = bin_reverse(BinAcc), % little endian!
+ {binary_to_list(Bin), MoreBin};
+bin_get_string(<<Letter, Tail/binary>>, BinAcc) ->
+ bin_get_string(Tail, <<Letter, BinAcc/binary>>).
+
+%% @doc
+make_offsets(NamesSizes) ->
+ {Names, Sizes} = lists:unzip(NamesSizes),
+ Offsets = make_offsets_from_sizes(Sizes, 1, []),
+ lists:zip(Names, Offsets).
+
+make_offsets_from_sizes([], _, Acc) ->
+ lists:reverse(Acc);
+make_offsets_from_sizes([Size | Sizes], Cur, Acc) ->
+ make_offsets_from_sizes(Sizes, Size+Cur+1, [Cur | Acc]). % For the "."!
+
+%% @doc Little-Endian Base 128 (LEB128) Decoder
+%% This function extracts the <b>first</b> LEB128-encoded integer in a
+%% binary and returns that integer along with the remaining binary. This is
+%% done because a LEB128 number has variable bit-size and that is a way of
+%% extracting only one number in a binary and continuing parsing the binary
+%% for other kind of data (e.g. different encoding).
+%% FIXME: Only decodes unsigned data!
+-spec leb128_decode(binary()) -> {integer(), binary()}.
+leb128_decode(LebNum) ->
+ leb128_decode(LebNum, 0, <<>>).
+
+-spec leb128_decode(binary(), integer(), binary()) -> {integer(), binary()}.
+leb128_decode(LebNum, NoOfBits, Acc) ->
+ <<Sentinel:1/bits, NextBundle:7/bits, MoreLebNums/bits>> = LebNum,
+ case Sentinel of
+ <<1:1>> -> % more bytes to follow
+ leb128_decode(MoreLebNums, NoOfBits+7, <<NextBundle:7/bits, Acc/bits>>);
+ <<0:1>> -> % byte bundle stop
+ Size = NoOfBits+7,
+ <<Num:Size/integer>> = <<NextBundle:7/bits, Acc/bits>>,
+ {Num, MoreLebNums}
+ end.
+
+%% @doc Extract ELF Class from ELF header and export symbol to process
+%% dictionary.
+-spec set_architecture_flag(elf()) -> 'ok'.
+set_architecture_flag(Elf) ->
+ %% Extract information about ELF Class from ELF Header
+ <<16#7f, $E, $L, $F, EI_Class, _MoreHeader/binary>>
+ = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
+ put(elf_class, EI_Class),
+ ok.
+
+%% @doc Read from object file header if the file class is ELF32 or ELF64.
+-spec is64bit() -> boolean().
+is64bit() ->
+ case get(elf_class) of
+ ?ELFCLASS64 -> true;
+ ?ELFCLASS32 -> false
+ end.
diff --git a/lib/hipe/llvm/elf_format.hrl b/lib/hipe/llvm/elf_format.hrl
new file mode 100644
index 0000000000..78592e6e2a
--- /dev/null
+++ b/lib/hipe/llvm/elf_format.hrl
@@ -0,0 +1,488 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%------------------------------------------------------------------------------
+%%
+%% ELF Header File
+%%
+%%------------------------------------------------------------------------------
+
+-ifdef(BIT32).
+-include("elf32_format.hrl"). % ELF32-specific definitions.
+-else.
+-include("elf64_format.hrl"). % ELF64-specific definitions.
+-endif.
+
+%%------------------------------------------------------------------------------
+%% ELF Data Types (in bytes)
+%%------------------------------------------------------------------------------
+%%XXX: Included in either elf32_format or elf64_format.
+
+%%------------------------------------------------------------------------------
+%% ELF File Header
+%%------------------------------------------------------------------------------
+-define(ELF_EHDR_SIZE, (?E_IDENT_SIZE + ?E_TYPE_SIZE + ?E_MACHINE_SIZE
+ +?E_VERSION_SIZE + ?E_ENTRY_SIZE + ?E_PHOFF_SIZE
+ +?E_SHOFF_SIZE + ?E_FLAGS_SIZE + ?E_EHSIZE_SIZE
+ +?E_PHENTSIZE_SIZE + ?E_PHNUM_SIZE + ?E_SHENTSIZE_SIZE
+ +?E_SHNUM_SIZE + ?E_SHSTRNDX_SIZE) ).
+
+-define(E_IDENT_SIZE, (16 * ?ELF_UNSIGNED_CHAR_SIZE) ).
+-define(E_TYPE_SIZE, ?ELF_HALF_SIZE).
+-define(E_MACHINE_SIZE, ?ELF_HALF_SIZE).
+-define(E_VERSION_SIZE, ?ELF_WORD_SIZE).
+-define(E_ENTRY_SIZE, ?ELF_ADDR_SIZE).
+-define(E_PHOFF_SIZE, ?ELF_OFF_SIZE).
+-define(E_SHOFF_SIZE, ?ELF_OFF_SIZE).
+-define(E_FLAGS_SIZE, ?ELF_WORD_SIZE).
+-define(E_EHSIZE_SIZE, ?ELF_HALF_SIZE).
+-define(E_PHENTSIZE_SIZE, ?ELF_HALF_SIZE).
+-define(E_PHNUM_SIZE, ?ELF_HALF_SIZE).
+-define(E_SHENTSIZE_SIZE, ?ELF_HALF_SIZE).
+-define(E_SHNUM_SIZE, ?ELF_HALF_SIZE).
+-define(E_SHSTRNDX_SIZE, ?ELF_HALF_SIZE).
+
+%% Useful arithmetics for computing byte offsets for various File Header
+%% entries from a File Header (erlang) binary
+-define(E_IDENT_OFFSET, 0).
+-define(E_TYPE_OFFSET, (?E_IDENT_OFFSET + ?E_IDENT_SIZE) ).
+-define(E_MACHINE_OFFSET, (?E_TYPE_OFFSET + ?E_TYPE_SIZE) ).
+-define(E_VERSION_OFFSET, (?E_MACHINE_OFFSET + ?E_MACHINE_SIZE) ).
+-define(E_ENTRY_OFFSET, (?E_VERSION_OFFSET + ?E_VERSION_SIZE) ).
+-define(E_PHOFF_OFFSET, (?E_ENTRY_OFFSET + ?E_ENTRY_SIZE) ).
+-define(E_SHOFF_OFFSET, (?E_PHOFF_OFFSET + ?E_PHOFF_SIZE) ).
+-define(E_FLAGS_OFFSET, (?E_SHOFF_OFFSET + ?E_SHOFF_SIZE) ).
+-define(E_EHSIZE_OFFSET, (?E_FLAGS_OFFSET + ?E_FLAGS_SIZE) ).
+-define(E_PHENTSIZE_OFFSET, (?E_EHSIZE_OFFSET + ?E_EHSIZE_SIZE) ).
+-define(E_PHNUM_OFFSET, (?E_PHENTSIZE_OFFSET + ?E_PHENTSIZE_SIZE) ).
+-define(E_SHENTSIZE_OFFSET, (?E_PHNUM_OFFSET + ?E_PHNUM_SIZE) ).
+-define(E_SHNUM_OFFSET, (?E_SHENTSIZE_OFFSET + ?E_SHENTSIZE_SIZE) ).
+-define(E_SHSTRNDX_OFFSET, (?E_SHNUM_OFFSET + ?E_SHNUM_SIZE) ).
+
+%% Name aliases of File Header fields information used in get_header_field
+%% function of elf64_format module.
+-define(E_IDENT, {?E_IDENT_OFFSET, ?E_IDENT_SIZE}).
+-define(E_TYPE, {?E_TYPE_OFFSET, ?E_TYPE_SIZE}).
+-define(E_MACHINE, {?E_MACHINE_OFFSET, ?E_MACHINE_SIZE}).
+-define(E_VERSION, {?E_VERSION_OFFSET, ?E_VERSION_SIZE})
+-define(E_ENTRY, {?E_ENTRY_OFFSET, ?E_ENTRY_SIZE}).
+-define(E_PHOFF, {?E_PHOFF_OFFSET, ?E_PHOFF_SIZE}).
+-define(E_SHOFF, {?E_SHOFF_OFFSET, ?E_SHOFF_SIZE}).
+-define(E_FLAGS, {?E_FLAGS_OFFSET, ?E_FLAGS_SIZE}).
+-define(E_EHSIZE, {?E_EHSIZE_OFFSET, ?E_EHSIZE_SIZE}).
+-define(E_PHENTSIZE, {?E_PHENTSIZE_OFFSET, ?E_PHENTSIZE_SIZE}).
+-define(E_PHNUM, {?E_PHNUM_OFFSET, ?E_PHNUM_SIZE}).
+-define(E_SHENTSIZE, {?E_SHENTSIZE_OFFSET, ?E_SHENTSIZE_SIZE}).
+-define(E_SHNUM, {?E_SHNUM_OFFSET, ?E_SHNUM_SIZE}).
+-define(E_SHSTRNDX, {?E_SHSTRNDX_OFFSET, ?E_SHSTRNDX_SIZE}).
+
+%% ELF Identification (e_ident)
+-define(EI_MAG0, 0).
+-define(EI_MAG1, 1).
+-define(EI_MAG2, 2).
+-define(EI_MAG3, 3).
+-define(EI_CLASS, 4).
+-define(EI_DATA, 5).
+-define(EI_VERSION, 6).
+-define(EI_OSABI, 7).
+-define(EI_ABIVERSION, 8).
+-define(EI_PAD, 9).
+-define(EI_NIDENT, 16).
+
+%% Object File Classes (e_ident[EI_CLASS])
+-define(ELFCLASSNONE, 0).
+-define(ELFCLASS32, 1).
+-define(ELFCLASS64, 2).
+
+%% Data Encodings (e_ident[EI_DATA])
+-define(ELFDATA2LSB, 1).
+-define(ELFDATA2MSB, 2).
+
+%% Operating System and ABI Identifiers (e_ident[EI_OSABI])
+-define(ELFOSABI_SYSV, 0).
+-define(ELFOSABI_HPUX, 1).
+-define(ELFOSABI_STANDALONE, 255).
+
+%% Object File Types (e_type)
+-define(ET_NONE, 0).
+-define(ET_REL, 1).
+-define(ET_EXEC, 2).
+-define(ET_DYN, 3).
+-define(ET_CORE, 4).
+-define(ET_LOOS, 16#FE00).
+-define(ET_HIOS, 16#FEFF).
+-define(ET_LOPROC, 16#FF00).
+-define(ET_HIPROC, 16#FFFF).
+
+%%------------------------------------------------------------------------------
+%% ELF Section Header
+%%------------------------------------------------------------------------------
+-define(ELF_SHDRENTRY_SIZE, (?SH_NAME_SIZE + ?SH_TYPE_SIZE + ?SH_FLAGS_SIZE
+ +?SH_ADDR_SIZE + ?SH_OFFSET_SIZE + ?SH_SIZE_SIZE
+ +?SH_LINK_SIZE + ?SH_INFO_SIZE
+ +?SH_ADDRALIGN_SIZE + ?SH_ENTSIZE_SIZE) ).
+
+-define(SH_NAME_SIZE, ?ELF_WORD_SIZE).
+-define(SH_TYPE_SIZE, ?ELF_WORD_SIZE).
+-define(SH_FLAGS_SIZE, ?ELF_XWORD_SIZE).
+-define(SH_ADDR_SIZE, ?ELF_ADDR_SIZE).
+-define(SH_OFFSET_SIZE, ?ELF_OFF_SIZE).
+-define(SH_SIZE_SIZE, ?ELF_XWORD_SIZE).
+-define(SH_LINK_SIZE, ?ELF_WORD_SIZE).
+-define(SH_INFO_SIZE, ?ELF_WORD_SIZE).
+-define(SH_ADDRALIGN_SIZE, ?ELF_XWORD_SIZE).
+-define(SH_ENTSIZE_SIZE, ?ELF_XWORD_SIZE).
+
+%% Useful arithmetics for computing byte offsets for various fields from a
+%% Section Header Entry (erlang) binary
+-define(SH_NAME_OFFSET, 0).
+-define(SH_TYPE_OFFSET, (?SH_NAME_OFFSET + ?SH_NAME_SIZE) ).
+-define(SH_FLAGS_OFFSET, (?SH_TYPE_OFFSET + ?SH_TYPE_SIZE) ).
+-define(SH_ADDR_OFFSET, (?SH_FLAGS_OFFSET + ?SH_FLAGS_SIZE) ).
+-define(SH_OFFSET_OFFSET, (?SH_ADDR_OFFSET + ?SH_ADDR_SIZE) ).
+-define(SH_SIZE_OFFSET, (?SH_OFFSET_OFFSET + ?SH_OFFSET_SIZE) ).
+-define(SH_LINK_OFFSET, (?SH_SIZE_OFFSET + ?SH_SIZE_SIZE) ).
+-define(SH_INFO_OFFSET, (?SH_LINK_OFFSET + ?SH_LINK_SIZE) ).
+-define(SH_ADDRALIGN_OFFSET, (?SH_INFO_OFFSET + ?SH_INFO_SIZE) ).
+-define(SH_ENTSIZE_OFFSET, (?SH_ADDRALIGN_OFFSET + ?SH_ADDRALIGN_SIZE) ).
+
+%% Name aliases of Section Header Table entry information used in
+%% get_shdrtab_entry function of elf64_format module.
+-define(SH_NAME, {?SH_NAME_OFFSET, ?SH_NAME_SIZE}).
+-define(SH_TYPE, {?SH_TYPE_OFFSET, ?SH_TYPE_SIZE}).
+-define(SH_FLAGS, {?SH_FLAGS_OFFSET, ?SH_FLAGS_SIZE}).
+-define(SH_ADDR, {?SH_ADDR_OFFSET, ?SH_ADDR_SIZE}).
+-define(SH_OFFSET, {?SH_OFFSET_OFFSET, ?SH_OFFSET_SIZE}).
+-define(SH_SIZE, {?SH_SIZE_OFFSET, ?SH_SIZE_SIZE}).
+-define(SH_LINK, {?SH_LINK_OFFSET, ?SH_LINK_SIZE}).
+-define(SH_INFO, {?SH_INFO_OFFSET, ?SH_INFO_SIZE}).
+-define(SH_ADDRALIGN, {?SH_ADDRALIGN_OFFSET, ?SH_ADDRALIGN_SIZE}).
+-define(SH_ENTSIZE, {?SH_ENTSIZE_OFFSET, ?SH_ENTSIZE_SIZE}).
+
+%% Section Indices
+-define(SHN_UNDEF, 0).
+-define(SHN_LOPROC, 16#FF00).
+-define(SHN_HIPROC, 16#FF1F).
+-define(SHN_LOOS, 16#FF20).
+-define(SHN_HIOS, 16#FF3F).
+-define(SHN_ABS, 16#FFF1).
+-define(SHN_COMMON, 16#FFF2).
+
+%% Section Types (sh_type)
+-define(SHT_NULL, 0).
+-define(SHT_PROGBITS, 1).
+-define(SHT_SYMTAB, 2).
+-define(SHT_STRTAB, 3).
+-define(SHT_RELA, 4).
+-define(SHT_HASH, 5).
+-define(SHT_DYNAMIC, 6).
+-define(SHT_NOTE, 7).
+-define(SHT_NOBITS, 8).
+-define(SHT_REL, 9).
+-define(SHT_SHLIB, 10).
+-define(SHT_DYNSYM, 11).
+-define(SHT_LOOS, 16#60000000).
+-define(SHT_HIOS, 16#6FFFFFFF).
+-define(SHT_LOPROC, 16#70000000).
+-define(SHT_HIPROC, 16#7FFFFFFF).
+
+%% Section Attributes (sh_flags)
+-define(SHF_WRITE, 16#1).
+-define(SHF_ALLOC, 16#2).
+-define(SHF_EXECINSTR, 16#4).
+-define(SHF_MASKOS, 16#0F000000).
+-define(SHF_MASKPROC, 16#F0000000).
+
+%%
+%% Standard Section names for Code and Data
+%%
+-define(BSS, ".bss").
+-define(DATA, ".data").
+-define(INTERP, ".interp").
+-define(RODATA, ".rodata").
+-define(TEXT, ".text").
+%% Other Standard Section names
+-define(COMMENT, ".comment").
+-define(DYNAMIC, ".dynamic").
+-define(DYNSTR, ".dynstr").
+-define(GOT, ".got").
+-define(HASH, ".hash").
+-define(NOTE(Name), (".note" ++ Name)).
+-define(PLT, ".plt").
+-define(REL(Name), (".rel" ++ Name) ).
+-define(RELA(Name), (".rela" ++ Name) ).
+-define(SHSTRTAB, ".shstrtab").
+-define(STRTAB, ".strtab").
+-define(SYMTAB, ".symtab").
+-define(GCC_EXN_TAB, ".gcc_except_table").
+
+%%------------------------------------------------------------------------------
+%% ELF Symbol Table Entries
+%%------------------------------------------------------------------------------
+-define(ELF_SYM_SIZE, (?ST_NAME_SIZE + ?ST_INFO_SIZE + ?ST_OTHER_SIZE
+ +?ST_SHNDX_SIZE + ?ST_VALUE_SIZE + ?ST_SIZE_SIZE) ).
+
+-define(ST_NAME_SIZE, ?ELF_WORD_SIZE).
+-define(ST_INFO_SIZE, ?ELF_UNSIGNED_CHAR_SIZE).
+-define(ST_OTHER_SIZE, ?ELF_UNSIGNED_CHAR_SIZE).
+-define(ST_SHNDX_SIZE, ?ELF_HALF_SIZE).
+-define(ST_VALUE_SIZE, ?ELF_ADDR_SIZE).
+-define(ST_SIZE_SIZE, ?ELF_XWORD_SIZE).
+
+%% Precomputed offset for Symbol Table entries in SymTab binary
+%%XXX: Included in either elf32_format or elf64_format.
+
+%% Name aliases for Symbol Table entry information
+-define(ST_NAME, {?ST_NAME_OFFSET, ?ST_NAME_SIZE}).
+-define(ST_INFO, {?ST_INFO_OFFSET, ?ST_INFO_SIZE}).
+-define(ST_OTHER, {?ST_OTHER_OFFSET, ?ST_OTHER_SIZE}).
+-define(ST_SHNDX, {?ST_SHNDX_OFFSET, ?ST_SHNDX_SIZE}).
+-define(ST_VALUE, {?ST_VALUE_OFFSET, ?ST_VALUE_SIZE}).
+-define(ST_SIZE, {?ST_SIZE_OFFSET, ?ST_SIZE_SIZE}).
+
+%% Macros to extract information from st_type
+-define(ELF_ST_BIND(I), (I bsr 4) ).
+-define(ELF_ST_TYPE(I), (I band 16#f) ).
+-define(ELF_ST_INFO(B,T), (B bsl 4 + T band 16#f) ).
+
+%% Symbol Bindings
+-define(STB_LOCAL, 0).
+-define(STB_GLOBAL, 1).
+-define(STB_WEAK, 2).
+-define(STB_LOOS, 10).
+-define(STB_HIOS, 12).
+-define(STB_LOPROC, 13).
+-define(STB_HIPROC, 15).
+
+%% Symbol Types
+-define(STT_NOTYPE, 0).
+-define(STT_OBJECT, 1).
+-define(STT_FUNC, 2).
+-define(STT_SECTION, 3).
+-define(STT_FILE, 4).
+-define(STT_LOOS, 10).
+-define(STT_HIOS, 12).
+-define(STT_LOPROC, 13).
+-define(STT_HIPROC, 15).
+
+%%------------------------------------------------------------------------------
+%% ELF Relocation Entries
+%%------------------------------------------------------------------------------
+-define(ELF_REL_SIZE, (?R_OFFSET_SIZE + ?R_INFO_SIZE) ).
+-define(ELF_RELA_SIZE, (?R_OFFSET_SIZE + ?R_INFO_SIZE + ?R_ADDEND_SIZE) ).
+
+-define(R_OFFSET_SIZE, ?ELF_ADDR_SIZE).
+-define(R_INFO_SIZE, ?ELF_XWORD_SIZE).
+-define(R_ADDEND_SIZE, ?ELF_SXWORD_SIZE).
+
+%% Arithmetics for computing byte offsets in a Relocation entry binary
+-define(R_OFFSET_OFFSET, 0).
+-define(R_INFO_OFFSET, (?R_OFFSET_OFFSET + ?R_OFFSET_SIZE) ).
+-define(R_ADDEND_OFFSET, (?R_INFO_OFFSET + ?R_INFO_SIZE) ).
+
+%% Name aliases for Relocation field information
+-define(R_OFFSET, {?R_OFFSET_OFFSET, ?R_OFFSET_SIZE}).
+-define(R_INFO, {?R_INFO_OFFSET, ?R_INFO_SIZE}).
+-define(R_ADDEND, {?R_ADDEND_OFFSET, ?R_ADDEND_SIZE}).
+
+%% Useful macros to extract information from r_info field
+%%XXX: Included in either elf32_format or elf64_format.
+
+%%------------------------------------------------------------------------------
+%% ELF Program Header Table
+%%------------------------------------------------------------------------------
+-define(ELF_PHDR_SIZE, (?P_TYPE_SIZE + ?P_FLAGS_SIZE + ?P_OFFSET_SIZE
+ +?P_VADDR_SIZE + ?P_PADDR_SIZE + ?P_FILESZ_SIZE
+ +?P_MEMSZ_SIZE + ?P_ALIGN_SIZE) ).
+
+-define(P_TYPE_SIZE, ?ELF_WORD_SIZE).
+-define(P_FLAGS_SIZE, ?ELF_WORD_SIZE).
+-define(P_OFFSET_SIZE, ?ELF_OFF_SIZE).
+-define(P_VADDR_SIZE, ?ELF_ADDR_SIZE).
+-define(P_PADDR_SIZE, ?ELF_ADDR_SIZE).
+-define(P_FILESZ_SIZE, ?ELF_XWORD_SIZE).
+-define(P_MEMSZ_SIZE, ?ELF_XWORD_SIZE).
+-define(P_ALIGN_SIZE, ?ELF_XWORD_SIZE).
+
+%% Offsets of various fields in a Program Header Table entry binary.
+%%XXX: Included in either elf32_format or elf64_format.
+
+%% Name aliases for each Program Header Table entry field information.
+-define(P_TYPE, {?P_TYPE_OFFSET, ?P_TYPE_SIZE} ).
+-define(P_FLAGS, {?P_FLAGS_OFFSET, ?P_FLAGS_SIZE} ).
+-define(P_OFFSET, {?P_OFFSET_OFFSET, ?P_OFFSET_SIZE} ).
+-define(P_VADDR, {?P_VADDR_OFFSET, ?P_VADDR_SIZE} ).
+-define(P_PADDR, {?P_PADDR_OFFSET, ?P_PADDR_SIZE} ).
+-define(P_FILESZ, {?P_FILESZ_OFFSET, ?P_FILESZ_SIZE} ).
+-define(P_MEMSZ, {?P_MEMSZ_OFFSET, ?P_MEMSZ_SIZE} ).
+-define(P_ALIGN, {?P_ALIGN_OFFSET, ?P_ALIGN_SIZE} ).
+
+%% Segment Types (p_type)
+-define(PT_NULL, 0).
+-define(PT_LOAD, 1).
+-define(PT_DYNAMIC, 2).
+-define(PT_INTERP, 3).
+-define(PT_NOTE, 4).
+-define(PT_SHLIB, 5).
+-define(PT_PHDR, 6).
+-define(PT_LOOS, 16#60000000).
+-define(PT_HIOS, 16#6FFFFFFF).
+-define(PT_LOPROC, 16#70000000).
+-define(PT_HIPROC, 16#7FFFFFFF).
+
+%% Segment Attributes (p_flags)
+-define(PF_X, 16#1).
+-define(PF_W, 16#2).
+-define(PF_R, 16#4).
+-define(PF_MASKOS, 16#00FF0000).
+-define(PF_MASKPROC, 16#FF000000).
+
+%%------------------------------------------------------------------------------
+%% ELF Dynamic Table
+%%------------------------------------------------------------------------------
+-define(ELF_DYN_SIZE, (?D_TAG_SIZE + ?D_VAL_PTR_SIZE) ).
+
+-define(D_TAG_SIZE, ?ELF_SXWORD_SIZE).
+-define(D_VAL_PTR_SIZE, ?ELF_ADDR_SIZE).
+
+%% Offsets of each field in Dynamic Table entry in binary
+-define(D_TAG_OFFSET, 0).
+-define(D_VAL_PTR_OFFSET, (?D_TAG_OFFSET + ?D_TAG_SIZE)).
+
+%% Name aliases for each field of a Dynamic Table entry information
+-define(D_TAG, {?D_TAG_OFFSET, ?D_TAG_SIZE} ).
+-define(D_VAL_PTR, {?D_VAL_PTR_OFFSET, ?D_VAL_PTR_SIZE} ).
+
+%% Dynamic Table Entries
+-define(DT_NULL, 0).
+-define(DT_NEEDED, 1).
+-define(DT_PLTRELSZ, 2).
+-define(DT_PLTGOT, 3).
+-define(DT_HASH, 4).
+-define(DT_STRTAB, 5).
+-define(DT_SYMTAB, 6).
+-define(DT_RELA, 7).
+-define(DT_RELASZ, 8).
+-define(DT_RELAENT, 9).
+-define(DT_STRSZ, 10).
+-define(DT_SYMENT, 11).
+-define(DT_INIT, 12).
+-define(DT_FINI, 13).
+-define(DT_SONAME, 14).
+-define(DT_RPATH, 15).
+-define(DT_SYMBOLIC, 16).
+-define(DT_REL, 17).
+-define(DT_RELSZ, 18).
+-define(DT_RELENT, 19).
+-define(DT_PLTREL, 20).
+-define(DT_DEBUG, 21).
+-define(DT_TEXTREL, 22).
+-define(DT_JMPREL, 23).
+-define(DT_BIND_NOW, 24).
+-define(DT_INIT_ARRAY, 25).
+-define(DT_FINI_ARRAY, 26).
+-define(DT_INIT_ARRAYSZ, 27).
+-define(DT_FINI_ARRAYSZ, 28).
+-define(DT_LOOS, 16#60000000).
+-define(DT_HIOS, 16#6FFFFFFF).
+-define(DT_LOPROC, 16#700000000).
+-define(DT_HIPROC, 16#7FFFFFFFF).
+
+%%------------------------------------------------------------------------------
+%% ELF GCC Exception Table
+%%------------------------------------------------------------------------------
+
+%% The DWARF Exception Header Encoding is used to describe the type of data used
+%% in the .eh_frame_hdr (and .gcc_except_table) section. The upper 4 bits
+%% indicate how the value is to be applied. The lower 4 bits indicate the format
+%% of the data.
+
+%% DWARF Exception Header value format
+-define(DW_EH_PE_omit, 16#ff). % No value is present.
+-define(DW_EH_PE_uleb128, 16#01). % Unsigned value encoded using LEB128.
+-define(DW_EH_PE_udata2, 16#02). % A 2 bytes unsigned value.
+-define(DW_EH_PE_udata4, 16#03). % A 4 bytes unsigned value.
+-define(DW_EH_PE_udata8, 16#04). % An 8 bytes unsigned value.
+-define(DW_EH_PE_sleb128, 16#09). % Signed value encoded using LEB128.
+-define(DW_EH_PE_sdata2, 16#0a). % A 2 bytes signed value.
+-define(DW_EH_PE_sdata4, 16#0b). % A 4 bytes signed value.
+-define(DW_EH_PE_sdata8, 16#0c). % An 8 bytes signed value.
+
+%% DWARF Exception Header application
+-define(DW_EH_PE_absptr, 16#00). % Value is used with no modification.
+-define(DW_EH_PE_pcrel, 16#10). % Value is relative to the current PC.
+-define(DW_EH_PE_datarel, 16#30). % Value is relative to the beginning of the
+ % section.
+
+%%------------------------------------------------------------------------------
+%% ELF Read-only data (constants, literlas etc.)
+%%------------------------------------------------------------------------------
+-define(RO_ENTRY_SIZE, 8).
+
+%%------------------------------------------------------------------------------
+%% Custom Note section: ".note.gc" for Erlang GC
+%%------------------------------------------------------------------------------
+
+%% The structure of this section is the following:
+%%
+%% .short <n> # number of safe points in code
+%%
+%% .long .L<label1> # safe point address |
+%% .long .L<label2> # safe point address |-> safe point addrs
+%% ..... |
+%% .long .L<label3> # safe point address |
+%%
+%% .short <n> # stack frame size (in words) |-> fixed-size part
+%% .short <n> # stack arity |
+%% .short <n> # number of live roots that follow |
+%%
+%% .short <n> # live root's stack index |
+%% ..... |-> live root indices
+%% .short <n> # >> |
+
+%% The name of the custom Note Section
+-define(NOTE_ERLGC_NAME, ".gc").
+
+%% The first word of a Note Section for Erlang GC (".note.gc") is always the
+%% number of safepoints in code.
+-define(SP_COUNT, {?SP_COUNT_OFFSET, ?SP_COUNT_SIZE}).
+-define(SP_COUNT_SIZE, ?ELF_HALF_SIZE).
+-define(SP_COUNT_OFFSET, 0). %(always the first entry in sdesc)
+
+%% The fixed-size part of a safe point (SP) entry consists of 4 words: the SP
+%% address (offset in code), the stack frame size of the function (where the SP
+%% is located), the stack arity of the function (the registered values are *not*
+%% counted), the number of live roots in the specific SP.
+-define(SP_FIXED, {?SP_FIXED_OFF, ?SP_FIXED_SIZE}).
+-define(SP_FIXED_OFF, 0).
+%%XXX: Exclude SP_ADDR_SIZE from SP_FIXED_SIZE in lew of new GC layout
+-define(SP_FIXED_SIZE, (?SP_STKFRAME_SIZE + ?SP_STKARITY_SIZE
+ + ?SP_LIVEROOTCNT_SIZE)).
+
+-define(SP_ADDR_SIZE, ?ELF_WORD_SIZE).
+-define(SP_STKFRAME_SIZE, ?ELF_HALF_SIZE).
+-define(SP_STKARITY_SIZE, ?ELF_HALF_SIZE).
+-define(SP_LIVEROOTCNT_SIZE, ?ELF_HALF_SIZE).
+
+%%XXX: SP_STKFRAME is the first piece of information in the new GC layout
+-define(SP_STKFRAME_OFFSET, 0).
+-define(SP_STKARITY_OFFSET, (?SP_STKFRAME_OFFSET + ?SP_STKFRAME_SIZE) ).
+-define(SP_LIVEROOTCNT_OFFSET, (?SP_STKARITY_OFFSET + ?SP_STKARITY_SIZE) ).
+
+%% Name aliases for safepoint fields.
+-define(SP_STKFRAME, {?SP_STKFRAME_OFFSET, ?SP_STKFRAME_SIZE}).
+-define(SP_STKARITY, {?SP_STKARITY_OFFSET, ?SP_STKARITY_SIZE}).
+-define(SP_LIVEROOTCNT, {?SP_LIVEROOTCNT_OFFSET, ?SP_LIVEROOTCNT_SIZE}).
+
+%% After the fixed-size part a variable-size part exists. This part holds the
+%% stack frame index of every live root in the specific SP.
+-define(LR_STKINDEX_SIZE, ?ELF_HALF_SIZE).
+
+%%------------------------------------------------------------------------------
+%% Misc.
+%%------------------------------------------------------------------------------
+-define(bits(Bytes), ((Bytes) bsl 3)).
diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl
new file mode 100644
index 0000000000..5e33731a2b
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm.erl
@@ -0,0 +1,1131 @@
+%% -*- erlang-indent-level: 2 -*-
+
+-module(hipe_llvm).
+
+-export([
+ mk_ret/1,
+ ret_ret_list/1,
+
+ mk_br/1,
+ br_dst/1,
+
+ mk_br_cond/3,
+ mk_br_cond/4,
+ br_cond_cond/1,
+ br_cond_true_label/1,
+ br_cond_false_label/1,
+ br_cond_meta/1,
+
+ mk_indirectbr/3,
+ indirectbr_type/1,
+ indirectbr_address/1,
+ indirectbr_label_list/1,
+
+ mk_switch/4,
+ switch_type/1,
+ switch_value/1,
+ switch_default_label/1,
+ switch_value_label_list/1,
+
+ mk_invoke/9,
+ invoke_dst/1,
+ invoke_cconv/1,
+ invoke_ret_attrs/1,
+ invoke_type/1,
+ invoke_fnptrval/1,
+ invoke_arglist/1,
+ invoke_fn_attrs/1,
+ invoke_to_label/1,
+ invoke_unwind_label/1,
+
+ mk_operation/6,
+ operation_dst/1,
+ operation_op/1,
+ operation_type/1,
+ operation_src1/1,
+ operation_src2/1,
+ operation_options/1,
+
+ mk_extractvalue/5,
+ extractvalue_dst/1,
+ extractvalue_type/1,
+ extractvalue_val/1,
+ extractvalue_idx/1,
+ extractvalue_idxs/1,
+
+ mk_insertvalue/7,
+ insertvalue_dst/1,
+ insertvalue_val_type/1,
+ insertvalue_val/1,
+ insertvalue_elem_type/1,
+ insertvalue_elem/1,
+ insertvalue_idx/1,
+ insertvalue_idxs/1,
+
+ mk_alloca/4,
+ alloca_dst/1,
+ alloca_type/1,
+ alloca_num/1,
+ alloca_align/1,
+
+ mk_load/6,
+ load_dst/1,
+ load_p_type/1,
+ load_pointer/1,
+ load_alignment/1,
+ load_nontemporal/1,
+ load_volatile/1,
+
+ mk_store/7,
+ store_type/1,
+ store_value/1,
+ store_p_type/1,
+ store_pointer/1,
+ store_alignment/1,
+ store_nontemporal/1,
+ store_volatile/1,
+
+ mk_getelementptr/5,
+ getelementptr_dst/1,
+ getelementptr_p_type/1,
+ getelementptr_value/1,
+ getelementptr_typed_idxs/1,
+ getelementptr_inbounds/1,
+
+ mk_conversion/5,
+ conversion_dst/1,
+ conversion_op/1,
+ conversion_src_type/1,
+ conversion_src/1,
+ conversion_dst_type/1,
+
+ mk_sitofp/4,
+ sitofp_dst/1,
+ sitofp_src_type/1,
+ sitofp_src/1,
+ sitofp_dst_type/1,
+
+ mk_ptrtoint/4,
+ ptrtoint_dst/1,
+ ptrtoint_src_type/1,
+ ptrtoint_src/1,
+ ptrtoint_dst_type/1,
+
+ mk_inttoptr/4,
+ inttoptr_dst/1,
+ inttoptr_src_type/1,
+ inttoptr_src/1,
+ inttoptr_dst_type/1,
+
+ mk_icmp/5,
+ icmp_dst/1,
+ icmp_cond/1,
+ icmp_type/1,
+ icmp_src1/1,
+ icmp_src2/1,
+
+ mk_fcmp/5,
+ fcmp_dst/1,
+ fcmp_cond/1,
+ fcmp_type/1,
+ fcmp_src1/1,
+ fcmp_src2/1,
+
+ mk_phi/3,
+ phi_dst/1,
+ phi_type/1,
+ phi_value_label_list/1,
+
+ mk_select/6,
+ select_dst/1,
+ select_cond/1,
+ select_typ1/1,
+ select_val1/1,
+ select_typ2/1,
+ select_val2/1,
+
+ mk_call/8,
+ call_dst/1,
+ call_is_tail/1,
+ call_cconv/1,
+ call_ret_attrs/1,
+ call_type/1,
+ call_fnptrval/1,
+ call_arglist/1,
+ call_fn_attrs/1,
+
+ mk_fun_def/10,
+ fun_def_linkage/1,
+ fun_def_visibility/1,
+ fun_def_cconv/1,
+ fun_def_ret_attrs/1,
+ fun_def_type/1,
+ fun_def_name/1,
+ fun_def_arglist/1,
+ fun_def_fn_attrs/1,
+ fun_def_align/1,
+ fun_def_body/1,
+
+ mk_fun_decl/8,
+ fun_decl_linkage/1,
+ fun_decl_visibility/1,
+ fun_decl_cconv/1,
+ fun_decl_ret_attrs/1,
+ fun_decl_type/1,
+ fun_decl_name/1,
+ fun_decl_arglist/1,
+ fun_decl_align/1,
+
+ mk_landingpad/0,
+
+ mk_comment/1,
+ comment_text/1,
+
+ mk_label/1,
+ label_label/1,
+ is_label/1,
+
+ mk_const_decl/4,
+ const_decl_dst/1,
+ const_decl_decl_type/1,
+ const_decl_type/1,
+ const_decl_value/1,
+
+ mk_asm/1,
+ asm_instruction/1,
+
+ mk_adj_stack/3,
+ adj_stack_offset/1,
+ adj_stack_register/1,
+ adj_stack_type/1,
+
+ mk_branch_meta/3,
+ branch_meta_id/1,
+ branch_meta_true_weight/1,
+ branch_meta_false_weight/1
+ ]).
+
+-export([
+ mk_void/0,
+
+ mk_label_type/0,
+
+ mk_int/1,
+ int_width/1,
+
+ mk_double/0,
+
+ mk_pointer/1,
+ pointer_type/1,
+
+ mk_array/2,
+ array_size/1,
+ array_type/1,
+
+ mk_vector/2,
+ vector_size/1,
+ vector_type/1,
+
+ mk_struct/1,
+ struct_type_list/1,
+
+ mk_fun/2,
+ function_ret_type/1,
+ function_arg_type_list/1
+ ]).
+
+-export([pp_ins_list/2, pp_ins/2]).
+
+
+%%-----------------------------------------------------------------------------
+%% Abstract Data Types for LLVM Assembly.
+%%-----------------------------------------------------------------------------
+
+%% Terminator Instructions
+-record(llvm_ret, {ret_list=[]}).
+-type llvm_ret() :: #llvm_ret{}.
+
+-record(llvm_br, {dst}).
+-type llvm_br() :: #llvm_br{}.
+
+-record(llvm_br_cond, {'cond', true_label, false_label, meta=[]}).
+-type llvm_br_cond() :: #llvm_br_cond{}.
+
+-record(llvm_indirectbr, {type, address, label_list}).
+-type llvm_indirectbr() :: #llvm_indirectbr{}.
+
+-record(llvm_switch, {type, value, default_label, value_label_list=[]}).
+-type llvm_switch() :: #llvm_switch{}.
+
+-record(llvm_invoke, {dst, cconv=[], ret_attrs=[], type, fnptrval, arglist=[],
+ fn_attrs=[], to_label, unwind_label}).
+-type llvm_invoke() :: #llvm_invoke{}.
+
+%% Binary Operations
+-record(llvm_operation, {dst, op, type, src1, src2, options=[]}).
+-type llvm_operation() :: #llvm_operation{}.
+
+%% Aggregate Operations
+-record(llvm_extractvalue, {dst, type, val, idx, idxs=[]}).
+-type llvm_extractvalue() :: #llvm_extractvalue{}.
+
+-record(llvm_insertvalue, {dst, val_type, val, elem_type, elem, idx, idxs=[]}).
+-type llvm_insertvalue() :: #llvm_insertvalue{}.
+
+%% Memory Access and Addressing Operations
+-record(llvm_alloca, {dst, type, num=[], align=[]}).
+-type llvm_alloca() :: #llvm_alloca{}.
+
+-record(llvm_load, {dst, p_type, pointer, alignment=[], nontemporal=[],
+ volatile=false}).
+-type llvm_load() :: #llvm_load{}.
+
+-record(llvm_store, {type, value, p_type, pointer, alignment=[],
+ nontemporal=[], volatile=false}).
+-type llvm_store() :: #llvm_store{}.
+
+-record(llvm_getelementptr, {dst, p_type, value, typed_idxs, inbounds}).
+-type llvm_getelementptr() :: #llvm_getelementptr{}.
+
+%% Conversion Operations
+-record(llvm_conversion, {dst, op, src_type, src, dst_type}).
+-type llvm_conversion() :: #llvm_conversion{}.
+
+-record(llvm_sitofp, {dst, src_type, src, dst_type}).
+-type llvm_sitofp() :: #llvm_sitofp{}.
+
+-record(llvm_ptrtoint, {dst, src_type, src, dst_type}).
+-type llvm_ptrtoint() :: #llvm_ptrtoint{}.
+
+-record(llvm_inttoptr, {dst, src_type, src, dst_type}).
+-type llvm_inttoptr() :: #llvm_inttoptr{}.
+
+%% Other Operations
+-record(llvm_icmp, {dst, 'cond', type, src1, src2}).
+-type llvm_icmp() :: #llvm_icmp{}.
+
+-record(llvm_fcmp, {dst, 'cond', type, src1, src2}).
+-type llvm_fcmp() :: #llvm_fcmp{}.
+
+-record(llvm_phi, {dst, type, value_label_list}).
+-type llvm_phi() :: #llvm_phi{}.
+
+-record(llvm_select, {dst, 'cond', typ1, val1, typ2, val2}).
+-type llvm_select() :: #llvm_select{}.
+
+-record(llvm_call, {dst=[], is_tail = false, cconv = [], ret_attrs = [], type,
+ fnptrval, arglist = [], fn_attrs = []}).
+-type llvm_call() :: #llvm_call{}.
+
+-record(llvm_fun_def, {linkage=[], visibility=[], cconv=[], ret_attrs=[],
+ type, 'name', arglist=[], fn_attrs=[], align=[], body=[]}).
+-type llvm_fun_def() :: #llvm_fun_def{}.
+
+-record(llvm_fun_decl, {linkage=[], visibility=[], cconv=[], ret_attrs=[],
+ type, 'name', arglist=[], align=[]}).
+-type llvm_fun_decl() :: #llvm_fun_decl{}.
+
+-record(llvm_landingpad, {}).
+-type llvm_landingpad() :: #llvm_landingpad{}.
+
+-record(llvm_comment, {text}).
+-type llvm_comment() :: #llvm_comment{}.
+
+-record(llvm_label, {label}).
+-type llvm_label() :: #llvm_label{}.
+
+-record(llvm_const_decl, {dst, decl_type, type, value}).
+-type llvm_const_decl() :: #llvm_const_decl{}.
+
+-record(llvm_asm, {instruction}).
+-type llvm_asm() :: #llvm_asm{}.
+
+-record(llvm_adj_stack, {offset, 'register', type}).
+-type llvm_adj_stack() :: #llvm_adj_stack{}.
+
+-record(llvm_branch_meta, {id, true_weight, false_weight}).
+-type llvm_branch_meta() :: #llvm_branch_meta{}.
+
+%% A type for any LLVM instruction
+-type llvm_instr() :: llvm_ret() | llvm_br() | llvm_br_cond()
+ | llvm_indirectbr() | llvm_switch() | llvm_invoke()
+ | llvm_operation() | llvm_extractvalue()
+ | llvm_insertvalue() | llvm_alloca() | llvm_load()
+ | llvm_store() | llvm_getelementptr() | llvm_conversion()
+ | llvm_sitofp() | llvm_ptrtoint() | llvm_inttoptr()
+ | llvm_icmp() | llvm_fcmp() | llvm_phi() | llvm_select()
+ | llvm_call() | llvm_fun_def() | llvm_fun_decl()
+ | llvm_landingpad() | llvm_comment() | llvm_label()
+ | llvm_const_decl() | llvm_asm() | llvm_adj_stack()
+ | llvm_branch_meta().
+
+%% Types
+-record(llvm_void, {}).
+%-type llvm_void() :: #llvm_void{}.
+
+-record(llvm_label_type, {}).
+%-type llvm_label_type() :: #llvm_label_type{}.
+
+-record(llvm_int, {width}).
+%-type llvm_int() :: #llvm_int{}.
+
+-record(llvm_float, {}).
+%-type llvm_float() :: #llvm_float{}.
+
+-record(llvm_double, {}).
+%-type llvm_double() :: #llvm_double{}.
+
+-record(llvm_fp80, {}).
+%-type llvm_fp80() :: #llvm_fp80{}.
+
+-record(llvm_fp128, {}).
+%-type llvm_fp128() :: #llvm_fp128{}.
+
+-record(llvm_ppc_fp128, {}).
+%-type llvm_ppc_fp128() :: #llvm_ppc_fp128{}.
+
+-record(llvm_pointer, {type}).
+%-type llvm_pointer() :: #llvm_pointer{}.
+
+-record(llvm_vector, {'size', type}).
+%-type llvm_vector() :: #llvm_vector{}.
+
+-record(llvm_struct, {type_list}).
+%-type llvm_struct() :: #llvm_struct{}.
+
+-record(llvm_array, {'size', type}).
+%-type llvm_array() :: #llvm_array{}.
+
+-record(llvm_fun, {ret_type, arg_type_list}).
+%-type llvm_fun() :: #llvm_fun{}.
+
+%%-----------------------------------------------------------------------------
+%% Accessor Functions
+%%-----------------------------------------------------------------------------
+
+%% ret
+mk_ret(Ret_list) -> #llvm_ret{ret_list=Ret_list}.
+ret_ret_list(#llvm_ret{ret_list=Ret_list}) -> Ret_list.
+
+%% br
+mk_br(Dst) -> #llvm_br{dst=Dst}.
+br_dst(#llvm_br{dst=Dst}) -> Dst.
+
+%% br_cond
+mk_br_cond(Cond, True_label, False_label) ->
+ #llvm_br_cond{'cond'=Cond, true_label=True_label, false_label=False_label}.
+mk_br_cond(Cond, True_label, False_label, Metadata) ->
+ #llvm_br_cond{'cond'=Cond, true_label=True_label, false_label=False_label,
+ meta=Metadata}.
+br_cond_cond(#llvm_br_cond{'cond'=Cond}) -> Cond.
+br_cond_true_label(#llvm_br_cond{true_label=True_label}) -> True_label.
+br_cond_false_label(#llvm_br_cond{false_label=False_label}) ->
+ False_label.
+br_cond_meta(#llvm_br_cond{meta=Metadata}) -> Metadata.
+
+%% indirectbr
+mk_indirectbr(Type, Address, Label_list) -> #llvm_indirectbr{type=Type, address=Address, label_list=Label_list}.
+indirectbr_type(#llvm_indirectbr{type=Type}) -> Type.
+indirectbr_address(#llvm_indirectbr{address=Address}) -> Address.
+indirectbr_label_list(#llvm_indirectbr{label_list=Label_list}) -> Label_list.
+
+%% invoke
+mk_invoke(Dst, Cconv, Ret_attrs, Type, Fnptrval, Arglist, Fn_attrs, To_label, Unwind_label) ->
+ #llvm_invoke{dst=Dst, cconv=Cconv, ret_attrs=Ret_attrs, type=Type,
+ fnptrval=Fnptrval, arglist=Arglist, fn_attrs=Fn_attrs, to_label=To_label,
+ unwind_label=Unwind_label}.
+invoke_dst(#llvm_invoke{dst=Dst}) -> Dst.
+invoke_cconv(#llvm_invoke{cconv=Cconv}) -> Cconv.
+invoke_ret_attrs(#llvm_invoke{ret_attrs=Ret_attrs}) -> Ret_attrs.
+invoke_type(#llvm_invoke{type=Type}) -> Type.
+invoke_fnptrval(#llvm_invoke{fnptrval=Fnptrval}) -> Fnptrval.
+invoke_arglist(#llvm_invoke{arglist=Arglist}) -> Arglist.
+invoke_fn_attrs(#llvm_invoke{fn_attrs=Fn_attrs}) -> Fn_attrs.
+invoke_to_label(#llvm_invoke{to_label=To_label}) -> To_label.
+invoke_unwind_label(#llvm_invoke{unwind_label=Unwind_label}) -> Unwind_label.
+
+%% switch
+mk_switch(Type, Value, Default_label, Value_label_list) ->
+ #llvm_switch{type=Type, value=Value, default_label=Default_label,
+ value_label_list=Value_label_list}.
+switch_type(#llvm_switch{type=Type}) -> Type.
+switch_value(#llvm_switch{value=Value}) -> Value.
+switch_default_label(#llvm_switch{default_label=Default_label}) ->
+ Default_label.
+switch_value_label_list(#llvm_switch{value_label_list=Value_label_list}) ->
+ Value_label_list.
+
+%% operation
+mk_operation(Dst, Op, Type, Src1, Src2, Options) ->
+ #llvm_operation{dst=Dst, op=Op, type=Type, src1=Src1, src2=Src2,
+ options=Options}.
+operation_dst(#llvm_operation{dst=Dst}) -> Dst.
+operation_op(#llvm_operation{op=Op}) -> Op.
+operation_type(#llvm_operation{type=Type}) -> Type.
+operation_src1(#llvm_operation{src1=Src1}) -> Src1.
+operation_src2(#llvm_operation{src2=Src2}) -> Src2.
+operation_options(#llvm_operation{options=Options}) -> Options.
+
+%% extractvalue
+mk_extractvalue(Dst, Type, Val, Idx, Idxs) ->
+ #llvm_extractvalue{dst=Dst,type=Type,val=Val,idx=Idx,idxs=Idxs}.
+extractvalue_dst(#llvm_extractvalue{dst=Dst}) -> Dst.
+extractvalue_type(#llvm_extractvalue{type=Type}) -> Type.
+extractvalue_val(#llvm_extractvalue{val=Val}) -> Val.
+extractvalue_idx(#llvm_extractvalue{idx=Idx}) -> Idx.
+extractvalue_idxs(#llvm_extractvalue{idxs=Idxs}) -> Idxs.
+
+%% insertvalue
+mk_insertvalue(Dst, Val_type, Val, Elem_type, Elem, Idx, Idxs) ->
+ #llvm_insertvalue{dst=Dst, val_type=Val_type, val=Val, elem_type=Elem_type,
+ elem=Elem, idx=Idx, idxs=Idxs}.
+insertvalue_dst(#llvm_insertvalue{dst=Dst}) -> Dst.
+insertvalue_val_type(#llvm_insertvalue{val_type=Val_type}) -> Val_type.
+insertvalue_val(#llvm_insertvalue{val=Val}) -> Val.
+insertvalue_elem_type(#llvm_insertvalue{elem_type=Elem_type}) -> Elem_type.
+insertvalue_elem(#llvm_insertvalue{elem=Elem}) -> Elem.
+insertvalue_idx(#llvm_insertvalue{idx=Idx}) -> Idx.
+insertvalue_idxs(#llvm_insertvalue{idxs=Idxs}) -> Idxs.
+
+%% alloca
+mk_alloca(Dst, Type, Num, Align) ->
+ #llvm_alloca{dst=Dst, type=Type, num=Num, align=Align}.
+alloca_dst(#llvm_alloca{dst=Dst}) -> Dst.
+alloca_type(#llvm_alloca{type=Type}) -> Type.
+alloca_num(#llvm_alloca{num=Num}) -> Num.
+alloca_align(#llvm_alloca{align=Align}) -> Align.
+
+%% load
+mk_load(Dst, Type, Pointer, Alignment, Nontemporal, Volatile) ->
+ #llvm_load{dst=Dst, p_type=Type, pointer=Pointer, alignment=Alignment,
+ nontemporal=Nontemporal, volatile=Volatile}.
+load_dst(#llvm_load{dst=Dst}) -> Dst.
+load_p_type(#llvm_load{p_type=Type}) -> Type.
+load_pointer(#llvm_load{pointer=Pointer}) -> Pointer.
+load_alignment(#llvm_load{alignment=Alignment}) -> Alignment.
+load_nontemporal(#llvm_load{nontemporal=Nontemporal}) -> Nontemporal.
+load_volatile(#llvm_load{volatile=Volatile}) -> Volatile.
+
+%% store
+mk_store(Type, Value, P_Type, Pointer, Alignment, Nontemporal, Volatile) ->
+ #llvm_store{type=Type, value=Value, p_type=P_Type, pointer=Pointer, alignment=Alignment,
+ nontemporal=Nontemporal, volatile=Volatile}.
+store_type(#llvm_store{type=Type}) -> Type.
+store_value(#llvm_store{value=Value}) -> Value.
+store_p_type(#llvm_store{p_type=P_Type}) -> P_Type.
+store_pointer(#llvm_store{pointer=Pointer}) -> Pointer.
+store_alignment(#llvm_store{alignment=Alignment}) -> Alignment.
+store_nontemporal(#llvm_store{nontemporal=Nontemporal}) -> Nontemporal.
+store_volatile(#llvm_store{volatile=Volatile}) -> Volatile.
+
+%% getelementptr
+mk_getelementptr(Dst, P_Type, Value, Typed_Idxs, Inbounds) ->
+ #llvm_getelementptr{dst=Dst,p_type=P_Type, value=Value,
+ typed_idxs=Typed_Idxs, inbounds=Inbounds}.
+getelementptr_dst(#llvm_getelementptr{dst=Dst}) -> Dst.
+getelementptr_p_type(#llvm_getelementptr{p_type=P_Type}) -> P_Type.
+getelementptr_value(#llvm_getelementptr{value=Value}) -> Value.
+getelementptr_typed_idxs(#llvm_getelementptr{typed_idxs=Typed_Idxs}) -> Typed_Idxs.
+getelementptr_inbounds(#llvm_getelementptr{inbounds=Inbounds}) -> Inbounds.
+
+%% conversion
+mk_conversion(Dst, Op, Src_type, Src, Dst_type) ->
+ #llvm_conversion{dst=Dst, op=Op, src_type=Src_type, src=Src, dst_type=Dst_type}.
+conversion_dst(#llvm_conversion{dst=Dst}) -> Dst.
+conversion_op(#llvm_conversion{op=Op}) -> Op.
+conversion_src_type(#llvm_conversion{src_type=Src_type}) -> Src_type.
+conversion_src(#llvm_conversion{src=Src}) -> Src.
+conversion_dst_type(#llvm_conversion{dst_type=Dst_type}) -> Dst_type.
+
+%% sitofp
+mk_sitofp(Dst, Src_type, Src, Dst_type) ->
+ #llvm_sitofp{dst=Dst, src_type=Src_type, src=Src, dst_type=Dst_type}.
+sitofp_dst(#llvm_sitofp{dst=Dst}) -> Dst.
+sitofp_src_type(#llvm_sitofp{src_type=Src_type}) -> Src_type.
+sitofp_src(#llvm_sitofp{src=Src}) -> Src.
+sitofp_dst_type(#llvm_sitofp{dst_type=Dst_type}) -> Dst_type.
+
+%% ptrtoint
+mk_ptrtoint(Dst, Src_Type, Src, Dst_Type) ->
+ #llvm_ptrtoint{dst=Dst, src_type=Src_Type, src=Src, dst_type=Dst_Type}.
+ptrtoint_dst(#llvm_ptrtoint{dst=Dst}) -> Dst.
+ptrtoint_src_type(#llvm_ptrtoint{src_type=Src_Type}) -> Src_Type.
+ptrtoint_src(#llvm_ptrtoint{src=Src}) -> Src.
+ptrtoint_dst_type(#llvm_ptrtoint{dst_type=Dst_Type}) -> Dst_Type .
+
+%% inttoptr
+mk_inttoptr(Dst, Src_Type, Src, Dst_Type) ->
+ #llvm_inttoptr{dst=Dst, src_type=Src_Type, src=Src, dst_type=Dst_Type}.
+inttoptr_dst(#llvm_inttoptr{dst=Dst}) -> Dst.
+inttoptr_src_type(#llvm_inttoptr{src_type=Src_Type}) -> Src_Type.
+inttoptr_src(#llvm_inttoptr{src=Src}) -> Src.
+inttoptr_dst_type(#llvm_inttoptr{dst_type=Dst_Type}) -> Dst_Type .
+
+%% icmp
+mk_icmp(Dst, Cond, Type, Src1, Src2) ->
+ #llvm_icmp{dst=Dst,'cond'=Cond,type=Type,src1=Src1,src2=Src2}.
+icmp_dst(#llvm_icmp{dst=Dst}) -> Dst.
+icmp_cond(#llvm_icmp{'cond'=Cond}) -> Cond.
+icmp_type(#llvm_icmp{type=Type}) -> Type.
+icmp_src1(#llvm_icmp{src1=Src1}) -> Src1.
+icmp_src2(#llvm_icmp{src2=Src2}) -> Src2.
+
+%% fcmp
+mk_fcmp(Dst, Cond, Type, Src1, Src2) ->
+ #llvm_fcmp{dst=Dst,'cond'=Cond,type=Type,src1=Src1,src2=Src2}.
+fcmp_dst(#llvm_fcmp{dst=Dst}) -> Dst.
+fcmp_cond(#llvm_fcmp{'cond'=Cond}) -> Cond.
+fcmp_type(#llvm_fcmp{type=Type}) -> Type.
+fcmp_src1(#llvm_fcmp{src1=Src1}) -> Src1.
+fcmp_src2(#llvm_fcmp{src2=Src2}) -> Src2.
+
+%% phi
+mk_phi(Dst, Type, Value_label_list) ->
+ #llvm_phi{dst=Dst, type=Type,value_label_list=Value_label_list}.
+phi_dst(#llvm_phi{dst=Dst}) -> Dst.
+phi_type(#llvm_phi{type=Type}) -> Type.
+phi_value_label_list(#llvm_phi{value_label_list=Value_label_list}) ->
+ Value_label_list.
+
+%% select
+mk_select(Dst, Cond, Typ1, Val1, Typ2, Val2) ->
+ #llvm_select{dst=Dst, 'cond'=Cond, typ1=Typ1, val1=Val1, typ2=Typ2, val2=Val2}.
+select_dst(#llvm_select{dst=Dst}) -> Dst.
+select_cond(#llvm_select{'cond'=Cond}) -> Cond.
+select_typ1(#llvm_select{typ1=Typ1}) -> Typ1.
+select_val1(#llvm_select{val1=Val1}) -> Val1.
+select_typ2(#llvm_select{typ2=Typ2}) -> Typ2.
+select_val2(#llvm_select{val2=Val2}) -> Val2.
+
+%% call
+mk_call(Dst, Is_tail, Cconv, Ret_attrs, Type, Fnptrval, Arglist, Fn_attrs) ->
+ #llvm_call{dst=Dst, is_tail=Is_tail, cconv=Cconv, ret_attrs=Ret_attrs,
+ type=Type, fnptrval=Fnptrval, arglist=Arglist, fn_attrs=Fn_attrs}.
+call_dst(#llvm_call{dst=Dst}) -> Dst.
+call_is_tail(#llvm_call{is_tail=Is_tail}) -> Is_tail.
+call_cconv(#llvm_call{cconv=Cconv}) -> Cconv.
+call_ret_attrs(#llvm_call{ret_attrs=Ret_attrs}) -> Ret_attrs.
+call_type(#llvm_call{type=Type}) -> Type.
+call_fnptrval(#llvm_call{fnptrval=Fnptrval}) -> Fnptrval.
+call_arglist(#llvm_call{arglist=Arglist}) -> Arglist.
+call_fn_attrs(#llvm_call{fn_attrs=Fn_attrs}) -> Fn_attrs.
+
+%% fun_def
+mk_fun_def(Linkage, Visibility, Cconv, Ret_attrs, Type, Name, Arglist,
+ Fn_attrs, Align, Body) ->
+ #llvm_fun_def{
+ linkage=Linkage,
+ visibility=Visibility,
+ cconv=Cconv,
+ ret_attrs=Ret_attrs,
+ type=Type,
+ 'name'=Name,
+ arglist=Arglist,
+ fn_attrs=Fn_attrs,
+ align=Align,
+ body=Body
+ }.
+
+fun_def_linkage(#llvm_fun_def{linkage=Linkage}) -> Linkage.
+fun_def_visibility(#llvm_fun_def{visibility=Visibility}) -> Visibility.
+fun_def_cconv(#llvm_fun_def{cconv=Cconv}) -> Cconv .
+fun_def_ret_attrs(#llvm_fun_def{ret_attrs=Ret_attrs}) -> Ret_attrs.
+fun_def_type(#llvm_fun_def{type=Type}) -> Type.
+fun_def_name(#llvm_fun_def{'name'=Name}) -> Name.
+fun_def_arglist(#llvm_fun_def{arglist=Arglist}) -> Arglist.
+fun_def_fn_attrs(#llvm_fun_def{fn_attrs=Fn_attrs}) -> Fn_attrs.
+fun_def_align(#llvm_fun_def{align=Align}) -> Align.
+fun_def_body(#llvm_fun_def{body=Body}) -> Body.
+
+%% fun_decl
+mk_fun_decl(Linkage, Visibility, Cconv, Ret_attrs, Type, Name, Arglist, Align)->
+ #llvm_fun_decl{
+ linkage=Linkage,
+ visibility=Visibility,
+ cconv=Cconv,
+ ret_attrs=Ret_attrs,
+ type=Type,
+ 'name'=Name,
+ arglist=Arglist,
+ align=Align
+ }.
+
+fun_decl_linkage(#llvm_fun_decl{linkage=Linkage}) -> Linkage.
+fun_decl_visibility(#llvm_fun_decl{visibility=Visibility}) -> Visibility.
+fun_decl_cconv(#llvm_fun_decl{cconv=Cconv}) -> Cconv .
+fun_decl_ret_attrs(#llvm_fun_decl{ret_attrs=Ret_attrs}) -> Ret_attrs.
+fun_decl_type(#llvm_fun_decl{type=Type}) -> Type.
+fun_decl_name(#llvm_fun_decl{'name'=Name}) -> Name.
+fun_decl_arglist(#llvm_fun_decl{arglist=Arglist}) -> Arglist.
+fun_decl_align(#llvm_fun_decl{align=Align}) -> Align.
+
+%% landingpad
+mk_landingpad() -> #llvm_landingpad{}.
+
+%% comment
+mk_comment(Text) -> #llvm_comment{text=Text}.
+comment_text(#llvm_comment{text=Text}) -> Text.
+
+%% label
+mk_label(Label) -> #llvm_label{label=Label}.
+label_label(#llvm_label{label=Label}) -> Label.
+
+-spec is_label(llvm_instr()) -> boolean().
+is_label(#llvm_label{}) -> true;
+is_label(#llvm_ret{}) -> false;
+is_label(#llvm_br{}) -> false;
+is_label(#llvm_br_cond{}) -> false;
+is_label(#llvm_indirectbr{}) -> false;
+is_label(#llvm_switch{}) -> false;
+is_label(#llvm_invoke{}) -> false;
+is_label(#llvm_operation{}) -> false;
+is_label(#llvm_extractvalue{}) -> false;
+is_label(#llvm_insertvalue{}) -> false;
+is_label(#llvm_alloca{}) -> false;
+is_label(#llvm_load{}) -> false;
+is_label(#llvm_store{}) -> false;
+is_label(#llvm_getelementptr{}) -> false;
+is_label(#llvm_conversion{}) -> false;
+is_label(#llvm_sitofp{}) -> false;
+is_label(#llvm_ptrtoint{}) -> false;
+is_label(#llvm_inttoptr{}) -> false;
+is_label(#llvm_icmp{}) -> false;
+is_label(#llvm_fcmp{}) -> false;
+is_label(#llvm_phi{}) -> false;
+is_label(#llvm_select{}) -> false;
+is_label(#llvm_call{}) -> false;
+is_label(#llvm_fun_def{}) -> false;
+is_label(#llvm_fun_decl{}) -> false;
+is_label(#llvm_landingpad{}) -> false;
+is_label(#llvm_comment{}) -> false;
+is_label(#llvm_const_decl{}) -> false;
+is_label(#llvm_asm{}) -> false;
+is_label(#llvm_adj_stack{}) -> false;
+is_label(#llvm_branch_meta{}) -> false.
+
+%% const_decl
+mk_const_decl(Dst, Decl_type, Type, Value) ->
+ #llvm_const_decl{dst=Dst, decl_type=Decl_type, type=Type, value=Value}.
+const_decl_dst(#llvm_const_decl{dst=Dst}) -> Dst.
+const_decl_decl_type(#llvm_const_decl{decl_type=Decl_type}) -> Decl_type.
+const_decl_type(#llvm_const_decl{type=Type}) -> Type.
+const_decl_value(#llvm_const_decl{value=Value}) -> Value.
+
+%% asm
+mk_asm(Instruction) -> #llvm_asm{instruction=Instruction}.
+asm_instruction(#llvm_asm{instruction=Instruction}) -> Instruction.
+
+%% adj_stack
+mk_adj_stack(Offset, Register, Type) ->
+ #llvm_adj_stack{offset=Offset, 'register'=Register, type=Type}.
+adj_stack_offset(#llvm_adj_stack{offset=Offset}) -> Offset.
+adj_stack_register(#llvm_adj_stack{'register'=Register}) -> Register.
+adj_stack_type(#llvm_adj_stack{type=Type}) -> Type.
+
+%% branch meta-data
+mk_branch_meta(Id, True_weight, False_weight) ->
+ #llvm_branch_meta{id=Id, true_weight=True_weight, false_weight=False_weight}.
+branch_meta_id(#llvm_branch_meta{id=Id}) -> Id.
+branch_meta_true_weight(#llvm_branch_meta{true_weight=True_weight}) ->
+ True_weight.
+branch_meta_false_weight(#llvm_branch_meta{false_weight=False_weight}) ->
+ False_weight.
+
+%% types
+mk_void() -> #llvm_void{}.
+
+mk_label_type() -> #llvm_label_type{}.
+
+mk_int(Width) -> #llvm_int{width=Width}.
+int_width(#llvm_int{width=Width}) -> Width.
+
+mk_double() -> #llvm_double{}.
+
+mk_pointer(Type) -> #llvm_pointer{type=Type}.
+pointer_type(#llvm_pointer{type=Type}) -> Type.
+
+mk_array(Size, Type) -> #llvm_array{'size'=Size, type=Type}.
+array_size(#llvm_array{'size'=Size}) -> Size.
+array_type(#llvm_array{type=Type}) -> Type.
+
+mk_vector(Size, Type) -> #llvm_vector{'size'=Size, type=Type}.
+vector_size(#llvm_vector{'size'=Size}) -> Size.
+vector_type(#llvm_vector{type=Type}) -> Type.
+
+mk_struct(Type_list) -> #llvm_struct{type_list=Type_list}.
+struct_type_list(#llvm_struct{type_list=Type_list}) -> Type_list.
+
+mk_fun(Ret_type, Arg_type_list) ->
+ #llvm_fun{ret_type=Ret_type, arg_type_list=Arg_type_list}.
+function_ret_type(#llvm_fun{ret_type=Ret_type}) -> Ret_type.
+function_arg_type_list(#llvm_fun{arg_type_list=Arg_type_list}) ->
+ Arg_type_list.
+
+%%----------------------------------------------------------------------------
+%% Pretty-printer Functions
+%%----------------------------------------------------------------------------
+
+%% @doc Pretty-print a list of LLVM instructions to a Device.
+pp_ins_list(_Dev, []) -> ok;
+pp_ins_list(Dev, [I|Is]) ->
+ pp_ins(Dev, I),
+ pp_ins_list(Dev, Is).
+
+pp_ins(Dev, I) ->
+ case indent(I) of
+ true -> write(Dev, " ");
+ false -> ok
+ end,
+ case I of
+ #llvm_ret{} ->
+ write(Dev, "ret "),
+ case ret_ret_list(I) of
+ [] -> write(Dev, "void");
+ List -> pp_args(Dev, List)
+ end,
+ write(Dev, "\n");
+ #llvm_br{} ->
+ write(Dev, ["br label ", br_dst(I), "\n"]);
+ #llvm_switch{} ->
+ write(Dev, "switch "),
+ pp_type(Dev, switch_type(I)),
+ write(Dev, [" ", switch_value(I), ", label ", switch_default_label(I),
+ " \n [\n"]),
+ pp_switch_value_label_list(Dev, switch_type(I),
+ switch_value_label_list(I)),
+ write(Dev, " ]\n");
+ #llvm_invoke{} ->
+ write(Dev, [invoke_dst(I), " = invoke ", invoke_cconv(I), " "]),
+ pp_options(Dev, invoke_ret_attrs(I)),
+ pp_type(Dev, invoke_type(I)),
+ write(Dev, [" ", invoke_fnptrval(I), "("]),
+ pp_args(Dev, invoke_arglist(I)),
+ write(Dev, ") "),
+ pp_options(Dev, invoke_fn_attrs(I)),
+ write(Dev, [" to label ", invoke_to_label(I)," unwind label ",
+ invoke_unwind_label(I), " \n"]);
+ #llvm_br_cond{} ->
+ write(Dev, ["br i1 ", br_cond_cond(I), ", label ", br_cond_true_label(I),
+ ", label ", br_cond_false_label(I)]),
+ case br_cond_meta(I) of
+ [] -> ok;
+ Metadata ->
+ write(Dev, [", !prof !", Metadata])
+ end,
+ write(Dev, "\n");
+ #llvm_indirectbr{} ->
+ write(Dev, "indirectbr "),
+ pp_type(Dev, indirectbr_type(I)),
+ write(Dev, [" ", indirectbr_address(I), ", [ "]),
+ pp_args(Dev, indirectbr_label_list(I)),
+ write(Dev, " ]\n");
+ #llvm_operation{} ->
+ write(Dev, [operation_dst(I), " = ", atom_to_list(operation_op(I)), " "]),
+ case op_has_options(operation_op(I)) of
+ true -> pp_options(Dev, operation_options(I));
+ false -> ok
+ end,
+ pp_type(Dev, operation_type(I)),
+ write(Dev, [" ", operation_src1(I), ", ", operation_src2(I), "\n"]);
+ #llvm_extractvalue{} ->
+ write(Dev, [extractvalue_dst(I), " = extractvalue "]),
+ pp_type(Dev, extractvalue_type(I)),
+ %% TODO Print idxs
+ write(Dev, [" ", extractvalue_val(I), ", ", extractvalue_idx(I), "\n"]);
+ #llvm_insertvalue{} ->
+ write(Dev, [insertvalue_dst(I), " = insertvalue "]),
+ pp_type(Dev, insertvalue_val_type(I)),
+ write(Dev, [" ", insertvalue_val(I), ", "]),
+ pp_type(Dev, insertvalue_elem_type(I)),
+ %%TODO Print idxs
+ write(Dev, [" ", insertvalue_elem(I), ", ", insertvalue_idx(I), "\n"]);
+ #llvm_alloca{} ->
+ write(Dev, [alloca_dst(I), " = alloca "]),
+ pp_type(Dev, alloca_type(I)),
+ case alloca_num(I) of
+ [] -> ok;
+ Num ->
+ write(Dev, ", "),
+ pp_type(Dev, alloca_type(I)),
+ write(Dev, [" ", Num, " "])
+ end,
+ case alloca_align(I) of
+ [] -> ok;
+ Align -> write(Dev, [",align ", Align])
+ end,
+ write(Dev, "\n");
+ #llvm_load{} ->
+ write(Dev, [load_dst(I), " = "]),
+ write(Dev, "load "),
+ case load_volatile(I) of
+ true -> write(Dev, "volatile ");
+ false -> ok
+ end,
+ pp_type(Dev, load_p_type(I)),
+ write(Dev, [" ", load_pointer(I), " "]),
+ case load_alignment(I) of
+ [] -> ok;
+ Al -> write(Dev, [", align ", Al, " "])
+ end,
+ case load_nontemporal(I) of
+ [] -> ok;
+ In -> write(Dev, [", !nontemporal !", In])
+ end,
+ write(Dev, "\n");
+ #llvm_store{} ->
+ write(Dev, "store "),
+ case store_volatile(I) of
+ true -> write(Dev, "volatile ");
+ false -> ok
+ end,
+ pp_type(Dev, store_type(I)),
+ write(Dev, [" ", store_value(I), ", "]),
+ pp_type(Dev, store_p_type(I)),
+ write(Dev, [" ", store_pointer(I), " "]),
+ case store_alignment(I) of
+ [] -> ok;
+ Al -> write(Dev, [", align ", Al, " "])
+ end,
+ case store_nontemporal(I) of
+ [] -> ok;
+ In -> write(Dev, [", !nontemporal !", In])
+ end,
+ write(Dev, "\n");
+ #llvm_getelementptr{} ->
+ write(Dev, [getelementptr_dst(I), " = getelementptr "]),
+ case getelementptr_inbounds(I) of
+ true -> write(Dev, "inbounds ");
+ false -> ok
+ end,
+ pp_type(Dev, getelementptr_p_type(I)),
+ write(Dev, [" ", getelementptr_value(I)]),
+ pp_typed_idxs(Dev, getelementptr_typed_idxs(I)),
+ write(Dev, "\n");
+ #llvm_conversion{} ->
+ write(Dev, [conversion_dst(I), " = ", atom_to_list(conversion_op(I)), " "]),
+ pp_type(Dev, conversion_src_type(I)),
+ write(Dev, [" ", conversion_src(I), " to "]),
+ pp_type(Dev, conversion_dst_type(I)),
+ write(Dev, "\n");
+ #llvm_icmp{} ->
+ write(Dev, [icmp_dst(I), " = icmp ", atom_to_list(icmp_cond(I)), " "]),
+ pp_type(Dev, icmp_type(I)),
+ write(Dev, [" ", icmp_src1(I), ", ", icmp_src2(I), "\n"]);
+ #llvm_fcmp{} ->
+ write(Dev, [fcmp_dst(I), " = fcmp ", atom_to_list(fcmp_cond(I)), " "]),
+ pp_type(Dev, fcmp_type(I)),
+ write(Dev, [" ", fcmp_src1(I), ", ", fcmp_src2(I), "\n"]);
+ #llvm_phi{} ->
+ write(Dev, [phi_dst(I), " = phi "]),
+ pp_type(Dev, phi_type(I)),
+ pp_phi_value_labels(Dev, phi_value_label_list(I)),
+ write(Dev, "\n");
+ #llvm_select{} ->
+ write(Dev, [select_dst(I), " = select i1 ", select_cond(I), ", "]),
+ pp_type(Dev, select_typ1(I)),
+ write(Dev, [" ", select_val1(I), ", "]),
+ pp_type(Dev, select_typ2(I)),
+ write(Dev, [" ", select_val2(I), "\n"]);
+ #llvm_call{} ->
+ case call_dst(I) of
+ [] -> ok;
+ Dst -> write(Dev, [Dst, " = "])
+ end,
+ case call_is_tail(I) of
+ true -> write(Dev, "tail ");
+ false -> ok
+ end,
+ write(Dev, ["call ", call_cconv(I), " "]),
+ pp_options(Dev, call_ret_attrs(I)),
+ pp_type(Dev, call_type(I)),
+ write(Dev, [" ", call_fnptrval(I), "("]),
+ pp_args(Dev, call_arglist(I)),
+ write(Dev, ") "),
+ pp_options(Dev, call_fn_attrs(I)),
+ write(Dev, "\n");
+ #llvm_fun_def{} ->
+ write(Dev, "define "),
+ pp_options(Dev, fun_def_linkage(I)),
+ pp_options(Dev, fun_def_visibility(I)),
+ case fun_def_cconv(I) of
+ [] -> ok;
+ Cc -> write(Dev, [Cc, " "])
+ end,
+ pp_options(Dev, fun_def_ret_attrs(I)),
+ write(Dev, " "),
+ pp_type(Dev, fun_def_type(I)),
+ write(Dev, [" @", fun_def_name(I), "("]),
+ pp_args(Dev, fun_def_arglist(I)),
+ write(Dev, ") "),
+ pp_options(Dev, fun_def_fn_attrs(I)),
+ case fun_def_align(I) of
+ [] -> ok;
+ N -> write(Dev, ["align ", N])
+ end,
+ write(Dev, "{\n"),
+ pp_ins_list(Dev, fun_def_body(I)),
+ write(Dev, "}\n");
+ #llvm_fun_decl{} ->
+ write(Dev, "declare "),
+ pp_options(Dev, fun_decl_linkage(I)),
+ pp_options(Dev, fun_decl_visibility(I)),
+ case fun_decl_cconv(I) of
+ [] -> ok;
+ Cc -> write(Dev, [Cc, " "])
+ end,
+ pp_options(Dev, fun_decl_ret_attrs(I)),
+ pp_type(Dev, fun_decl_type(I)),
+ write(Dev, [" ", fun_decl_name(I), "("]),
+ pp_type_list(Dev, fun_decl_arglist(I)),
+ write(Dev, ") "),
+ case fun_decl_align(I) of
+ [] -> ok;
+ N -> write(Dev, ["align ", N])
+ end,
+ write(Dev, "\n");
+ #llvm_comment{} ->
+ write(Dev, ["; ", atom_to_list(comment_text(I)), "\n"]);
+ #llvm_label{} ->
+ write(Dev, [label_label(I), ":\n"]);
+ #llvm_const_decl{} ->
+ write(Dev, [const_decl_dst(I), " = ", const_decl_decl_type(I), " "]),
+ pp_type(Dev, const_decl_type(I)),
+ write(Dev, [" ", const_decl_value(I), "\n"]);
+ #llvm_landingpad{} ->
+ write(Dev, "landingpad { i8*, i32 } personality i32 (i32, i64, i8*,i8*)*
+ @__gcc_personality_v0 cleanup\n");
+ #llvm_asm{} ->
+ write(Dev, [asm_instruction(I), "\n"]);
+ #llvm_adj_stack{} ->
+ write(Dev, ["call void asm sideeffect \"sub $0, ",
+ adj_stack_register(I), "\", \"r\"("]),
+ pp_type(Dev, adj_stack_type(I)),
+ write(Dev, [" ", adj_stack_offset(I),")\n"]);
+ #llvm_branch_meta{} ->
+ write(Dev, ["!", branch_meta_id(I), " = metadata !{metadata !\"branch_weights\",
+ i32 ", branch_meta_true_weight(I), ", i32 ",
+ branch_meta_false_weight(I), "}\n"]);
+ Other ->
+ exit({?MODULE, pp_ins, {"Unknown LLVM instruction", Other}})
+ end.
+
+%% @doc Pretty-print a list of types
+pp_type_list(_Dev, []) -> ok;
+pp_type_list(Dev, [T]) ->
+ pp_type(Dev, T);
+pp_type_list(Dev, [T|Ts]) ->
+ pp_type(Dev, T),
+ write(Dev, ", "),
+ pp_type_list(Dev, Ts).
+
+pp_type(Dev, Type) ->
+ case Type of
+ #llvm_void{} ->
+ write(Dev, "void");
+ #llvm_label_type{} ->
+ write(Dev, "label");
+ %% Integer
+ #llvm_int{} ->
+ write(Dev, ["i", integer_to_list(int_width(Type))]);
+ %% Float
+ #llvm_float{} ->
+ write(Dev, "float");
+ #llvm_double{} ->
+ write(Dev, "double");
+ #llvm_fp80{} ->
+ write(Dev, "x86_fp80");
+ #llvm_fp128{} ->
+ write(Dev, "fp128");
+ #llvm_ppc_fp128{} ->
+ write(Dev, "ppc_fp128");
+ %% Pointer
+ #llvm_pointer{} ->
+ pp_type(Dev, pointer_type(Type)),
+ write(Dev, "*");
+ %% Function
+ #llvm_fun{} ->
+ pp_type(Dev, function_ret_type(Type)),
+ write(Dev, " ("),
+ pp_type_list(Dev, function_arg_type_list(Type)),
+ write(Dev, ")");
+ %% Aggregate
+ #llvm_array{} ->
+ write(Dev, ["[", integer_to_list(array_size(Type)), " x "]),
+ pp_type(Dev, array_type(Type)),
+ write(Dev, "]");
+ #llvm_struct{} ->
+ write(Dev, "{"),
+ pp_type_list(Dev, struct_type_list(Type)),
+ write(Dev, "}");
+ #llvm_vector{} ->
+ write(Dev, ["{", integer_to_list(vector_size(Type)), " x "]),
+ pp_type(Dev, vector_type(Type)),
+ write(Dev, "}")
+ end.
+
+%% @doc Pretty-print a list of typed arguments
+pp_args(_Dev, []) -> ok;
+pp_args(Dev, [{Type, Arg} | []]) ->
+ pp_type(Dev, Type),
+ write(Dev, [" ", Arg]);
+pp_args(Dev, [{Type, Arg} | Args]) ->
+ pp_type(Dev, Type),
+ write(Dev, [" ", Arg, ", "]),
+ pp_args(Dev, Args).
+
+%% @doc Pretty-print a list of options
+pp_options(_Dev, []) -> ok;
+pp_options(Dev, [O|Os]) ->
+ write(Dev, [atom_to_list(O), " "]),
+ pp_options(Dev, Os).
+
+%% @doc Pretty-print a list of phi value-labels
+pp_phi_value_labels(_Dev, []) -> ok;
+pp_phi_value_labels(Dev, [{Value, Label}|[]]) ->
+ write(Dev, ["[ ", Value, ", ", Label, " ]"]);
+pp_phi_value_labels(Dev,[{Value, Label}|VL]) ->
+ write(Dev, ["[ ", Value, ", ", Label, " ], "]),
+ pp_phi_value_labels(Dev, VL).
+
+%% @doc Pretty-print a list of typed indexes
+pp_typed_idxs(_Dev, []) -> ok;
+pp_typed_idxs(Dev, [{Type, Id} | Tids]) ->
+ write(Dev, ", "),
+ pp_type(Dev, Type),
+ write(Dev, [" ", Id]),
+ pp_typed_idxs(Dev, Tids).
+
+%% @doc Pretty-print a switch label list
+pp_switch_value_label_list(_Dev, _Type, []) -> ok;
+pp_switch_value_label_list(Dev, Type, [{Value, Label} | VLs]) ->
+ write(Dev, " "),
+ pp_type(Dev, Type),
+ write(Dev, [" ", Value, ", label ", Label, "\n"]),
+ pp_switch_value_label_list(Dev, Type, VLs).
+
+%%----------------------------------------------------------------------------
+%% Auxiliary Functions
+%%----------------------------------------------------------------------------
+
+%% @doc Returns if an instruction needs to be intended
+indent(I) ->
+ case I of
+ #llvm_label{} -> false;
+ #llvm_fun_def{} -> false;
+ #llvm_fun_decl{} -> false;
+ #llvm_const_decl{} -> false;
+ #llvm_branch_meta{} -> false;
+ _ -> true
+ end.
+
+op_has_options(Op) ->
+ case Op of
+ 'and' -> false;
+ 'or' -> false;
+ 'xor' -> false;
+ _ -> true
+ end.
+
+%% @doc Abstracts actual writing to file operations
+write(Dev, Msg) ->
+ ok = file:write(Dev, Msg).
diff --git a/lib/hipe/llvm/hipe_llvm_arch.hrl b/lib/hipe/llvm/hipe_llvm_arch.hrl
new file mode 100644
index 0000000000..689a5a52ea
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_arch.hrl
@@ -0,0 +1,11 @@
+-ifdef(BIT32).
+-define(NR_PINNED_REGS, 2).
+-define(NR_ARG_REGS, 3).
+-define(ARCH_REGISTERS, hipe_x86_registers).
+-define(FLOAT_OFFSET, 2).
+-else.
+-define(NR_PINNED_REGS, 2).
+-define(NR_ARG_REGS, 4).
+-define(ARCH_REGISTERS, hipe_amd64_registers).
+-define(FLOAT_OFFSET, 6).
+-endif.
diff --git a/lib/hipe/llvm/hipe_llvm_liveness.erl b/lib/hipe/llvm/hipe_llvm_liveness.erl
new file mode 100644
index 0000000000..d1c90ed4c9
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_liveness.erl
@@ -0,0 +1,112 @@
+-module(hipe_llvm_liveness).
+
+-export([analyze/1]).
+
+%% @doc Find gc roots and explicitly mark when they go out of scope, based
+%% on the liveness analyzis performed by the hipe_rtl_liveness:analyze/1.
+analyze(RtlCfg) ->
+ Liveness = hipe_rtl_liveness:analyze(RtlCfg),
+ Roots = find_roots(RtlCfg, Liveness),
+ %% erlang:display(Roots),
+ NewRtlCfg = mark_dead_roots(RtlCfg, Liveness, Roots),
+ {NewRtlCfg, Roots}.
+
+%% @doc Determine which are the GC Roots.Possible roots are all
+%% RTL variables (rtl_var). However, since safe points are function calls, we
+%% consider as possible GC roots only RTL variables that are live around
+%% function calls.
+find_roots(Cfg, Liveness) ->
+ Labels = hipe_rtl_cfg:postorder(Cfg),
+ Roots = find_roots_bb(Labels, Cfg, Liveness, []),
+ lists:usort(lists:flatten(Roots)).
+
+find_roots_bb([], _Cfg, _Liveness, RootAcc) ->
+ RootAcc;
+find_roots_bb([L|Ls], Cfg, Liveness, RootAcc) ->
+ Block = hipe_rtl_cfg:bb(Cfg, L),
+ BlockCode = hipe_bb:code(Block),
+ LiveIn = ordsets:from_list(strip(hipe_rtl_liveness:livein(Liveness, L))),
+ LiveOut = ordsets:from_list(strip(hipe_rtl_liveness:liveout(Liveness, L))),
+ Roots = do_find_roots_bb(BlockCode, L, LiveOut, LiveIn, []),
+ find_roots_bb(Ls, Cfg, Liveness, Roots++RootAcc).
+
+%% For each call inside a BB the GC roots are those RTL variables that
+%% are live before and after the call.
+%% --> Live Before Call: These are the RTL variables that belong to the
+%% LiveIn list or are initialized inside the BB before the call
+%% --> Live After Call: These are the RTL variables that belong to the
+%% LiveOut list or are used after the call inside the BB (they die
+%% inside the BB and so do not belong to the LiveOut list)
+do_find_roots_bb([], _Label, _LiveOut, _LiveBefore, RootAcc) ->
+ RootAcc;
+do_find_roots_bb([I|Is], L, LiveOut, LiveBefore, RootAcc) ->
+ case hipe_rtl:is_call(I) of
+ true ->
+ %% Used inside the BB after the call
+ UsedAfterCall_ = strip(lists:flatten([hipe_rtl:uses(V) || V <- Is])),
+ UsedAfterCall = ordsets:from_list(UsedAfterCall_),
+ LiveAfter = ordsets:union(UsedAfterCall, LiveOut),
+ %% The Actual Roots
+ Roots = ordsets:intersection(LiveBefore, LiveAfter),
+ %% The result of the instruction
+ Defines = ordsets:from_list(strip(hipe_rtl:defines(I))),
+ LiveBefore1 = ordsets:union(LiveBefore, Defines),
+ do_find_roots_bb(Is, L, LiveOut, LiveBefore1, [Roots|RootAcc]);
+ false ->
+ %% The result of the instruction
+ Defines = ordsets:from_list(strip(hipe_rtl:defines(I))),
+ LiveBefore1 = ordsets:union(LiveBefore, Defines),
+ do_find_roots_bb(Is, L, LiveOut, LiveBefore1, RootAcc)
+ end.
+
+%% @doc This function is responsible for marking when GC Roots, which can be
+%% only RTL variables go out of scope (dead). This pass is needed for the LLVM
+%% back end because the LLVM framework forces us to explicit mark when gc roots
+%% are no longer live.
+mark_dead_roots(CFG, Liveness, Roots) ->
+ Labels = hipe_rtl_cfg:postorder(CFG),
+ mark_dead_bb(Labels, CFG, Liveness, Roots).
+
+mark_dead_bb([], Cfg, _Liveness, _Roots) ->
+ Cfg;
+mark_dead_bb([L|Ls], Cfg, Liveness, Roots) ->
+ Block = hipe_rtl_cfg:bb(Cfg, L),
+ BlockCode = hipe_bb:code(Block),
+ LiveOut = ordsets:from_list(strip(hipe_rtl_liveness:liveout(Liveness, L))),
+ NewBlockCode = do_mark_dead_bb(BlockCode, LiveOut, Roots, []),
+ %% Update the CFG
+ NewBB = hipe_bb:code_update(Block, NewBlockCode),
+ NewCFG = hipe_rtl_cfg:bb_add(Cfg, L, NewBB),
+ mark_dead_bb(Ls, NewCFG, Liveness, Roots).
+
+do_mark_dead_bb([], _LiveOut, _Roots, NewBlockCode) ->
+ lists:reverse(NewBlockCode);
+do_mark_dead_bb([I|Is], LiveOut ,Roots, NewBlockCode) ->
+ Uses = ordsets:from_list(strip(hipe_rtl:uses(I))),
+ %% GC roots that are used in this instruction
+ RootsUsed = ordsets:intersection(Roots, Uses),
+ UsedAfter_ = strip(lists:flatten([hipe_rtl:uses(V) || V <- Is])),
+ UsedAfter = ordsets:from_list(UsedAfter_),
+ %% GC roots that are live after this instruction
+ LiveAfter = ordsets:union(LiveOut, UsedAfter),
+ %% GC roots that their last use is in this instruction
+ DeadRoots = ordsets:subtract(RootsUsed, LiveAfter),
+ %% Recreate the RTL variable from the corresponding Index
+ OldVars = [hipe_rtl:mk_var(V1) || V1 <- DeadRoots],
+ %% Mark the RTL variable as DEAD (last use)
+ NewVars = [kill_var(V2) || V2 <- OldVars],
+ %% Create a list with the substitution of the old vars with the new
+ %% ones which are marked with the dead keyword
+ Subtitution = lists:zip(OldVars, NewVars),
+ NewI = case Subtitution of
+ [] -> I;
+ _ -> hipe_rtl:subst_uses_llvm(Subtitution, I)
+ end,
+ do_mark_dead_bb(Is, LiveOut, Roots, [NewI|NewBlockCode]).
+
+%% Update the liveness of a var,in order to mark that this is the last use.
+kill_var(Var) -> hipe_rtl:var_liveness_update(Var, dead).
+
+%% We are only interested for rtl_vars, since only rtl_vars are possible gc
+%% roots.
+strip(L) -> [Y || {rtl_var, Y, _} <- L].
diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
new file mode 100644
index 0000000000..e911fb89c9
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -0,0 +1,514 @@
+%% -*- erlang-indent-level: 2 -*-
+-module(hipe_llvm_main).
+
+-export([rtl_to_native/4]).
+
+-include("../../kernel/src/hipe_ext_format.hrl").
+-include("hipe_llvm_arch.hrl").
+-include("elf_format.hrl").
+
+%% @doc Translation of RTL to a loadable object. This function takes the RTL
+%% code and calls hipe_rtl_to_llvm:translate/2 to translate the RTL code to
+%% LLVM code. After this, LLVM asm is printed to a file and the LLVM tool
+%% chain is invoked in order to produce an object file.
+rtl_to_native(MFA, RTL, Roots, Options) ->
+ %% Compile to LLVM and get Instruction List (along with infos)
+ {LLVMCode, RelocsDict, ConstTab} =
+ hipe_rtl_to_llvm:translate(RTL, Roots),
+ %% Fix function name to an acceptable LLVM identifier (needed for closures)
+ {_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA),
+ %% Write LLVM Assembly to intermediate file (on disk)
+ {ok, Dir, ObjectFile} =
+ compile_with_llvm(Fun, Arity, LLVMCode, Options, false),
+ %%
+ %% Extract information from object file
+ %%
+ ObjBin = open_object_file(ObjectFile),
+ %% Read and set the ELF class
+ elf_format:set_architecture_flag(ObjBin),
+ %% Get labels info (for switches and jump tables)
+ Labels = elf_format:get_rodata_relocs(ObjBin),
+ {Switches, Closures} = get_tables(ObjBin),
+ %% Associate Labels with Switches and Closures with stack args
+ {SwitchInfos, ExposedClosures} =
+ correlate_labels(Switches ++ Closures, Labels),
+ %% SwitchInfos: [{"table_50", [Labels]}]
+ %% ExposedClosures: [{"table_closures", [Labels]}]
+
+ %% Labelmap contains the offsets of the labels in the code that are
+ %% used for switch's jump tables
+ LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict),
+ %% Get relocation info
+ TextRelocs = elf_format:get_text_relocs(ObjBin),
+ %% AccRefs contains the offsets of all references to relocatable symbols in
+ %% the code:
+ AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA),
+ %% Get stack descriptors
+ SDescs = get_sdescs(ObjBin),
+ %% FixedSDescs are the stack descriptors after correcting calls that have
+ %% arguments in the stack
+ FixedSDescs =
+ fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures),
+ Refs = AccRefs ++ FixedSDescs,
+ %% Get binary code from object file
+ BinCode = elf_format:extract_text(ObjBin),
+ %% Remove temp files (if needed)
+ ok = remove_temp_folder(Dir, Options),
+ %% Return the code together with information that will be used in the
+ %% hipe_llvm_merge module to produce the final binary that will be loaded
+ %% by the hipe unified loader.
+ {MFA, BinCode, byte_size(BinCode), ConstTab, Refs, LabelMap}.
+
+%%------------------------------------------------------------------------------
+%% LLVM tool chain
+%%------------------------------------------------------------------------------
+
+%% @doc Compile function FunName/Arity to LLVM. Return Dir (in order to remove
+%% it if we do not want to store temporary files) and ObjectFile name that
+%% is created by the LLVM tools.
+compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) ->
+ Filename = atom_to_list(FunName) ++ "_" ++ integer_to_list(Arity),
+ %% Save temp files in a unique folder
+ Dir = unique_folder(FunName, Arity, Options),
+ ok = file:make_dir(Dir),
+ %% Print LLVM assembly to file
+ OpenOpts = [append, raw] ++
+ case UseBuffer of
+ %% true -> [delayed_write]; % Use delayed_write!
+ false -> []
+ end,
+ {ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts),
+ hipe_llvm:pp_ins_list(File_llvm, LLVMCode),
+ %% delayed_write can cause file:close not to do a close, hence the two calls
+ ok = file:close(File_llvm),
+ __ = file:close(File_llvm),
+ %% Invoke LLVM compiler tools to produce an object file
+ llvm_opt(Dir, Filename, Options),
+ llvm_llc(Dir, Filename, Options),
+ compile(Dir, Filename, "gcc"), %%FIXME: use llc -filetype=obj and skip this!
+ {ok, Dir, Dir ++ Filename ++ ".o"}.
+
+%% @doc Invoke opt tool to optimize the bitcode (_name.ll -> _name.bc).
+llvm_opt(Dir, Filename, Options) ->
+ Source = Dir ++ Filename ++ ".ll",
+ Dest = Dir ++ Filename ++ ".bc",
+ OptLevel = trans_optlev_flag(opt, Options),
+ OptFlags = [OptLevel, "-mem2reg", "-strip"],
+ Command = "opt " ++ fix_opts(OptFlags) ++ " " ++ Source ++ " -o " ++ Dest,
+ %% io:format("OPT: ~s~n", [Command]),
+ case os:cmd(Command) of
+ "" -> ok;
+ Error -> exit({?MODULE, opt, Error})
+ end.
+
+%% @doc Invoke llc tool to compile the bitcode to object file
+%% (_name.bc -> _name.o).
+llvm_llc(Dir, Filename, Options) ->
+ Source = Dir ++ Filename ++ ".bc",
+ OptLevel = trans_optlev_flag(llc, Options),
+ Align = find_stack_alignment(),
+ LlcFlags = [OptLevel, "-code-model=medium", "-stack-alignment=" ++ Align
+ , "-tailcallopt", "-filetype=asm"], %%FIXME
+ Command = "llc " ++ fix_opts(LlcFlags) ++ " " ++ Source,
+ %% io:format("LLC: ~s~n", [Command]),
+ case os:cmd(Command) of
+ "" -> ok;
+ Error -> exit({?MODULE, llc, Error})
+ end.
+
+%% @doc Invoke the compiler tool ("gcc", "llvmc", etc.) to generate an object
+%% file from native assembly.
+compile(Dir, Fun_Name, Compiler) ->
+ Source = Dir ++ Fun_Name ++ ".s",
+ Dest = Dir ++ Fun_Name ++ ".o",
+ Command = Compiler ++ " -c " ++ Source ++ " -o " ++ Dest,
+ %% io:format("~s: ~s~n", [Compiler, Command]),
+ case os:cmd(Command) of
+ "" -> ok;
+ Error -> exit({?MODULE, cc, Error})
+ end.
+
+find_stack_alignment() ->
+ case get(hipe_target_arch) of
+ x86 -> "4";
+ amd64 -> "8";
+ _ -> exit({?MODULE, find_stack_alignment, "Unimplemented architecture"})
+ end.
+
+%% @doc Join options.
+fix_opts(Opts) ->
+ string:join(Opts, " ").
+
+%% @doc Translate optimization-level flag (default is "O2").
+trans_optlev_flag(Tool, Options) ->
+ Flag = case Tool of
+ opt -> llvm_opt;
+ llc -> llvm_llc
+ end,
+ case proplists:get_value(Flag, Options) of
+ o0 -> ""; % "-O0" does not exist in opt tool
+ o1 -> "-O1";
+ o2 -> "-O2";
+ o3 -> "-O3";
+ undefined -> "-O2"
+ end.
+
+%%------------------------------------------------------------------------------
+%% Functions to manage Relocations
+%%------------------------------------------------------------------------------
+
+%% @doc Get switch table and closure table.
+get_tables(Elf) ->
+ %% Search Symbol Table for an entry with name prefixed with "table_":
+ Triples = elf_format:get_tab_entries(Elf),
+ Switches = [T || T={"table_" ++ _, _, _} <- Triples],
+ Closures = [T || T={"table_closures" ++ _, _, _} <- Switches],
+ {Switches, Closures}.
+
+%% @doc This function associates symbols who point to some table of labels with
+%% the corresponding offsets of the labels in the code. These tables can
+%% either be jump tables for switches or a table which contains the labels
+%% of blocks that contain closure calls with more than ?NR_ARG_REGS.
+correlate_labels([], _L) -> {[], []};
+correlate_labels(Tables, Labels) ->
+ %% Sort "Tables" based on "ValueOffsets"
+ OffsetSortedTb = lists:ukeysort(2, Tables),
+ %% Unzip offset-sorted list of "Switches"
+ {Names, _Offsets, TablesSizeList} = lists:unzip3(OffsetSortedTb),
+ %% Associate switch names with labels
+ L = split_list(Labels, TablesSizeList),
+ %% Zip back! (to [{SwitchName, Values}])
+ NamesValues = lists:zip(Names, L),
+ case lists:keytake("table_closures", 1, NamesValues) of
+ false -> %% No closures in the code, no closure table
+ {NamesValues, []};
+ {value, ClosureTableNV, SwitchesNV} ->
+ {SwitchesNV, ClosureTableNV}
+ end.
+
+%% @doc Create a gb_tree which contains information about the labels that used
+%% for switch's jump tables. The keys of the gb_tree are of the form
+%% {MFA, Label} and the values are the actual Offsets.
+create_labelmap(MFA, SwitchInfos, RelocsDict) ->
+ create_labelmap(MFA, SwitchInfos, RelocsDict, gb_trees:empty()).
+
+create_labelmap(_, [], _, LabelMap) -> LabelMap;
+create_labelmap(MFA, [{Name, Offsets} | Rest], RelocsDict, LabelMap) ->
+ case dict:fetch(Name, RelocsDict) of
+ {switch, {_TableType, LabelList, _NrLabels, _SortOrder}, _JTabLab} ->
+ KVDict = lists:ukeysort(1, lists:zip(LabelList, Offsets)),
+ NewLabelMap = insert_to_labelmap(KVDict, LabelMap),
+ create_labelmap(MFA, Rest, RelocsDict, NewLabelMap);
+ _ ->
+ exit({?MODULE, create_labelmap, "Not a jump table!"})
+ end.
+
+%% @doc Insert a list of [{Key,Value}] to a LabelMap (gb_tree).
+insert_to_labelmap([], LabelMap) -> LabelMap;
+insert_to_labelmap([{Key, Value}|Rest], LabelMap) ->
+ case gb_trees:lookup(Key, LabelMap) of
+ none ->
+ insert_to_labelmap(Rest, gb_trees:insert(Key, Value, LabelMap));
+ {value, Value} -> %% Exists with the *exact* same Value.
+ insert_to_labelmap(Rest, LabelMap)
+ end.
+
+%% @doc Correlate object file relocation symbols with info from translation to
+%% llvm code.
+fix_relocations(Relocs, RelocsDict, MFA) ->
+ fix_relocs(Relocs, RelocsDict, MFA, []).
+
+fix_relocs([], _, _, RelocAcc) -> RelocAcc;
+fix_relocs([{Name, Offset}|Rs], RelocsDict, {ModName,_,_}=MFA, RelocAcc) ->
+ case dict:fetch(Name, RelocsDict) of
+ {atom, AtomName} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ATOM, Offset, AtomName}|RelocAcc]);
+ {constant, Label} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ADDRESS, Offset, {constant, Label}}|RelocAcc]);
+ {switch, _, JTabLab} -> %% Treat switch exactly as constant
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ADDRESS, Offset, {constant, JTabLab}}|RelocAcc]);
+ {closure, _}=Closure ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ADDRESS, Offset, Closure}|RelocAcc]);
+ {call, {bif, BifName, _}} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?CALL_LOCAL, Offset, BifName}|RelocAcc]);
+ %% MFA calls to functions in the same module are of type 3, while all
+ %% other MFA calls are of type 2.
+ {call, {ModName,_F,_A}=CallMFA} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?CALL_LOCAL, Offset, CallMFA}|RelocAcc]);
+ {call, CallMFA} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?CALL_REMOTE, Offset, CallMFA}|RelocAcc]);
+ Other ->
+ exit({?MODULE, fix_relocs,
+ {"Relocation not in relocation dictionary", Other}})
+ end.
+
+%%------------------------------------------------------------------------------
+%% Functions to manage Stack Descriptors
+%%------------------------------------------------------------------------------
+
+%% @doc This function takes an ELF Object File binary and returns a proper sdesc
+%% list for Erlang/OTP System's loader. The return value should be of the
+%% form:
+%% {
+%% 4, Safepoint Address,
+%% {ExnLabel OR [], FrameSize, StackArity, {Liveroot stack frame indexes}},
+%% }
+get_sdescs(Elf) ->
+ case elf_format:extract_note(Elf, ?NOTE_ERLGC_NAME) of
+ <<>> -> % Object file has no ".note.gc" section!
+ [];
+ NoteGC_bin ->
+ %% Get safe point addresses (stored in ".rela.note.gc" section):
+ RelaNoteGC = elf_format:extract_rela(Elf, ?NOTE(?NOTE_ERLGC_NAME)),
+ SPCount = length(RelaNoteGC),
+ T = SPCount * ?SP_ADDR_SIZE,
+ %% Pattern match fields of ".note.gc":
+ <<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check!
+ SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
+ StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little,
+ StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little,
+ _LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip
+ Roots/binary>> = NoteGC_bin,
+ LiveRoots = get_liveroots(Roots, []),
+ %% Extract information about the safe point addresses:
+ SPOffs =
+ case elf_format:is64bit() of
+ true -> %% Find offsets in ".rela.note.gc":
+ elf_format:get_rela_addends(RelaNoteGC);
+ false -> %% Find offsets in SPAddrs (in ".note.gc"):
+ get_spoffs(SPAddrs, [])
+ end,
+ %% Extract Exception Handler labels:
+ ExnHandlers = elf_format:get_exn_handlers(Elf),
+ %% Combine ExnHandlers and Safe point addresses (return addresses):
+ ExnAndSPOffs = combine_ras_and_exns(ExnHandlers, SPOffs, []),
+ create_sdesc_list(ExnAndSPOffs, StkFrameSize, StkArity, LiveRoots, [])
+ end.
+
+%% @doc Extracts a bunch of integers (live roots) from a binary. Returns a tuple
+%% as need for stack descriptors.
+get_liveroots(<<>>, Acc) ->
+ list_to_tuple(Acc);
+get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little,
+ MoreRoots/binary>>, Acc) ->
+ get_liveroots(MoreRoots, [Root | Acc]).
+
+%% @doc Extracts a bunch of integers (safepoint offsets) from a binary. Returns
+%% a tuple as need for stack descriptors.
+get_spoffs(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_spoffs(<<SPOff:?bits(?SP_ADDR_SIZE)/integer-little, More/binary>>, Acc) ->
+ get_spoffs(More, [SPOff | Acc]).
+
+combine_ras_and_exns(_, [], Acc) ->
+ lists:reverse(Acc);
+combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) ->
+ %% FIXME: do something better than O(n^2) by taking advantage of the property
+ %% ||ExnHandlers|| <= ||RAs||
+ Handler = find_exn_handler(RA, ExnHandlers),
+ combine_ras_and_exns(ExnHandlers, MoreRAs, [{Handler, RA} | Acc]).
+
+find_exn_handler(_, []) ->
+ [];
+find_exn_handler(RA, [{Start, End, Handler} | MoreExnHandlers]) ->
+ case (RA >= Start andalso RA =< End) of
+ true ->
+ Handler;
+ false ->
+ find_exn_handler(RA, MoreExnHandlers)
+ end.
+
+create_sdesc_list([], _, _, _, Acc) ->
+ lists:reverse(Acc);
+create_sdesc_list([{ExnLbl, SPOff} | MoreExnAndSPOffs],
+ StkFrameSize, StkArity, LiveRoots, Acc) ->
+ Hdlr = case ExnLbl of
+ 0 -> [];
+ N -> N
+ end,
+ create_sdesc_list(MoreExnAndSPOffs, StkFrameSize, StkArity, LiveRoots,
+ [{?SDESC, SPOff, {Hdlr, StkFrameSize, StkArity, LiveRoots}}
+ | Acc]).
+
+%% @doc This function is responsible for correcting the stack descriptors of
+%% the calls that are found in the code and have more than NR_ARG_REGS
+%% (thus, some of their arguments are passed to the stack). Because of the
+%% Reserved Call Frame feature that the LLVM uses, the stack descriptors
+%% are not correct since at the point of call the frame size is reduced
+%% proportionally to the number of arguments that are passed on the stack.
+%% Also the offsets of the roots need to be re-adjusted.
+fix_stack_descriptors(_, _, [], _) ->
+ [];
+fix_stack_descriptors(RelocsDict, Relocs, SDescs, ExposedClosures) ->
+ %% NamedCalls are MFA and BIF calls that need fix
+ NamedCalls = calls_with_stack_args(RelocsDict),
+ NamedCallsOffs = calls_offsets_arity(Relocs, NamedCalls),
+ ExposedClosures1 =
+ case dict:is_key("table_closures", RelocsDict) of
+ true -> %% A Table with closures exists
+ {table_closures, ArityList} = dict:fetch("table_closures", RelocsDict),
+ case ExposedClosures of
+ {_, Offsets} ->
+ lists:zip(Offsets, ArityList);
+ _ ->
+ exit({?MODULE, fix_stack_descriptors,
+ {"Wrong exposed closures", ExposedClosures}})
+ end;
+ false ->
+ []
+ end,
+ ClosuresOffs = closures_offsets_arity(ExposedClosures1, SDescs),
+ fix_sdescs(NamedCallsOffs ++ ClosuresOffs, SDescs).
+
+%% @doc This function takes as argument the relocation dictionary as produced by
+%% the translation of RTL code to LLVM and finds the names of the calls
+%% (MFA and BIF calls) that have more than NR_ARG_REGS.
+calls_with_stack_args(Dict) ->
+ calls_with_stack_args(dict:to_list(Dict), []).
+
+calls_with_stack_args([], Calls) -> Calls;
+calls_with_stack_args([ {_Name, {call, {M, F, A}}} | Rest], Calls)
+ when A > ?NR_ARG_REGS ->
+ Call =
+ case M of
+ bif -> {F,A};
+ _ -> {M,F,A}
+ end,
+ calls_with_stack_args(Rest, [Call|Calls]);
+calls_with_stack_args([_|Rest], Calls) ->
+ calls_with_stack_args(Rest, Calls).
+
+%% @doc This function extracts the stack arity and the offset in the code of
+%% the named calls (MFAs, BIFs) that have stack arguments.
+calls_offsets_arity(AccRefs, CallsWithStackArgs) ->
+ calls_offsets_arity(AccRefs, CallsWithStackArgs, []).
+
+calls_offsets_arity([], _, Acc) -> Acc;
+calls_offsets_arity([{Type, Offset, Term} | Rest], CallsWithStackArgs, Acc)
+ when Type =:= ?CALL_REMOTE orelse Type =:= ?CALL_LOCAL ->
+ case lists:member(Term, CallsWithStackArgs) of
+ true ->
+ Arity =
+ case Term of
+ {_M, _F, A} -> A;
+ {_F, A} -> A
+ end,
+ calls_offsets_arity(Rest, CallsWithStackArgs,
+ [{Offset + 4, Arity - ?NR_ARG_REGS} | Acc]);
+ false ->
+ calls_offsets_arity(Rest, CallsWithStackArgs, Acc)
+ end;
+calls_offsets_arity([_|Rest], CallsWithStackArgs, Acc) ->
+ calls_offsets_arity(Rest, CallsWithStackArgs, Acc).
+
+%% @doc This function extracts the stack arity and the offsets of closures that
+%% have stack arity. The Closures argument represents the
+%% hipe_bifs:llvm_exposure_closure/0 calls in the code. The actual closure
+%% is the next call in the code, so the offset of the next call must be
+%% calculated from the stack descriptors.
+closures_offsets_arity([], _) ->
+ [];
+closures_offsets_arity(ExposedClosures, SDescs) ->
+ Offsets = [Offset || {_, Offset, _} <- SDescs],
+ %% Offsets and closures must be sorted in order for find_offsets/3 to work
+ SortedOffsets = lists:sort(Offsets),
+ SortedExposedClosures = lists:keysort(1, ExposedClosures),
+ find_offsets(SortedExposedClosures, SortedOffsets, []).
+
+find_offsets([], _, Acc) -> Acc;
+find_offsets([{Off,Arity}|Rest], Offsets, Acc) ->
+ [I | RestOffsets] = lists:dropwhile(fun (Y) -> Y<Off end, Offsets),
+ find_offsets(Rest, RestOffsets, [{I, Arity}|Acc]).
+
+%% The functions below correct the arity of calls, that are identified
+%% by offset, in the stack descriptors.
+fix_sdescs([], SDescs) -> SDescs;
+fix_sdescs([{Offset, Arity} | Rest], SDescs) ->
+ case lists:keyfind(Offset, 2, SDescs) of
+ false ->
+ fix_sdescs(Rest, SDescs);
+ {?SDESC, Offset, SDesc} ->
+ {ExnHandler, FrameSize, StkArity, Roots} = SDesc,
+ DecRoot = fun(X) -> X-Arity end,
+ NewRootsList = lists:map(DecRoot, tuple_to_list(Roots)),
+ NewSDesc =
+ case length(NewRootsList) > 0 andalso hd(NewRootsList) >= 0 of
+ true ->
+ {?SDESC, Offset, {ExnHandler, FrameSize-Arity, StkArity,
+ list_to_tuple(NewRootsList)}};
+ false ->
+ {?SDESC, Offset, {ExnHandler, FrameSize, StkArity, Roots}}
+ end,
+ RestSDescs = lists:keydelete(Offset, 2, SDescs),
+ fix_sdescs(Rest, [NewSDesc | RestSDescs])
+ end.
+
+
+%%------------------------------------------------------------------------------
+%% Miscellaneous functions
+%%------------------------------------------------------------------------------
+
+%% @doc A function that opens a file as binary. The function takes as argument
+%% the name of the file and returns an Erlang binary.
+-spec open_object_file(string()) -> binary().
+open_object_file(ObjFile) ->
+ case file:read_file(ObjFile) of
+ {ok, Binary} ->
+ Binary;
+ {error, Reason} ->
+ exit({?MODULE, open_file, Reason})
+ end.
+
+remove_temp_folder(Dir, Options) ->
+ case proplists:get_bool(llvm_save_temps, Options) of
+ true -> ok;
+ false -> spawn(fun () -> "" = os:cmd("rm -rf " ++ Dir) end), ok
+ end.
+
+unique_id(FunName, Arity) ->
+ integer_to_list(erlang:phash2({FunName, Arity, now()})).
+
+unique_folder(FunName, Arity, Options) ->
+ DirName = "llvm_" ++ unique_id(FunName, Arity) ++ "/",
+ Dir =
+ case proplists:get_bool(llvm_save_temps, Options) of
+ true -> %% Store folder in current directory
+ DirName;
+ false -> %% Temporarily store folder in tempfs (/dev/shm/)
+ "/dev/shm/" ++ DirName
+ end,
+ %% Make sure it does not exist
+ case dir_exists(Dir) of
+ true -> %% Dir already exists! Generate again.
+ unique_folder(FunName, Arity, Options);
+ false ->
+ Dir
+ end.
+
+%% @doc Function that checks that a given Filename is an existing Directory
+%% Name (from http://rosettacode.org/wiki/Ensure_that_a_file_exists#Erlang)
+dir_exists(Filename) ->
+ {Flag, Info} = file:read_file_info(Filename),
+ (Flag =:= ok) andalso (element(3, Info) =:= directory).
+
+%% @doc Function that takes as arguments a list of integers and a list with
+%% numbers indicating how many items should each tuple have and splits
+%% the original list to a list of lists of integers (with the specified
+%% number of elements), i.e. [ [...], [...] ].
+-spec split_list([integer()], [integer()]) -> [ [integer()] ].
+split_list(List, ElemsPerTuple) ->
+ split_list(List, ElemsPerTuple, []).
+
+-spec split_list([integer()], [integer()], [ [integer()] ]) -> [ [integer()] ].
+split_list([], [], Acc) ->
+ lists:reverse(Acc);
+split_list(List, [NumOfElems | MoreNums], Acc) ->
+ {L1, L2} = lists:split(NumOfElems, List),
+ split_list(L2, MoreNums, [ L1 | Acc]).
diff --git a/lib/hipe/llvm/hipe_llvm_merge.erl b/lib/hipe/llvm/hipe_llvm_merge.erl
new file mode 100644
index 0000000000..3ababfc21a
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_merge.erl
@@ -0,0 +1,114 @@
+%%% -*- erlang-indent-level: 2 -*-
+-module(hipe_llvm_merge).
+
+-export([finalize/3]).
+
+-include("hipe_llvm_arch.hrl").
+-include("../../kernel/src/hipe_ext_format.hrl").
+-include("../rtl/hipe_literals.hrl").
+-include("../main/hipe.hrl").
+
+finalize(CompiledCode, Closures, Exports) ->
+ CompiledCode1 = [CodePack || {_, CodePack} <- CompiledCode],
+ Code = [{MFA, [], ConstTab}
+ || {MFA, _, _ , ConstTab, _, _} <- CompiledCode1],
+ {ConstAlign, ConstSize, ConstMap, RefsFromConsts} =
+ hipe_pack_constants:pack_constants(Code, ?ARCH_REGISTERS:alignment()),
+ %% Compute total code size separately as a sanity check for alignment
+ CodeSize = compute_code_size(CompiledCode1, 0),
+ %% io:format("Code Size (pre-computed): ~w~n", [CodeSize]),
+ {CodeBinary, ExportMap} = merge_mfas(CompiledCode1, 0, <<>>, []),
+ %% io:format("Code Size (post-computed): ~w~n", [byte_size(CodeBinary)]),
+ ?VERBOSE_ASSERT(CodeSize =:= byte_size(CodeBinary)),
+ AccRefs = merge_refs(CompiledCode1, ConstMap, 0, []),
+ %% Bring CompiledCode to a combine_label_maps-acceptable form.
+ LabelMap = combine_label_maps(CompiledCode1, 0, gb_trees:empty()),
+ SC = hipe_pack_constants:slim_constmap(ConstMap),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap, Closures, Exports),
+ SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
+ term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
+ ConstAlign, ConstSize,
+ SC, % ConstMap
+ DataRelocs, % LabelMap
+ SSE, % ExportMap
+ CodeSize, CodeBinary, SlimRefs,
+ 0,[] % ColdCodeSize, SlimColdRefs
+ ]).
+
+%% Copied from hipe_x86_assemble.erl
+nr_pad_bytes(Address) ->
+ (4 - (Address rem 4)) rem 4. % XXX: 16 or 32 instead?
+
+align_entry(Address) ->
+ Address + nr_pad_bytes(Address).
+
+compute_code_size([{_MFA, _BinaryCode, CodeSize, _, _, _}|Code], Size) ->
+ compute_code_size(Code, align_entry(Size+CodeSize));
+compute_code_size([], Size) -> Size.
+
+combine_label_maps([{MFA, _, CodeSize, _, _, LabelMap}|Code], Address, CLM) ->
+ NewCLM = merge_label_map(gb_trees:to_list(LabelMap), MFA, Address, CLM),
+ combine_label_maps(Code, align_entry(Address+CodeSize), NewCLM);
+combine_label_maps([], _Address, CLM) -> CLM.
+
+merge_label_map([{Label,Offset}|Rest], MFA, Address, CLM) ->
+ NewCLM = gb_trees:insert({MFA,Label}, Address+Offset, CLM),
+ merge_label_map(Rest, MFA, Address, NewCLM);
+merge_label_map([], _MFA, _Address, CLM) -> CLM.
+
+%% @doc Merge the MFAs' binary code to one continuous binary and compute the
+%% size of this binary. At the same time create an exportmap in a form
+%% of {Address, M, F, A}.
+%% XXX: Is alignment correct/optimal for X86/AMD64?
+merge_mfas([{{M,F,A}, CodeBinary, CodeSize, _, _, _}|Code],
+ Address, AccCode, AccExportMap) ->
+ ?VERBOSE_ASSERT(CodeSize =:= byte_size(CodeBinary)),
+ {Address1, Code1} =
+ case nr_pad_bytes(Address + CodeSize) of
+ 0 -> %% Retains alignment:
+ {Address + CodeSize, CodeBinary};
+ NrPadBytes -> %% Needs padding!
+ Padding = list_to_binary(lists:duplicate(NrPadBytes, 0)),
+ {Address + CodeSize + NrPadBytes, % =:= align_entry(Address+CodeSize)
+ <<CodeBinary/binary, Padding/binary>>}
+ end,
+ ?VERBOSE_ASSERT(Address1 =:=
+ align_entry(Address + CodeSize)), %XXX: Should address be aligned?
+ AccCode1 = <<AccCode/binary, Code1/binary>>,
+ merge_mfas(Code, Address1, AccCode1, [{Address, M, F, A}|AccExportMap]);
+merge_mfas([], _Address, AccCode, AccExportMap) ->
+ {AccCode, AccExportMap}.
+
+%% @doc Merge the references of relocatable symbols in the binary code. The
+%% offsets must be updated because of the merging of the code binaries!
+merge_refs([], _ConstMap, _Addr, AccRefs) -> AccRefs;
+merge_refs([{MFA, _, CodeSize, _, Refs, _}|Rest], ConstMap, Address, AccRefs) ->
+ %% Important!: The hipe_pack_constants:pack_constants/2 function assignes
+ %% unique numbers to constants (ConstNo). This numbers are used from now on,
+ %% instead of labels that were used before. So, in order to be compatible, we
+ %% must change all the constant labels in the Refs to the corresponding
+ %% ConstNo, that can be found in the ConstMap (#pcm_entry{}).
+ UpdatedRefs = [update_ref(label_to_constno(Ref, MFA, ConstMap), Address)
+ || Ref <- Refs],
+ merge_refs(Rest, ConstMap, align_entry(Address+CodeSize),
+ UpdatedRefs++AccRefs).
+
+label_to_constno({Type, Offset, {constant, Label}}, MFA, ConstMap) ->
+ ConstNo = hipe_pack_constants:find_const({MFA, Label}, ConstMap),
+ {Type, Offset, {constant, ConstNo}};
+label_to_constno(Other, _MFA, _ConstMap) ->
+ Other.
+
+%% @doc Update offset to a reference. In case of stack descriptors we must check
+%% if there exists an exception handler, because it must also be updated.
+update_ref({?SDESC, Offset, SDesc}, CodeAddr) ->
+ NewRefAddr = Offset+CodeAddr,
+ case SDesc of
+ {[], _, _, _} -> % No handler; only update offset
+ {?SDESC, NewRefAddr, SDesc};
+ {ExnHandler, FrameSize, StackArity, Roots} -> % Update exception handler
+ {?SDESC, NewRefAddr, {ExnHandler+CodeAddr, FrameSize, StackArity, Roots}}
+ end;
+update_ref({Type, Offset, Term}, CodeAddr) ->
+ {Type, Offset+CodeAddr, Term}.
diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
new file mode 100644
index 0000000000..ba76e1d815
--- /dev/null
+++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
@@ -0,0 +1,1612 @@
+%% -*- erlang-indent-level: 2 -*-
+
+-module(hipe_rtl_to_llvm).
+-author("Chris Stavrakakis, Yiannis Tsiouris").
+
+-export([translate/2]). % the main function of this module
+-export([fix_mfa_name/1]). % a help function used in hipe_llvm_main
+
+-include("../rtl/hipe_rtl.hrl").
+-include("../rtl/hipe_literals.hrl").
+-include("hipe_llvm_arch.hrl").
+
+-define(WORD_WIDTH, (?bytes_to_bits(hipe_rtl_arch:word_size()))).
+-define(BRANCH_META_TAKEN, "0").
+-define(BRANCH_META_NOT_TAKEN, "1").
+
+%%------------------------------------------------------------------------------
+%% @doc Main function for translating an RTL function to LLVM Assembly. Takes as
+%% input the RTL code and the variable indexes of possible garbage
+%% collection roots and returns the corresponing LLVM, a dictionary with
+%% all the relocations in the code and a hipe_consttab() with informaton
+%% about data.
+%%------------------------------------------------------------------------------
+translate(RTL, Roots) ->
+ Fun = hipe_rtl:rtl_fun(RTL),
+ Params = hipe_rtl:rtl_params(RTL),
+ Data = hipe_rtl:rtl_data(RTL),
+ Code = hipe_rtl:rtl_code(RTL),
+ %% Init unique symbol generator and initialize the label counter to the last
+ %% RTL label.
+ hipe_gensym:init(llvm),
+ {_, MaxLabel} = hipe_rtl:rtl_label_range(RTL),
+ put({llvm,label_count}, MaxLabel + 1),
+ %% Put first label of RTL code in process dictionary
+ find_code_entry_label(Code),
+ %% Initialize relocations symbol dictionary
+ Relocs = dict:new(),
+ %% Print RTL to file
+ %% {ok, File_rtl} = file:open("rtl_" ++integer_to_list(random:uniform(2000))
+ %% ++ ".rtl", [write]),
+ %% hipe_rtl:pp(File_rtl, RTL),
+ %% file:close(File_rtl),
+
+ %% Pass on RTL code to handle exception handling and identify labels of Fail
+ %% Blocks
+ {Code1, FailLabels} = fix_code(Code),
+ %% Allocate stack slots for each virtual register and declare gc roots
+ AllocaStackCode = alloca_stack(Code1, Params, Roots),
+ %% Translate Code
+ {LLVM_Code1, Relocs1, NewData} =
+ translate_instr_list(Code1, [], Relocs, Data),
+ %% Create LLVM code to declare relocation symbols as external symbols along
+ %% with local variables in order to use them as just any other variable
+ {FinalRelocs, ExternalDecl, LocalVars} =
+ handle_relocations(Relocs1, Data, Fun),
+ %% Pass on LLVM code in order to create Fail blocks and a landingpad
+ %% instruction to each one
+ LLVM_Code2 = add_landingpads(LLVM_Code1, FailLabels),
+ %% Create LLVM Code for the compiled function
+ LLVM_Code3 = create_function_definition(Fun, Params, LLVM_Code2,
+ AllocaStackCode ++ LocalVars),
+ %% Final Code = CompiledFunction + External Declarations
+ FinalLLVMCode = [LLVM_Code3 | ExternalDecl],
+ {FinalLLVMCode, FinalRelocs, NewData}.
+
+find_code_entry_label([]) ->
+ exit({?MODULE, find_code_entry_label, "Empty code"});
+find_code_entry_label([I|_]) ->
+ case hipe_rtl:is_label(I) of
+ true ->
+ put(first_label, hipe_rtl:label_name(I));
+ false ->
+ exit({?MODULE, find_code_entry_label, "First instruction is not a label"})
+ end.
+
+%% @doc Create a stack slot for each virtual register. The stack slots
+%% that correspond to possible garbage collection roots must be
+%% marked as such.
+alloca_stack(Code, Params, Roots) ->
+ %% Find all assigned virtual registers
+ Destinations = collect_destinations(Code),
+ %% Declare virtual registers, and declare garbage collection roots
+ do_alloca_stack(Destinations++Params, Params, Roots).
+
+collect_destinations(Code) ->
+ lists:usort(lists:flatmap(fun insn_dst/1, Code)).
+
+do_alloca_stack(Destinations, Params, Roots) ->
+ do_alloca_stack(Destinations, Params, Roots, []).
+
+do_alloca_stack([], _, _, Acc) ->
+ Acc;
+do_alloca_stack([D|Ds], Params, Roots, Acc) ->
+ {Name, _I} = trans_dst(D),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ case hipe_rtl:is_var(D) of
+ true ->
+ Num = hipe_rtl:var_index(D),
+ I1 = hipe_llvm:mk_alloca(Name, WordTy, [], []),
+ case lists:member(Num, Roots) of
+ true -> %% Variable is a possible Root
+ T1 = mk_temp(),
+ BYTE_TYPE_PP = hipe_llvm:mk_pointer(ByteTyPtr),
+ I2 =
+ hipe_llvm:mk_conversion(T1, bitcast, WordTyPtr, Name, BYTE_TYPE_PP),
+ GcRootArgs = [{BYTE_TYPE_PP, T1}, {ByteTyPtr, "@gc_metadata"}],
+ I3 = hipe_llvm:mk_call([], false, [], [], hipe_llvm:mk_void(),
+ "@llvm.gcroot", GcRootArgs, []),
+ I4 = case lists:member(D, Params) of
+ false ->
+ hipe_llvm:mk_store(WordTy, "-5", WordTyPtr, Name,
+ [], [], false);
+ true -> []
+ end,
+ do_alloca_stack(Ds, Params, Roots, [I1, I2, I3, I4 | Acc]);
+ false ->
+ do_alloca_stack(Ds, Params, Roots, [I1|Acc])
+ end;
+ false ->
+ case hipe_rtl:is_reg(D) andalso isPrecoloured(D) of
+ true -> %% Precoloured registers are mapped to "special" stack slots
+ do_alloca_stack(Ds, Params, Roots, Acc);
+ false ->
+ I1 = case hipe_rtl:is_fpreg(D) of
+ true ->
+ FloatTy = hipe_llvm:mk_double(),
+ hipe_llvm:mk_alloca(Name, FloatTy, [], []);
+ false -> hipe_llvm:mk_alloca(Name, WordTy, [], [])
+ end,
+ do_alloca_stack(Ds, Params, Roots, [I1|Acc])
+ end
+ end.
+
+%%------------------------------------------------------------------------------
+%% @doc Translation of the linearized RTL Code. Each RTL instruction is
+%% translated to a list of LLVM Assembly instructions. The relocation
+%% dictionary is updated when needed.
+%%------------------------------------------------------------------------------
+translate_instr_list([], Acc, Relocs, Data) ->
+ {lists:reverse(lists:flatten(Acc)), Relocs, Data};
+translate_instr_list([I | Is], Acc, Relocs, Data) ->
+ {Acc1, NewRelocs, NewData} = translate_instr(I, Relocs, Data),
+ translate_instr_list(Is, [Acc1 | Acc], NewRelocs, NewData).
+
+translate_instr(I, Relocs, Data) ->
+ case I of
+ #alu{} ->
+ {I2, Relocs2} = trans_alu(I, Relocs),
+ {I2, Relocs2, Data};
+ #alub{} ->
+ {I2, Relocs2} = trans_alub(I, Relocs),
+ {I2, Relocs2, Data};
+ #branch{} ->
+ {I2, Relocs2} = trans_branch(I, Relocs),
+ {I2, Relocs2, Data};
+ #call{} ->
+ {I2, Relocs2} =
+ case hipe_rtl:call_fun(I) of
+ %% In AMD64 this instruction does nothing!
+ %% TODO: chech use of fwait in other architectures!
+ fwait ->
+ {[], Relocs};
+ _ ->
+ trans_call(I, Relocs)
+ end,
+ {I2, Relocs2, Data};
+ #comment{} ->
+ {I2, Relocs2} = trans_comment(I, Relocs),
+ {I2, Relocs2, Data};
+ #enter{} ->
+ {I2, Relocs2} = trans_enter(I, Relocs),
+ {I2, Relocs2, Data};
+ #fconv{} ->
+ {I2, Relocs2} = trans_fconv(I, Relocs),
+ {I2, Relocs2, Data};
+ #fload{} ->
+ {I2, Relocs2} = trans_fload(I, Relocs),
+ {I2, Relocs2, Data};
+ #fmove{} ->
+ {I2, Relocs2} = trans_fmove(I, Relocs),
+ {I2, Relocs2, Data};
+ #fp{} ->
+ {I2, Relocs2} = trans_fp(I, Relocs),
+ {I2, Relocs2, Data};
+ #fp_unop{} ->
+ {I2, Relocs2} = trans_fp_unop(I, Relocs),
+ {I2, Relocs2, Data};
+ #fstore{} ->
+ {I2, Relocs2} = trans_fstore(I, Relocs),
+ {I2, Relocs2, Data};
+ #goto{} ->
+ {I2, Relocs2} = trans_goto(I, Relocs),
+ {I2, Relocs2, Data};
+ #label{} ->
+ {I2, Relocs2} = trans_label(I, Relocs),
+ {I2, Relocs2, Data};
+ #load{} ->
+ {I2, Relocs2} = trans_load(I, Relocs),
+ {I2, Relocs2, Data};
+ #load_address{} ->
+ {I2, Relocs2} = trans_load_address(I, Relocs),
+ {I2, Relocs2, Data};
+ #load_atom{} ->
+ {I2, Relocs2} = trans_load_atom(I, Relocs),
+ {I2, Relocs2, Data};
+ #move{} ->
+ {I2, Relocs2} = trans_move(I, Relocs),
+ {I2, Relocs2, Data};
+ #return{} ->
+ {I2, Relocs2} = trans_return(I, Relocs),
+ {I2, Relocs2, Data};
+ #store{} ->
+ {I2, Relocs2} = trans_store(I, Relocs),
+ {I2, Relocs2, Data};
+ #switch{} -> %% Only switch instruction updates Data
+ {I2, Relocs2, NewData} = trans_switch(I, Relocs, Data),
+ {I2, Relocs2, NewData};
+ Other ->
+ exit({?MODULE, translate_instr, {"Unknown RTL instruction", Other}})
+ end.
+
+%%
+%% alu
+%%
+trans_alu(I, Relocs) ->
+ RtlDst = hipe_rtl:alu_dst(I),
+ TmpDst = mk_temp(),
+ {Src1, I1} = trans_src(hipe_rtl:alu_src1(I)),
+ {Src2, I2} = trans_src(hipe_rtl:alu_src2(I)),
+ Op = trans_op(hipe_rtl:alu_op(I)),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I3 = hipe_llvm:mk_operation(TmpDst, Op, WordTy, Src1, Src2, []),
+ I4 = store_stack_dst(TmpDst, RtlDst),
+ {[I4, I3, I2, I1], Relocs}.
+
+%%
+%% alub
+%%
+trans_alub(I, Relocs) ->
+ case hipe_rtl:alub_cond(I) of
+ Op when Op =:= overflow orelse Op =:= not_overflow ->
+ trans_alub_overflow(I, signed, Relocs);
+ ltu -> %% ltu means unsigned overflow
+ trans_alub_overflow(I, unsigned, Relocs);
+ _ ->
+ trans_alub_no_overflow(I, Relocs)
+ end.
+
+trans_alub_overflow(I, Sign, Relocs) ->
+ {Src1, I1} = trans_src(hipe_rtl:alub_src1(I)),
+ {Src2, I2} = trans_src(hipe_rtl:alub_src2(I)),
+ RtlDst = hipe_rtl:alub_dst(I),
+ TmpDst = mk_temp(),
+ Name = trans_alub_op(I, Sign),
+ NewRelocs = relocs_store(Name, {call, {llvm, Name, 2}}, Relocs),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ ReturnType = hipe_llvm:mk_struct([WordTy, hipe_llvm:mk_int(1)]),
+ T1 = mk_temp(),
+ I3 = hipe_llvm:mk_call(T1, false, [], [], ReturnType, "@" ++ Name,
+ [{WordTy, Src1}, {WordTy, Src2}], []),
+ %% T1{0}: result of the operation
+ I4 = hipe_llvm:mk_extractvalue(TmpDst, ReturnType, T1 , "0", []),
+ I5 = store_stack_dst(TmpDst, RtlDst),
+ T2 = mk_temp(),
+ %% T1{1}: Boolean variable indicating overflow
+ I6 = hipe_llvm:mk_extractvalue(T2, ReturnType, T1, "1", []),
+ case hipe_rtl:alub_cond(I) of
+ Op when Op =:= overflow orelse Op =:= ltu ->
+ True_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
+ False_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
+ MetaData = branch_metadata(hipe_rtl:alub_pred(I));
+ not_overflow ->
+ True_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
+ False_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
+ MetaData = branch_metadata(1 - hipe_rtl:alub_pred(I))
+ end,
+ I7 = hipe_llvm:mk_br_cond(T2, True_label, False_label, MetaData),
+ {[I7, I6, I5, I4, I3, I2, I1], NewRelocs}.
+
+trans_alub_op(I, Sign) ->
+ Name =
+ case Sign of
+ signed ->
+ case hipe_rtl:alub_op(I) of
+ add -> "llvm.sadd.with.overflow.";
+ mul -> "llvm.smul.with.overflow.";
+ sub -> "llvm.ssub.with.overflow.";
+ Op -> exit({?MODULE, trans_alub_op, {"Unknown alub operator", Op}})
+ end;
+ unsigned ->
+ case hipe_rtl:alub_op(I) of
+ add -> "llvm.uadd.with.overflow.";
+ mul -> "llvm.umul.with.overflow.";
+ sub -> "llvm.usub.with.overflow.";
+ Op -> exit({?MODULE, trans_alub_op, {"Unknown alub operator", Op}})
+ end
+ end,
+ Type =
+ case hipe_rtl_arch:word_size() of
+ 4 -> "i32";
+ 8 -> "i64"
+ %% Other -> exit({?MODULE, trans_alub_op, {"Unknown type", Other}})
+ end,
+ Name ++ Type.
+
+trans_alub_no_overflow(I, Relocs) ->
+ %% alu
+ T = hipe_rtl:mk_alu(hipe_rtl:alub_dst(I), hipe_rtl:alub_src1(I),
+ hipe_rtl:alub_op(I), hipe_rtl:alub_src2(I)),
+ %% A trans_alu instruction cannot change relocations
+ {I1, _} = trans_alu(T, Relocs),
+ %% icmp
+ %% Translate destination as src, to match with the semantics of instruction
+ {Dst, I2} = trans_src(hipe_rtl:alub_dst(I)),
+ Cond = trans_rel_op(hipe_rtl:alub_cond(I)),
+ T3 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I5 = hipe_llvm:mk_icmp(T3, Cond, WordTy, Dst, "0"),
+ %% br
+ Metadata = branch_metadata(hipe_rtl:alub_pred(I)),
+ True_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
+ False_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
+ I6 = hipe_llvm:mk_br_cond(T3, True_label, False_label, Metadata),
+ {[I6, I5, I2, I1], Relocs}.
+
+%%
+%% branch
+%%
+trans_branch(I, Relocs) ->
+ {Src1, I1} = trans_src(hipe_rtl:branch_src1(I)),
+ {Src2, I2} = trans_src(hipe_rtl:branch_src2(I)),
+ Cond = trans_rel_op(hipe_rtl:branch_cond(I)),
+ %% icmp
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I3 = hipe_llvm:mk_icmp(T1, Cond, WordTy, Src1, Src2),
+ %% br
+ True_label = mk_jump_label(hipe_rtl:branch_true_label(I)),
+ False_label = mk_jump_label(hipe_rtl:branch_false_label(I)),
+ Metadata = branch_metadata(hipe_rtl:branch_pred(I)),
+ I4 = hipe_llvm:mk_br_cond(T1, True_label, False_label, Metadata),
+ {[I4, I3, I2, I1], Relocs}.
+
+branch_metadata(X) when X =:= 0.5 -> [];
+branch_metadata(X) when X > 0.5 -> ?BRANCH_META_TAKEN;
+branch_metadata(X) when X < 0.5 -> ?BRANCH_META_NOT_TAKEN.
+
+%%
+%% call
+%%
+trans_call(I, Relocs) ->
+ RtlCallArgList= hipe_rtl:call_arglist(I),
+ RtlCallName = hipe_rtl:call_fun(I),
+ {I0, Relocs1} = expose_closure(RtlCallName, RtlCallArgList, Relocs),
+ TmpDst = mk_temp(),
+ {CallArgs, I1} = trans_call_args(RtlCallArgList),
+ FixedRegs = fixed_registers(),
+ {LoadedFixedRegs, I2} = load_fixed_regs(FixedRegs),
+ FinalArgs = fix_reg_args(LoadedFixedRegs) ++ CallArgs,
+ {Name, I3, Relocs2} =
+ trans_call_name(RtlCallName, Relocs1, CallArgs, FinalArgs),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ I4 =
+ case hipe_rtl:call_fail(I) of
+ %% Normal Call
+ [] ->
+ hipe_llvm:mk_call(T1, false, "cc 11", [], FunRetTy, Name, FinalArgs,
+ []);
+ %% Call With Exception
+ FailLabelNum ->
+ TrueLabel = "L" ++ integer_to_list(hipe_rtl:call_normal(I)),
+ FailLabel = "%FL" ++ integer_to_list(FailLabelNum),
+ II1 =
+ hipe_llvm:mk_invoke(T1, "cc 11", [], FunRetTy, Name, FinalArgs, [],
+ "%" ++ TrueLabel, FailLabel),
+ II2 = hipe_llvm:mk_label(TrueLabel),
+ [II2, II1]
+ end,
+ I5 = store_fixed_regs(FixedRegs, T1),
+ I6 =
+ case hipe_rtl:call_dstlist(I) of
+ [] -> []; %% No return value
+ [Destination] ->
+ II3 =
+ hipe_llvm:mk_extractvalue(TmpDst, FunRetTy, T1,
+ integer_to_list(?NR_PINNED_REGS), []),
+ II4 = store_stack_dst(TmpDst, Destination),
+ [II4, II3]
+ end,
+ I7 =
+ case hipe_rtl:call_continuation(I) of
+ [] -> []; %% No continuation
+ CC ->
+ {II5, _} = trans_goto(hipe_rtl:mk_goto(CC), Relocs2),
+ II5
+ end,
+ {[I7, I6, I5, I4, I3, I2, I1, I0], Relocs2}.
+
+%% In case of call to a register (closure call) with more than ?NR_ARG_REGS
+%% arguments we must track the offset this call in the code, in order to
+%% to correct the stack descriptor. So, we insert a new Label and add this label
+%% to the "table_closures"
+%% --------------------------------|--------------------------------------------
+%% Old Code | New Code
+%% --------------------------------|--------------------------------------------
+%% | br %ClosureLabel
+%% call %reg(Args) | ClosureLabel:
+%% | call %reg(Args)
+expose_closure(CallName, CallArgs, Relocs) ->
+ CallArgsNr = length(CallArgs),
+ case hipe_rtl:is_reg(CallName) andalso CallArgsNr > ?NR_ARG_REGS of
+ true ->
+ LabelNum = hipe_gensym:new_label(llvm),
+ ClosureLabel = hipe_llvm:mk_label(mk_label(LabelNum)),
+ JumpIns = hipe_llvm:mk_br(mk_jump_label(LabelNum)),
+ Relocs1 =
+ relocs_store({CallName, LabelNum},
+ {closure_label, LabelNum, CallArgsNr - ?NR_ARG_REGS},
+ Relocs),
+ {[ClosureLabel, JumpIns], Relocs1};
+ false ->
+ {[], Relocs}
+ end.
+
+trans_call_name(RtlCallName, Relocs, CallArgs, FinalArgs) ->
+ case RtlCallName of
+ PrimOp when is_atom(PrimOp) ->
+ LlvmName = trans_prim_op(PrimOp),
+ Relocs1 = relocs_store(LlvmName, {call, {bif, PrimOp, length(CallArgs)}},
+ Relocs),
+ {"@" ++ LlvmName, [], Relocs1};
+ {M, F, A} when is_atom(M), is_atom(F), is_integer(A) ->
+ LlvmName = trans_mfa_name({M,F,A}),
+ Relocs1 = relocs_store(LlvmName, {call, {M,F,A}}, Relocs),
+ {"@" ++ LlvmName, [], Relocs1};
+ Reg ->
+ case hipe_rtl:is_reg(Reg) of
+ true ->
+ %% In case of a closure call, the register holding the address
+ %% of the closure must be converted to function type in
+ %% order to make the call
+ TT1 = mk_temp(),
+ {RegName, II1} = trans_src(Reg),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ II2 =
+ hipe_llvm:mk_conversion(TT1, inttoptr, WordTy, RegName, WordTyPtr),
+ TT2 = mk_temp(),
+ ArgsTypeList = lists:duplicate(length(FinalArgs), WordTy),
+ FunRetTy =
+ hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ FunType = hipe_llvm:mk_fun(FunRetTy, ArgsTypeList),
+ FunTypeP = hipe_llvm:mk_pointer(FunType),
+ II3 = hipe_llvm:mk_conversion(TT2, bitcast, WordTyPtr, TT1, FunTypeP),
+ {TT2, [II3, II2, II1], Relocs};
+ false ->
+ exit({?MODULE, trans_call, {"Unimplemented call to", RtlCallName}})
+ end
+ end.
+
+%%
+trans_call_args(ArgList) ->
+ {Args, I} = lists:unzip(trans_args(ArgList)),
+ %% Reverse arguments that are passed to stack to match with the Erlang
+ %% calling convention. (Propably not needed in prim calls.)
+ ReversedArgs =
+ case erlang:length(Args) > ?NR_ARG_REGS of
+ false ->
+ Args;
+ true ->
+ {ArgsInRegs, ArgsInStack} = lists:split(?NR_ARG_REGS, Args),
+ ArgsInRegs ++ lists:reverse(ArgsInStack)
+ end,
+ %% Reverse I, because some of the arguments may go out of scope and
+ %% should be killed(store -5). When two or more arguments are they
+ %% same, then order matters!
+ {ReversedArgs, lists:reverse(I)}.
+
+%%
+%% trans_comment
+%%
+trans_comment(I, Relocs) ->
+ I1 = hipe_llvm:mk_comment(hipe_rtl:comment_text(I)),
+ {I1, Relocs}.
+
+%%
+%% enter
+%%
+trans_enter(I, Relocs) ->
+ {CallArgs, I0} = trans_call_args(hipe_rtl:enter_arglist(I)),
+ FixedRegs = fixed_registers(),
+ {LoadedFixedRegs, I1} = load_fixed_regs(FixedRegs),
+ FinalArgs = fix_reg_args(LoadedFixedRegs) ++ CallArgs,
+ {Name, I2, NewRelocs} =
+ trans_call_name(hipe_rtl:enter_fun(I), Relocs, CallArgs, FinalArgs),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ I3 = hipe_llvm:mk_call(T1, true, "cc 11", [], FunRetTy, Name, FinalArgs, []),
+ I4 = hipe_llvm:mk_ret([{FunRetTy, T1}]),
+ {[I4, I3, I2, I1, I0], NewRelocs}.
+
+%%
+%% fconv
+%%
+trans_fconv(I, Relocs) ->
+ %% XXX: Can a fconv destination be a precoloured reg?
+ RtlDst = hipe_rtl:fconv_dst(I),
+ TmpDst = mk_temp(),
+ {Src, I1} = trans_float_src(hipe_rtl:fconv_src(I)),
+ FloatTy = hipe_llvm:mk_double(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I2 = hipe_llvm:mk_conversion(TmpDst, sitofp, WordTy, Src, FloatTy),
+ I3 = store_float_stack(TmpDst, RtlDst),
+ {[I3, I2, I1], Relocs}.
+
+
+%% TODO: fload, fstore, fmove, and fp are almost the same with load, store, move
+%% and alu. Maybe we should join them.
+
+%%
+%% fload
+%%
+trans_fload(I, Relocs) ->
+ RtlDst = hipe_rtl:fload_dst(I),
+ RtlSrc = hipe_rtl:fload_src(I),
+ _Offset = hipe_rtl:fload_offset(I),
+ TmpDst = mk_temp(),
+ {Src, I1} = trans_float_src(RtlSrc),
+ {Offset, I2} = trans_float_src(_Offset),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FloatTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_double()),
+ I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []),
+ T2 = mk_temp(),
+ I4 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, FloatTyPtr),
+ I5 = hipe_llvm:mk_load(TmpDst, FloatTyPtr, T2, [], [], false),
+ I6 = store_float_stack(TmpDst, RtlDst),
+ {[I6, I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% fmove
+%%
+trans_fmove(I, Relocs) ->
+ RtlDst = hipe_rtl:fmove_dst(I),
+ RtlSrc = hipe_rtl:fmove_src(I),
+ {Src, I1} = trans_float_src(RtlSrc),
+ I2 = store_float_stack(Src, RtlDst),
+ {[I2, I1], Relocs}.
+
+%%
+%% fp
+%%
+trans_fp(I, Relocs) ->
+ %% XXX: Just copied trans_alu...think again..
+ RtlDst = hipe_rtl:fp_dst(I),
+ RtlSrc1 = hipe_rtl:fp_src1(I),
+ RtlSrc2 = hipe_rtl:fp_src2(I),
+ %% Destination cannot be a precoloured register
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ TmpDst = mk_temp(),
+ {Src1, I1} = trans_float_src(RtlSrc1),
+ {Src2, I2} = trans_float_src(RtlSrc2),
+ Op = trans_fp_op(hipe_rtl:fp_op(I)),
+ I3 = hipe_llvm:mk_operation(TmpDst, Op, FloatTy, Src1, Src2, []),
+ I4 = store_float_stack(TmpDst, RtlDst),
+ %% Synchronization for floating point exceptions
+ I5 = hipe_llvm:mk_store(FloatTy, TmpDst, FloatTyPtr, "%exception_sync", [],
+ [], true),
+ T1 = mk_temp(),
+ I6 = hipe_llvm:mk_load(T1, FloatTyPtr, "%exception_sync", [], [], true),
+ {[I6, I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% fp_unop
+%%
+trans_fp_unop(I, Relocs) ->
+ RtlDst = hipe_rtl:fp_unop_dst(I),
+ RtlSrc = hipe_rtl:fp_unop_src(I),
+ %% Destination cannot be a precoloured register
+ TmpDst = mk_temp(),
+ {Src, I1} = trans_float_src(RtlSrc),
+ Op = trans_fp_op(hipe_rtl:fp_unop_op(I)),
+ FloatTy = hipe_llvm:mk_double(),
+ I2 = hipe_llvm:mk_operation(TmpDst, Op, FloatTy, "0.0", Src, []),
+ I3 = store_float_stack(TmpDst, RtlDst),
+ {[I3, I2, I1], Relocs}.
+%% TODO: Fix fp_unop in a way like the following. You must change trans_dest,
+%% in order to call float_to_list in a case of float constant. Maybe the type
+%% check is expensive...
+%% Dst = hipe_rtl:fp_unop_dst(I),
+%% Src = hipe_rtl:fp_unop_src(I),
+%% Op = hipe_rtl:fp_unop_op(I),
+%% Zero = hipe_rtl:mk_imm(0.0),
+%% I1 = hipe_rtl:mk_fp(Dst, Zero, Op, Src),
+%% trans_fp(I, Relocs1).
+
+%%
+%% fstore
+%%
+trans_fstore(I, Relocs) ->
+ Base = hipe_rtl:fstore_base(I),
+ case isPrecoloured(Base) of
+ true ->
+ trans_fstore_reg(I, Relocs);
+ false ->
+ exit({?MODULE, trans_fstore ,{"Not implemented yet", false}})
+ end.
+
+trans_fstore_reg(I, Relocs) ->
+ {Base, I0} = trans_reg(hipe_rtl:fstore_base(I), dst),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, WordTyPtr, Base, [], [], false),
+ {Offset, I2} = trans_src(hipe_rtl:fstore_offset(I)),
+ T2 = mk_temp(),
+ I3 = hipe_llvm:mk_operation(T2, add, WordTy, T1, Offset, []),
+ T3 = mk_temp(),
+ I4 = hipe_llvm:mk_conversion(T3, inttoptr, WordTy, T2, FloatTyPtr),
+ {Value, I5} = trans_src(hipe_rtl:fstore_src(I)),
+ I6 = hipe_llvm:mk_store(FloatTy, Value, FloatTyPtr, T3, [], [], false),
+ {[I6, I5, I4, I3, I2, I1, I0], Relocs}.
+
+%%
+%% goto
+%%
+trans_goto(I, Relocs) ->
+ I1 = hipe_llvm:mk_br(mk_jump_label(hipe_rtl:goto_label(I))),
+ {I1, Relocs}.
+
+%%
+%% label
+%%
+trans_label(I, Relocs) ->
+ Label = mk_label(hipe_rtl:label_name(I)),
+ I1 = hipe_llvm:mk_label(Label),
+ {I1, Relocs}.
+
+%%
+%% load
+%%
+trans_load(I, Relocs) ->
+ RtlDst = hipe_rtl:load_dst(I),
+ TmpDst = mk_temp(),
+ %% XXX: Why translate them independently? ------------------------
+ {Src, I1} = trans_src(hipe_rtl:load_src(I)),
+ {Offset, I2} = trans_src(hipe_rtl:load_offset(I)),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []),
+ %%----------------------------------------------------------------
+ I4 = case hipe_rtl:load_size(I) of
+ word ->
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, WordTyPtr),
+ II2 = hipe_llvm:mk_load(TmpDst, WordTyPtr, T2, [], [], false),
+ [II2, II1];
+ Size ->
+ LoadType = llvm_type_from_size(Size),
+ LoadTypeP = hipe_llvm:mk_pointer(LoadType),
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, LoadTypeP),
+ T3 = mk_temp(),
+ LoadTypePointer = hipe_llvm:mk_pointer(LoadType),
+ II2 = hipe_llvm:mk_load(T3, LoadTypePointer, T2, [], [], false),
+ Conversion =
+ case hipe_rtl:load_sign(I) of
+ signed -> sext;
+ unsigned -> zext
+ end,
+ II3 =
+ hipe_llvm:mk_conversion(TmpDst, Conversion, LoadType, T3, WordTy),
+ [II3, II2, II1]
+ end,
+ I5 = store_stack_dst(TmpDst, RtlDst),
+ {[I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% load_address
+%%
+trans_load_address(I, Relocs) ->
+ RtlDst = hipe_rtl:load_address_dst(I),
+ RtlAddr = hipe_rtl:load_address_addr(I),
+ {Addr, NewRelocs} =
+ case hipe_rtl:load_address_type(I) of
+ constant ->
+ {"%DL" ++ integer_to_list(RtlAddr) ++ "_var", Relocs};
+ closure ->
+ {{_, ClosureName, _}, _, _} = RtlAddr,
+ FixedClosureName = fix_closure_name(ClosureName),
+ Relocs1 = relocs_store(FixedClosureName, {closure, RtlAddr}, Relocs),
+ {"%" ++ FixedClosureName ++ "_var", Relocs1};
+ type ->
+ exit({?MODULE, trans_load_address,
+ {"Type not implemented in load_address", RtlAddr}})
+ end,
+ I1 = store_stack_dst(Addr, RtlDst),
+ {[I1], NewRelocs}.
+
+%%
+%% load_atom
+%%
+trans_load_atom(I, Relocs) ->
+ RtlDst = hipe_rtl:load_atom_dst(I),
+ RtlAtom = hipe_rtl:load_atom_atom(I),
+ AtomName = "atom_" ++ make_llvm_id(atom_to_list(RtlAtom)),
+ AtomVar = "%" ++ AtomName ++ "_var",
+ NewRelocs = relocs_store(AtomName, {atom, RtlAtom}, Relocs),
+ I1 = store_stack_dst(AtomVar, RtlDst),
+ {[I1], NewRelocs}.
+
+%%
+%% move
+%%
+trans_move(I, Relocs) ->
+ RtlDst = hipe_rtl:move_dst(I),
+ RtlSrc = hipe_rtl:move_src(I),
+ {Src, I1} = trans_src(RtlSrc),
+ I2 = store_stack_dst(Src, RtlDst),
+ {[I2, I1], Relocs}.
+
+%%
+%% return
+%%
+trans_return(I, Relocs) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ {VarRet, I1} =
+ case hipe_rtl:return_varlist(I) of
+ [] ->
+ {[], []};
+ [A] ->
+ {Name, II1} = trans_src(A),
+ {[{WordTy, Name}], II1}
+ end,
+ FixedRegs = fixed_registers(),
+ {LoadedFixedRegs, I2} = load_fixed_regs(FixedRegs),
+ FixedRet = [{WordTy, X} || X <- LoadedFixedRegs],
+ Ret = FixedRet ++ VarRet,
+ {RetTypes, _RetNames} = lists:unzip(Ret),
+ Type = hipe_llvm:mk_struct(RetTypes),
+ {RetStruct, I3} = mk_return_struct(Ret, Type),
+ I4 = hipe_llvm:mk_ret([{Type, RetStruct}]),
+ {[I4, I3, I2, I1], Relocs}.
+
+%% @doc Create a structure to hold the return value and the precoloured
+%% registers.
+mk_return_struct(RetValues, Type) ->
+ mk_return_struct(RetValues, Type, [], "undef", 0).
+
+mk_return_struct([], _, Acc, StructName, _) ->
+ {StructName, Acc};
+mk_return_struct([{ElemType, ElemName}|Rest], Type, Acc, StructName, Index) ->
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_insertvalue(T1, Type, StructName, ElemType, ElemName,
+ integer_to_list(Index), []),
+ mk_return_struct(Rest, Type, [I1 | Acc], T1, Index+1).
+
+%%
+%% store
+%%
+trans_store(I, Relocs) ->
+ {Base, I1} = trans_src(hipe_rtl:store_base(I)),
+ {Offset, I2} = trans_src(hipe_rtl:store_offset(I)),
+ {Value, I3} = trans_src(hipe_rtl:store_src(I)),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ I4 = hipe_llvm:mk_operation(T1, add, WordTy, Base, Offset, []),
+ I5 =
+ case hipe_rtl:store_size(I) of
+ word ->
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, WordTyPtr),
+ II2 = hipe_llvm:mk_store(WordTy, Value, WordTyPtr, T2, [], [],
+ false),
+ [II2, II1];
+ Size ->
+ %% XXX: Is always trunc correct ?
+ LoadType = llvm_type_from_size(Size),
+ LoadTypePointer = hipe_llvm:mk_pointer(LoadType),
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, LoadTypePointer),
+ T3 = mk_temp(),
+ II2 = hipe_llvm:mk_conversion(T3, 'trunc', WordTy, Value, LoadType),
+ II3 = hipe_llvm:mk_store(LoadType, T3, LoadTypePointer, T2, [], [], false),
+ [II3, II2, II1]
+ end,
+ {[I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% switch
+%%
+trans_switch(I, Relocs, Data) ->
+ RtlSrc = hipe_rtl:switch_src(I),
+ {Src, I1} = trans_src(RtlSrc),
+ Labels = hipe_rtl:switch_labels(I),
+ JumpLabels = [mk_jump_label(L) || L <- Labels],
+ SortOrder = hipe_rtl:switch_sort_order(I),
+ NrLabels = length(Labels),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr),
+ TableTypeP = hipe_llvm:mk_pointer(TableType),
+ TypedJumpLabels = [{hipe_llvm:mk_label_type(), X} || X <- JumpLabels],
+ T1 = mk_temp(),
+ {Src2, []} = trans_dst(RtlSrc),
+ TableName = "table_" ++ tl(Src2),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I2 = hipe_llvm:mk_getelementptr(T1, TableTypeP, "@"++TableName,
+ [{WordTy, "0"}, {WordTy, Src}], false),
+ T2 = mk_temp(),
+ BYTE_TYPE_PP = hipe_llvm:mk_pointer(ByteTyPtr),
+ I3 = hipe_llvm:mk_load(T2, BYTE_TYPE_PP, T1, [], [], false),
+ I4 = hipe_llvm:mk_indirectbr(ByteTyPtr, T2, TypedJumpLabels),
+ LMap = [{label, L} || L <- Labels],
+ %% Update data with the info for the jump table
+ {NewData, JTabLab} =
+ case hipe_rtl:switch_sort_order(I) of
+ [] ->
+ hipe_consttab:insert_block(Data, word, LMap);
+ SortOrder ->
+ hipe_consttab:insert_sorted_block(Data, word, LMap, SortOrder)
+ end,
+ Relocs2 = relocs_store(TableName, {switch, {TableType, Labels, NrLabels,
+ SortOrder}, JTabLab}, Relocs),
+ {[I4, I3, I2, I1], Relocs2, NewData}.
+
+%% @doc Pass on RTL code in order to fix invoke and closure calls.
+fix_code(Code) ->
+ fix_calls(Code).
+
+%% @doc Fix invoke calls and closure calls with more than ?NR_ARG_REGS
+%% arguments.
+fix_calls(Code) ->
+ fix_calls(Code, [], []).
+
+fix_calls([], Acc, FailLabels) ->
+ {lists:reverse(Acc), FailLabels};
+fix_calls([I | Is], Acc, FailLabels) ->
+ case hipe_rtl:is_call(I) of
+ true ->
+ {NewCall, NewFailLabels} =
+ case hipe_rtl:call_fail(I) of
+ [] ->
+ {I, FailLabels};
+ FailLabel ->
+ fix_invoke_call(I, FailLabel, FailLabels)
+ end,
+ fix_calls(Is, [NewCall|Acc], NewFailLabels);
+ false ->
+ fix_calls(Is, [I|Acc], FailLabels)
+ end.
+
+%% @doc When a call has a fail continuation label it must be extended with a
+%% normal continuation label to go with the LLVM's invoke instruction.
+%% FailLabels is the list of labels of all fail blocks, which are needed to
+%% be declared as landing pads. Furtermore, we must add to fail labels a
+%% call to hipe_bifs:llvm_fix_pinned_regs/0 in order to avoid reloading old
+%% values of pinned registers. This may happen because the result of an
+%% invoke instruction is not available at fail-labels, and, thus, we cannot
+%% get the correct values of pinned registers. Finally, the stack needs to
+%% be re-adjusted when there are stack arguments.
+fix_invoke_call(I, FailLabel, FailLabels) ->
+ NewLabel = hipe_gensym:new_label(llvm),
+ NewCall1 = hipe_rtl:call_normal_update(I, NewLabel),
+ SpAdj = find_sp_adj(hipe_rtl:call_arglist(I)),
+ case lists:keyfind(FailLabel, 1, FailLabels) of
+ %% Same fail label with same Stack Pointer adjustment
+ {FailLabel, NewFailLabel, SpAdj} ->
+ NewCall2 = hipe_rtl:call_fail_update(NewCall1, NewFailLabel),
+ {NewCall2, FailLabels};
+ %% Same fail label but with different Stack Pointer adjustment
+ {_, _, _} ->
+ NewFailLabel = hipe_gensym:new_label(llvm),
+ NewCall2 = hipe_rtl:call_fail_update(NewCall1, NewFailLabel),
+ {NewCall2, [{FailLabel, NewFailLabel, SpAdj} | FailLabels]};
+ %% New Fail label
+ false ->
+ NewFailLabel = hipe_gensym:new_label(llvm),
+ NewCall2 = hipe_rtl:call_fail_update(NewCall1, NewFailLabel),
+ {NewCall2, [{FailLabel, NewFailLabel, SpAdj} | FailLabels]}
+ end.
+
+find_sp_adj(ArgList) ->
+ NrArgs = length(ArgList),
+ case NrArgs > ?NR_ARG_REGS of
+ true ->
+ (NrArgs - ?NR_ARG_REGS) * hipe_rtl_arch:word_size();
+ false ->
+ 0
+ end.
+
+%% @doc Add landingpad instruction in Fail Blocks.
+add_landingpads(LLVM_Code, FailLabels) ->
+ FailLabels2 = [convert_label(T) || T <- FailLabels],
+ add_landingpads(LLVM_Code, FailLabels2, []).
+
+add_landingpads([], _, Acc) ->
+ lists:reverse(Acc);
+add_landingpads([I | Is], FailLabels, Acc) ->
+ case hipe_llvm:is_label(I) of
+ true ->
+ Label = hipe_llvm:label_label(I),
+ Ins = create_fail_blocks(Label, FailLabels),
+ add_landingpads(Is, FailLabels, [I | Ins] ++ Acc);
+ false ->
+ add_landingpads(Is, FailLabels, [I | Acc])
+ end.
+
+convert_label({X,Y,Z}) ->
+ {"L" ++ integer_to_list(X), "FL" ++ integer_to_list(Y), Z}.
+
+%% @doc Create a fail block wich.
+create_fail_blocks(_, []) -> [];
+create_fail_blocks(Label, FailLabels) ->
+ create_fail_blocks(Label, FailLabels, []).
+
+create_fail_blocks(Label, FailLabels, Acc) ->
+ case lists:keytake(Label, 1, FailLabels) of
+ false ->
+ Acc;
+ {value, {Label, FailLabel, SpAdj}, RestFailLabels} ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I1 = hipe_llvm:mk_label(FailLabel),
+ LP = hipe_llvm:mk_landingpad(),
+ I2 =
+ case SpAdj > 0 of
+ true ->
+ StackPointer = ?ARCH_REGISTERS:reg_name(?ARCH_REGISTERS:sp()),
+ hipe_llvm:mk_adj_stack(integer_to_list(SpAdj), StackPointer,
+ WordTy);
+ false -> []
+ end,
+ T1 = mk_temp(),
+ FixedRegs = fixed_registers(),
+ FunRetTy =
+ hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ I3 = hipe_llvm:mk_call(T1, false, "cc 11", [], FunRetTy,
+ "@hipe_bifs.llvm_fix_pinned_regs.0", [], []),
+ I4 = store_fixed_regs(FixedRegs, T1),
+ I5 = hipe_llvm:mk_br("%" ++ Label),
+ Ins = lists:flatten([I5, I4, I3, I2, LP,I1]),
+ create_fail_blocks(Label, RestFailLabels, Ins ++ Acc)
+ end.
+
+%%------------------------------------------------------------------------------
+%% Miscellaneous Functions
+%%------------------------------------------------------------------------------
+
+%% @doc Convert RTL argument list to LLVM argument list.
+trans_args(ArgList) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ MakeArg =
+ fun(A) ->
+ {Name, I1} = trans_src(A),
+ {{WordTy, Name}, I1}
+ end,
+ [MakeArg(A) || A <- ArgList].
+
+%% @doc Convert a list of Precoloured registers to LLVM argument list.
+fix_reg_args(ArgList) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ [{WordTy, A} || A <- ArgList].
+
+%% @doc Load Precoloured registers.
+load_fixed_regs(RegList) ->
+ Names = [mk_temp_reg(R) || R <- RegList],
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ Fun1 =
+ fun (X, Y) ->
+ hipe_llvm:mk_load(X, WordTyPtr, "%" ++ Y ++ "_reg_var", [], [], false)
+ end,
+ Ins = lists:zipwith(Fun1, Names, RegList),
+ {Names, Ins}.
+
+%% @doc Store Precoloured registers.
+store_fixed_regs(RegList, Name) ->
+ Names = [mk_temp_reg(R) || R <- RegList],
+ Indexes = lists:seq(0, erlang:length(RegList) - 1),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ Fun1 =
+ fun(X,Y) ->
+ hipe_llvm:mk_extractvalue(X, FunRetTy, Name, integer_to_list(Y), [])
+ end,
+ I1 = lists:zipwith(Fun1, Names, Indexes),
+ Fun2 =
+ fun (X, Y) ->
+ hipe_llvm:mk_store(WordTy, X, WordTyPtr, "%" ++ Y ++ "_reg_var", [], [],
+ false)
+ end,
+ I2 = lists:zipwith(Fun2, Names, RegList),
+ [I2, I1].
+
+%%------------------------------------------------------------------------------
+%% Translation of Names
+%%------------------------------------------------------------------------------
+
+%% @doc Fix F in MFA tuple to acceptable LLVM identifier (case of closure).
+-spec fix_mfa_name(mfa()) -> mfa().
+fix_mfa_name({Mod_Name, Closure_Name, Arity}) ->
+ Fun_Name = list_to_atom(fix_closure_name(Closure_Name)),
+ {Mod_Name, Fun_Name, Arity}.
+
+%% @doc Make an acceptable LLVM identifier for a closure name.
+fix_closure_name(ClosureName) ->
+ make_llvm_id(atom_to_list(ClosureName)).
+
+%% @doc Create an acceptable LLVM identifier.
+make_llvm_id(Name) ->
+ case Name of
+ "" -> "Empty";
+ Other -> lists:flatten([llvm_id(C) || C <- Other])
+ end.
+
+llvm_id(C) when C=:=46; C>47 andalso C<58; C>64 andalso C<91; C=:=95;
+ C>96 andalso C<123 ->
+ C;
+llvm_id(C) ->
+ io_lib:format("_~2.16.0B_",[C]).
+
+%% @doc Create an acceptable LLVM identifier for an MFA.
+trans_mfa_name({M,F,A}) ->
+ N = atom_to_list(M) ++ "." ++ atom_to_list(F) ++ "." ++ integer_to_list(A),
+ make_llvm_id(N).
+
+%%------------------------------------------------------------------------------
+%% Creation of Labels and Temporaries
+%%------------------------------------------------------------------------------
+mk_label(N) ->
+ "L" ++ integer_to_list(N).
+
+mk_jump_label(N) ->
+ "%L" ++ integer_to_list(N).
+
+mk_temp() ->
+ "%t" ++ integer_to_list(hipe_gensym:new_var(llvm)).
+
+mk_temp_reg(Name) ->
+ "%" ++ Name ++ integer_to_list(hipe_gensym:new_var(llvm)).
+
+%%----------------------------------------------------------------------------
+%% Translation of Operands
+%%----------------------------------------------------------------------------
+
+store_stack_dst(TempDst, Dst) ->
+ {Dst2, II1} = trans_dst(Dst),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ II2 = hipe_llvm:mk_store(WordTy, TempDst, WordTyPtr, Dst2, [], [], false),
+ [II2, II1].
+
+store_float_stack(TempDst, Dst) ->
+ {Dst2, II1} = trans_dst(Dst),
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ II2 = hipe_llvm:mk_store(FloatTy, TempDst, FloatTyPtr, Dst2, [], [], false),
+ [II2, II1].
+
+trans_float_src(Src) ->
+ case hipe_rtl:is_const_label(Src) of
+ true ->
+ Name = "@DL" ++ integer_to_list(hipe_rtl:const_label_label(Src)),
+ T1 = mk_temp(),
+ %% XXX: Hardcoded offset
+ ByteTy = hipe_llvm:mk_int(8),
+ ByteTyPtr = hipe_llvm:mk_pointer(ByteTy),
+ I1 = hipe_llvm:mk_getelementptr(T1, ByteTyPtr, Name,
+ [{ByteTy, integer_to_list(?FLOAT_OFFSET)}], true),
+ T2 = mk_temp(),
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ I2 = hipe_llvm:mk_conversion(T2, bitcast, ByteTyPtr, T1, FloatTyPtr),
+ T3 = mk_temp(),
+ I3 = hipe_llvm:mk_load(T3, FloatTyPtr, T2, [], [], false),
+ {T3, [I3, I2, I1]};
+ false ->
+ trans_src(Src)
+ end.
+
+trans_src(A) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ case hipe_rtl:is_imm(A) of
+ true ->
+ Value = integer_to_list(hipe_rtl:imm_value(A)),
+ {Value, []};
+ false ->
+ case hipe_rtl:is_reg(A) of
+ true ->
+ case isPrecoloured(A) of
+ true -> trans_reg(A, src);
+ false ->
+ {Name, []} = trans_reg(A, src),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, WordTyPtr, Name, [], [], false),
+ {T1, [I1]}
+ end;
+ false ->
+ case hipe_rtl:is_var(A) of
+ true ->
+ RootName = "%vr" ++ integer_to_list(hipe_rtl:var_index(A)),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, WordTyPtr, RootName, [], [], false),
+ I2 =
+ case hipe_rtl:var_liveness(A) of
+ live ->
+ [];
+ dead ->
+ NilValue = hipe_tagscheme:mk_nil(),
+ hipe_llvm:mk_store(WordTy, integer_to_list(NilValue), WordTyPtr, RootName,
+ [], [], false)
+ end,
+ {T1, [I2, I1]};
+ false ->
+ case hipe_rtl:is_fpreg(A) of
+ true ->
+ {Name, []} = trans_dst(A),
+ T1 = mk_temp(),
+ FloatTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_double()),
+ I1 = hipe_llvm:mk_load(T1, FloatTyPtr, Name, [], [], false),
+ {T1, [I1]};
+ false -> trans_dst(A)
+ end
+ end
+ end
+ end.
+
+trans_dst(A) ->
+ case hipe_rtl:is_reg(A) of
+ true ->
+ trans_reg(A, dst);
+ false ->
+ Name = case hipe_rtl:is_var(A) of
+ true ->
+ "%vr" ++ integer_to_list(hipe_rtl:var_index(A));
+ false ->
+ case hipe_rtl:is_fpreg(A) of
+ true -> "%fr" ++ integer_to_list(hipe_rtl:fpreg_index(A));
+ false ->
+ case hipe_rtl:is_const_label(A) of
+ true ->
+ "%DL" ++ integer_to_list(hipe_rtl:const_label_label(A)) ++ "_var";
+ false ->
+ exit({?MODULE, trans_dst, {"Bad RTL argument",A}})
+ end
+ end
+ end,
+ {Name, []}
+ end.
+
+%% @doc Translate a register. If it is precoloured it must be mapped to the
+%% correct stack slot that holds the precoloured register value.
+trans_reg(Arg, Position) ->
+ Index = hipe_rtl:reg_index(Arg),
+ case isPrecoloured(Arg) of
+ true ->
+ Name = map_precoloured_reg(Index),
+ case Position of
+ src -> fix_reg_src(Name);
+ dst -> fix_reg_dst(Name)
+ end;
+ false ->
+ {hipe_rtl_arch:reg_name(Index), []}
+ end.
+
+map_precoloured_reg(Index) ->
+ case hipe_rtl_arch:reg_name(Index) of
+ "%r15" -> "%hp_reg_var";
+ "%rbp" -> "%p_reg_var";
+ "%esi" -> "%hp_reg_var";
+ "%ebp" -> "%p_reg_var";
+ "%fcalls" ->
+ {"%p_reg_var", ?ARCH_REGISTERS:proc_offset(?ARCH_REGISTERS:fcalls())};
+ "%hplim" ->
+ {"%p_reg_var", ?ARCH_REGISTERS:proc_offset(?ARCH_REGISTERS:heap_limit())};
+ _ ->
+ exit({?MODULE, map_precoloured_reg, {"Register not mapped yet", Index}})
+ end.
+
+%% @doc Load precoloured dst register.
+fix_reg_dst(Register) ->
+ case Register of
+ {Name, Offset} -> %% Case of %fcalls, %hplim
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ pointer_from_reg(Name, WordTy, Offset);
+ Name -> %% Case of %p and %hp
+ {Name, []}
+ end.
+
+%% @doc Load precoloured src register.
+fix_reg_src(Register) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ case Register of
+ {Name, Offset} -> %% Case of %fcalls, %hplim
+ {T1, I1} = pointer_from_reg(Name, WordTy, Offset),
+ T2 = mk_temp(),
+ I2 = hipe_llvm:mk_load(T2, WordTyPtr, T1, [], [] , false),
+ {T2, [I2, I1]};
+ Name -> %% Case of %p and %hp
+ T1 = mk_temp(),
+ {T1, hipe_llvm:mk_load(T1, WordTyPtr, Name, [], [], false)}
+ end.
+
+%% @doc Load %fcalls and %hplim.
+pointer_from_reg(RegName, Type, Offset) ->
+ PointerType = hipe_llvm:mk_pointer(Type),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, PointerType, RegName, [], [] ,false),
+ T2 = mk_temp(),
+ I2 = hipe_llvm:mk_conversion(T2, inttoptr, Type, T1, PointerType),
+ T3 = mk_temp(),
+ %% XXX: Offsets should be a power of 2.
+ I3 = hipe_llvm:mk_getelementptr(T3, PointerType, T2,
+ [{Type, integer_to_list(Offset div hipe_rtl_arch:word_size())}], true),
+ {T3, [I3, I2, I1]}.
+
+isPrecoloured(X) ->
+ hipe_rtl_arch:is_precoloured(X).
+
+%%------------------------------------------------------------------------------
+%% Translation of operators
+%%------------------------------------------------------------------------------
+
+trans_op(Op) ->
+ case Op of
+ add -> add;
+ sub -> sub;
+ 'or' -> 'or';
+ 'and' -> 'and';
+ 'xor' -> 'xor';
+ sll -> shl;
+ srl -> lshr;
+ sra -> ashr;
+ mul -> mul;
+ 'fdiv' -> fdiv;
+ 'sdiv' -> sdiv;
+ 'srem' -> srem;
+ Other -> exit({?MODULE, trans_op, {"Unknown RTL operator", Other}})
+ end.
+
+trans_rel_op(Op) ->
+ case Op of
+ eq -> eq;
+ ne -> ne;
+ gtu -> ugt;
+ geu -> uge;
+ ltu -> ult;
+ leu -> ule;
+ gt -> sgt;
+ ge -> sge;
+ lt -> slt;
+ le -> sle
+ end.
+
+trans_prim_op(Op) ->
+ case Op of
+ '+' -> "bif_add";
+ '-' -> "bif_sub";
+ '*' -> "bif_mul";
+ 'div' -> "bif_div";
+ '/' -> "bif_div";
+ Other -> atom_to_list(Other)
+ end.
+
+trans_fp_op(Op) ->
+ case Op of
+ fadd -> fadd;
+ fsub -> fsub;
+ fdiv -> fdiv;
+ fmul -> fmul;
+ fchs -> fsub;
+ Other -> exit({?MODULE, trans_fp_op, {"Unknown RTL float operator",Other}})
+ end.
+
+%% Misc.
+insn_dst(I) ->
+ case I of
+ #alu{} ->
+ [hipe_rtl:alu_dst(I)];
+ #alub{} ->
+ [hipe_rtl:alub_dst(I)];
+ #call{} ->
+ case hipe_rtl:call_dstlist(I) of
+ [] -> [];
+ [Dst] -> [Dst]
+ end;
+ #load{} ->
+ [hipe_rtl:load_dst(I)];
+ #load_address{} ->
+ [hipe_rtl:load_address_dst(I)];
+ #load_atom{} ->
+ [hipe_rtl:load_atom_dst(I)];
+ #move{} ->
+ [hipe_rtl:move_dst(I)];
+ #phi{} ->
+ [hipe_rtl:phi_dst(I)];
+ #fconv{} ->
+ [hipe_rtl:fconv_dst(I)];
+ #fload{} ->
+ [hipe_rtl:fload_dst(I)];
+ #fmove{} ->
+ [hipe_rtl:fmove_dst(I)];
+ #fp{} ->
+ [hipe_rtl:fp_dst(I)];
+ #fp_unop{} ->
+ [hipe_rtl:fp_unop_dst(I)];
+ _ ->
+ []
+ end.
+
+llvm_type_from_size(Size) ->
+ case Size of
+ byte -> hipe_llvm:mk_int(8);
+ int16 -> hipe_llvm:mk_int(16);
+ int32 -> hipe_llvm:mk_int(32);
+ word -> hipe_llvm:mk_int(64)
+ end.
+
+%% @doc Create definition for the compiled function. The parameters that are
+%% passed to the stack must be reversed to match with the CC. Also
+%% precoloured registers that are passed as arguments must be stored to
+%% the corresonding stack slots.
+create_function_definition(Fun, Params, Code, LocalVars) ->
+ FunctionName = trans_mfa_name(Fun),
+ FixedRegs = fixed_registers(),
+ %% Reverse parameters to match with the Erlang calling convention
+ ReversedParams =
+ case erlang:length(Params) > ?NR_ARG_REGS of
+ false ->
+ Params;
+ true ->
+ {ParamsInRegs, ParamsInStack} = lists:split(?NR_ARG_REGS, Params),
+ ParamsInRegs ++ lists:reverse(ParamsInStack)
+ end,
+ Args = header_regs(FixedRegs) ++ header_params(ReversedParams),
+ EntryLabel = hipe_llvm:mk_label("Entry"),
+ FloatTy = hipe_llvm:mk_double(),
+ ExceptionSync = hipe_llvm:mk_alloca("%exception_sync", FloatTy, [], []),
+ I2 = load_regs(FixedRegs),
+ I3 = hipe_llvm:mk_br(mk_jump_label(get(first_label))),
+ StoredParams = store_params(Params),
+ EntryBlock =
+ lists:flatten([EntryLabel, ExceptionSync, I2, LocalVars, StoredParams, I3]),
+ Final_Code = EntryBlock ++ Code,
+ FunctionOptions = [nounwind, noredzone, list_to_atom("gc \"erlang\"")],
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ hipe_llvm:mk_fun_def([], [], "cc 11", [], FunRetTy, FunctionName, Args,
+ FunctionOptions, [], Final_Code).
+
+header_params(Params) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ [{WordTy, "%v" ++ integer_to_list(hipe_rtl:var_index(P))} || P <- Params].
+
+store_params(Params) ->
+ Fun1 =
+ fun(X) ->
+ Index = hipe_rtl:var_index(X),
+ {Name, _} = trans_dst(X),
+ ParamName = "%v" ++ integer_to_list(Index),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ hipe_llvm:mk_store(WordTy, ParamName, WordTyPtr, Name, [], [], false)
+ end,
+ lists:map(Fun1, Params).
+
+fixed_registers() ->
+ case get(hipe_target_arch) of
+ x86 ->
+ ["hp", "p"];
+ amd64 ->
+ ["hp", "p"];
+ Other ->
+ exit({?MODULE, map_registers, {"Unknown architecture", Other}})
+ end.
+
+header_regs(Registers) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ [{WordTy, "%" ++ X ++ "_in"} || X <- Registers].
+
+load_regs(Registers) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ Fun1 =
+ fun(X) ->
+ I1 = hipe_llvm:mk_alloca("%" ++ X ++ "_reg_var", WordTy, [], []),
+ I2 = hipe_llvm:mk_store(WordTy, "%" ++ X ++ "_in", WordTyPtr,
+ "%" ++ X ++ "_reg_var", [], [], false),
+ [I1, I2]
+ end,
+ lists:map(Fun1, Registers).
+
+%%------------------------------------------------------------------------------
+%% Relocation-specific Stuff
+%%------------------------------------------------------------------------------
+
+relocs_store(Key, Value, Relocs) ->
+ dict:store(Key, Value, Relocs).
+
+relocs_to_list(Relocs) ->
+ dict:to_list(Relocs).
+
+%% @doc This function is responsible for the actions needed to handle
+%% relocations:
+%% 1) Updates relocations with constants and switch jump tables.
+%% 2) Creates LLVM code to declare relocations as external
+%% functions/constants.
+%% 3) Creates LLVM code in order to create local variables for the external
+%% constants/labels.
+handle_relocations(Relocs, Data, Fun) ->
+ RelocsList = relocs_to_list(Relocs),
+ %% Seperate Relocations according to their type
+ {CallList, AtomList, ClosureList, ClosureLabels, SwitchList} =
+ seperate_relocs(RelocsList),
+ %% Create code to declare atoms
+ AtomDecl = [declare_atom(A) || A <- AtomList],
+ %% Create code to create local name for atoms
+ AtomLoad = [load_atom(A) || A <- AtomList],
+ %% Create code to declare closures
+ ClosureDecl = [declare_closure(C) || C <- ClosureList],
+ %% Create code to create local name for closures
+ ClosureLoad = [load_closure(C) || C <- ClosureList],
+ %% Find function calls
+ IsExternalCall = fun (X) -> is_external_call(X, Fun) end,
+ ExternalCallList = lists:filter(IsExternalCall, CallList),
+ %% Create code to declare external function
+ FunDecl = fixed_fun_decl() ++ [call_to_decl(C) || C <- ExternalCallList],
+ %% Extract constant labels from Constant Map (remove duplicates)
+ ConstLabels = hipe_consttab:labels(Data),
+ %% Create code to declare constants
+ ConstDecl = [declare_constant(C) || C <- ConstLabels],
+ %% Create code to create local name for constants
+ ConstLoad = [load_constant(C) || C <- ConstLabels],
+ %% Create code to create jump tables
+ SwitchDecl = declare_switches(SwitchList, Fun),
+ %% Create code to create a table with the labels of all closure calls
+ {ClosureLabelDecl, Relocs1} =
+ declare_closure_labels(ClosureLabels, Relocs, Fun),
+ %% Enter constants to relocations
+ Relocs2 = lists:foldl(fun const_to_dict/2, Relocs1, ConstLabels),
+ %% Temporary Store inc_stack and llvm_fix_pinned_regs to Dictionary
+ %% TODO: Remove this
+ Relocs3 = dict:store("inc_stack_0", {call, {bif, inc_stack_0, 0}}, Relocs2),
+ Relocs4 = dict:store("hipe_bifs.llvm_fix_pinned_regs.0",
+ {call, {hipe_bifs, llvm_fix_pinned_regs, 0}}, Relocs3),
+ BranchMetaData = [
+ hipe_llvm:mk_branch_meta(?BRANCH_META_TAKEN, "99", "1")
+ , hipe_llvm:mk_branch_meta(?BRANCH_META_NOT_TAKEN, "1", "99")
+ ],
+ ExternalDeclarations = AtomDecl ++ ClosureDecl ++ ConstDecl ++ FunDecl ++
+ ClosureLabelDecl ++ SwitchDecl ++ BranchMetaData,
+ LocalVariables = AtomLoad ++ ClosureLoad ++ ConstLoad,
+ {Relocs4, ExternalDeclarations, LocalVariables}.
+
+%% @doc Seperate relocations according to their type.
+seperate_relocs(Relocs) ->
+ seperate_relocs(Relocs, [], [], [], [], []).
+
+seperate_relocs([], CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc) ->
+ {CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc};
+seperate_relocs([R|Rs], CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc) ->
+ case R of
+ {_, {call, _}} ->
+ seperate_relocs(Rs, [R | CallAcc], AtomAcc, ClosureAcc, LabelAcc,
+ JmpTableAcc);
+ {_, {atom, _}} ->
+ seperate_relocs(Rs, CallAcc, [R | AtomAcc], ClosureAcc, LabelAcc,
+ JmpTableAcc);
+ {_, {closure, _}} ->
+ seperate_relocs(Rs, CallAcc, AtomAcc, [R | ClosureAcc], LabelAcc,
+ JmpTableAcc);
+ {_, {switch, _, _}} ->
+ seperate_relocs(Rs, CallAcc, AtomAcc, ClosureAcc, LabelAcc,
+ [R | JmpTableAcc]);
+ {_, {closure_label, _, _}} ->
+ seperate_relocs(Rs, CallAcc, AtomAcc, ClosureAcc, [R | LabelAcc],
+ JmpTableAcc)
+ end.
+
+%% @doc External declaration of an atom.
+declare_atom({AtomName, _}) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ hipe_llvm:mk_const_decl("@" ++ AtomName, "external constant", WordTy, "").
+
+%% @doc Creation of local variable for an atom.
+load_atom({AtomName, _}) ->
+ Dst = "%" ++ AtomName ++ "_var",
+ Name = "@" ++ AtomName,
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, WordTyPtr, Name, WordTy).
+
+%% @doc External declaration of a closure.
+declare_closure({ClosureName, _})->
+ ByteTy = hipe_llvm:mk_int(8),
+ hipe_llvm:mk_const_decl("@" ++ ClosureName, "external constant", ByteTy, "").
+
+%% @doc Creation of local variable for a closure.
+load_closure({ClosureName, _})->
+ Dst = "%" ++ ClosureName ++ "_var",
+ Name = "@" ++ ClosureName,
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
+
+%% @doc Declaration of a local variable for a switch jump table.
+declare_switches(JumpTableList, Fun) ->
+ FunName = trans_mfa_name(Fun),
+ [declare_switch_table(X, FunName) || X <- JumpTableList].
+
+declare_switch_table({Name, {switch, {TableType, Labels, _, _}, _}}, FunName) ->
+ LabelList = [mk_jump_label(L) || L <- Labels],
+ Fun1 = fun(X) -> "i8* blockaddress(@" ++ FunName ++ ", " ++ X ++ ")" end,
+ List2 = lists:map(Fun1, LabelList),
+ List3 = string:join(List2, ",\n"),
+ List4 = "[\n" ++ List3 ++ "\n]\n",
+ hipe_llvm:mk_const_decl("@" ++ Name, "constant", TableType, List4).
+
+%% @doc Declaration of a variable for a table with the labels of all closure
+%% calls in the code.
+declare_closure_labels([], Relocs, _Fun) ->
+ {[], Relocs};
+declare_closure_labels(ClosureLabels, Relocs, Fun) ->
+ FunName = trans_mfa_name(Fun),
+ {LabelList, ArityList} =
+ lists:unzip([{mk_jump_label(Label), A} ||
+ {_, {closure_label, Label, A}} <- ClosureLabels]),
+ Relocs1 = relocs_store("table_closures", {table_closures, ArityList}, Relocs),
+ List2 =
+ ["i8* blockaddress(@" ++ FunName ++ ", " ++ L ++ ")" || L <- LabelList],
+ List3 = string:join(List2, ",\n"),
+ List4 = "[\n" ++ List3 ++ "\n]\n",
+ NrLabels = length(LabelList),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr),
+ ConstDecl =
+ hipe_llvm:mk_const_decl("@table_closures", "constant", TableType, List4),
+ {[ConstDecl], Relocs1}.
+
+%% @doc A call is treated as non external only in a case of a recursive
+%% function.
+is_external_call({_, {call, Fun}}, Fun) -> false;
+is_external_call(_, _) -> true.
+
+%% @doc External declaration of a function.
+call_to_decl({Name, {call, MFA}}) ->
+ {M, _F, A} = MFA,
+ CConv = "cc 11",
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ {Type, Args} =
+ case M of
+ llvm ->
+ {hipe_llvm:mk_struct([WordTy, hipe_llvm:mk_int(1)]), [1, 2]};
+ %% +precoloured regs
+ _ ->
+ {FunRetTy, lists:seq(1, A + ?NR_PINNED_REGS)}
+ end,
+ ArgsTypes = lists:duplicate(length(Args), WordTy),
+ hipe_llvm:mk_fun_decl([], [], CConv, [], Type, "@" ++ Name, ArgsTypes, []).
+
+%% @doc These functions are always declared, even if not used.
+fixed_fun_decl() ->
+ ByteTy = hipe_llvm:mk_int(8),
+ ByteTyPtr = hipe_llvm:mk_pointer(ByteTy),
+ LandPad = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_int(32),
+ "@__gcc_personality_v0", [hipe_llvm:mk_int(32), hipe_llvm:mk_int(64),
+ ByteTyPtr, ByteTyPtr], []),
+ GCROOTDecl = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_void(),
+ "@llvm.gcroot", [hipe_llvm:mk_pointer(ByteTyPtr), ByteTyPtr], []),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ FixPinnedRegs = hipe_llvm:mk_fun_decl([], [], [], [], FunRetTy,
+ "@hipe_bifs.llvm_fix_pinned_regs.0", [], []),
+ GcMetadata = hipe_llvm:mk_const_decl("@gc_metadata", "external constant",
+ ByteTy, ""),
+ [LandPad, GCROOTDecl, FixPinnedRegs, GcMetadata].
+
+%% @doc Declare an External Consant. We declare all constants as i8 in order to
+%% be able to calcucate pointers of the form DL+6, with the getelementptr
+%% instruction. Otherwise we have to convert constants form pointers to
+%% values, add the offset and convert them again to pointers.
+declare_constant(Label) ->
+ Name = "@DL" ++ integer_to_list(Label),
+ ByteTy = hipe_llvm:mk_int(8),
+ hipe_llvm:mk_const_decl(Name, "external constant", ByteTy, "").
+
+%% @doc Load a constant is achieved by converting a pointer to an integer of
+%% the correct width.
+load_constant(Label) ->
+ Dst = "%DL" ++ integer_to_list(Label) ++ "_var",
+ Name = "@DL" ++ integer_to_list(Label),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
+
+%% @doc Store external constants and calls to dictionary.
+const_to_dict(Elem, Dict) ->
+ Name = "DL" ++ integer_to_list(Elem),
+ dict:store(Name, {'constant', Elem}, Dict).
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index 7db4db8a57..e81212d4dc 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -30,6 +30,7 @@
cerl_prettypr,
cerl_to_icode,
cerl_typean,
+ elf_format,
erl_bif_types,
erl_types,
hipe,
@@ -108,6 +109,10 @@
hipe_ig,
hipe_ig_moves,
hipe_jit,
+ hipe_llvm,
+ hipe_llvm_liveness,
+ hipe_llvm_main,
+ hipe_llvm_merge,
hipe_ls_regalloc,
hipe_main,
hipe_moves,
@@ -159,6 +164,7 @@
hipe_rtl_symbolic,
hipe_rtl_to_amd64,
hipe_rtl_to_arm,
+ hipe_rtl_to_llvm,
hipe_rtl_to_ppc,
hipe_rtl_to_sparc,
hipe_rtl_to_x86,
@@ -192,7 +198,6 @@
hipe_tagscheme,
hipe_temp_map,
hipe_timing,
- hipe_tool,
hipe_vectors,
hipe_x86,
hipe_x86_assemble,
@@ -217,4 +222,6 @@
hipe_x86_x87]},
{registered,[]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["syntax_tools-1.6.14","stdlib-2.0","kernel-3.0",
+ "erts-6.0","compiler-5.0"]}]}.
diff --git a/lib/hipe/main/hipe.appup.src b/lib/hipe/main/hipe.appup.src
index 1d5a0d93f5..02679fab21 100644
--- a/lib/hipe/main/hipe.appup.src
+++ b/lib/hipe/main/hipe.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -15,5 +15,4 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-{"%VSN%",[],[]}.
+{"%VSN%", [], []}.
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index 434d5c3061..d47eced6d8 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -821,7 +821,9 @@ finalize_fun_sequential({MFA, Icode}, Opts, Servers) ->
?debug_msg("Compiled ~w in ~.2f s\n", [MFA,(T2-T1)/1000])),
{MFA, Code};
{rtl, LinearRtl} ->
- {MFA, LinearRtl}
+ {MFA, LinearRtl};
+ {llvm_binary, Binary} ->
+ {MFA, Binary}
catch
error:Error ->
?when_option(verbose, Opts, ?debug_untagged_msg("\n", [])),
@@ -890,21 +892,27 @@ do_load(Mod, Bin, BeamBinOrPath) when is_binary(BeamBinOrPath);
end.
assemble(CompiledCode, Closures, Exports, Options) ->
- case get(hipe_target_arch) of
- ultrasparc ->
- hipe_sparc_assemble:assemble(CompiledCode, Closures, Exports, Options);
- powerpc ->
- hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
- ppc64 ->
- hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
- arm ->
- hipe_arm_assemble:assemble(CompiledCode, Closures, Exports, Options);
- x86 ->
- hipe_x86_assemble:assemble(CompiledCode, Closures, Exports, Options);
- amd64 ->
- hipe_amd64_assemble:assemble(CompiledCode, Closures, Exports, Options);
- Arch ->
- ?EXIT({executing_on_an_unsupported_architecture, Arch})
+ case proplists:get_bool(to_llvm, Options) of
+ false ->
+ case get(hipe_target_arch) of
+ ultrasparc ->
+ hipe_sparc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ powerpc ->
+ hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ ppc64 ->
+ hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ arm ->
+ hipe_arm_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ x86 ->
+ hipe_x86_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ amd64 ->
+ hipe_amd64_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ Arch ->
+ ?EXIT({executing_on_an_unsupported_architecture, Arch})
+ end;
+ true ->
+ %% Merge already compiled code (per MFA) to a single binary.
+ hipe_llvm_merge:finalize(CompiledCode, Closures, Exports)
end.
%% --------------------------------------------------------------------
@@ -1330,6 +1338,11 @@ opt_keys() ->
timeregalloc,
timers,
to_rtl,
+ to_llvm, % Use the LLVM backend for compilation.
+ llvm_save_temps, % Save the LLVM intermediate files in the current
+ % directory; useful for debugging.
+ llvm_llc, % Specify llc optimization-level: o1, o2, o3, undefined.
+ llvm_opt, % Specify opt optimization-level: o1, o2, o3, undefined.
use_indexing,
use_inline_atom_search,
use_callgraph,
@@ -1468,11 +1481,19 @@ opt_expansions() ->
[{o1, o1_opts()},
{o2, o2_opts()},
{o3, o3_opts()},
+ {to_llvm, llvm_opts(o3)},
+ {{to_llvm, o0}, llvm_opts(o0)},
+ {{to_llvm, o1}, llvm_opts(o1)},
+ {{to_llvm, o2}, llvm_opts(o2)},
+ {{to_llvm, o3}, llvm_opts(o3)},
{x87, [x87, inline_fp]},
{inline_fp, case get(hipe_target_arch) of %% XXX: Temporary until x86
x86 -> [x87, inline_fp]; %% has sse2
_ -> [inline_fp] end}].
+llvm_opts(O) ->
+ [to_llvm, {llvm_opt, O}, {llvm_llc, O}].
+
%% This expands "basic" options, which may be tested early and cannot be
%% in conflict with options found in the source code.
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index 99028cc3c1..89b79998be 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -49,7 +49,7 @@
%%=====================================================================
-type comp_icode_ret() :: {'native',hipe_architecture(),{'unprofiled',_}}
- | {'rtl',tuple()}.
+ | {'rtl',tuple()} | {'llvm_binary',term()}.
%%=====================================================================
@@ -115,11 +115,18 @@ compile_icode(MFA, LinearIcode0, Options, Servers, DebugState) ->
pp(IcodeCfg7, MFA, icode_liveness, pp_icode_liveness, Options, Servers),
FinalIcode = hipe_icode_cfg:cfg_to_linear(IcodeCfg7),
?opt_stop_timer("Icode"),
- LinearRTL = ?option_time(icode_to_rtl(MFA,FinalIcode,Options, Servers),
- "RTL", Options),
+ {LinearRTL, Roots} = ?option_time(icode_to_rtl(MFA, FinalIcode, Options, Servers),
+ "RTL", Options),
case proplists:get_bool(to_rtl, Options) of
false ->
- rtl_to_native(MFA, LinearRTL, Options, DebugState);
+ case proplists:get_bool(to_llvm, Options) of
+ false ->
+ rtl_to_native(MFA, LinearRTL, Options, DebugState);
+ true ->
+ %% The LLVM backend returns binary code, unlike the rest of the HiPE
+ %% backends which return native assembly.
+ rtl_to_llvm_to_binary(MFA, LinearRTL, Roots, Options, DebugState)
+ end;
true ->
put(hipe_debug, DebugState),
{rtl, LinearRTL}
@@ -385,11 +392,21 @@ icode_to_rtl(MFA, Icode, Options, Servers) ->
%% hipe_rtl_cfg:pp(RtlCfg3),
pp(RtlCfg3, MFA, rtl_liveness, pp_rtl_liveness, Options, Servers),
RtlCfg4 = rtl_lcm(RtlCfg3, Options),
- pp(RtlCfg4, MFA, rtl, pp_rtl, Options, Servers),
- LinearRTL1 = hipe_rtl_cfg:linearize(RtlCfg4),
+ %% LLVM: A liveness analysis on RTL must be performed in order to find the GC
+ %% roots and explicitly mark them (in RTL) when they go out of scope (only
+ %% when the LLVM backend is used).
+ {RtlCfg5, Roots} =
+ case proplists:get_bool(to_llvm, Options) of
+ false ->
+ {RtlCfg4, []};
+ true ->
+ hipe_llvm_liveness:analyze(RtlCfg4)
+ end,
+ pp(RtlCfg5, MFA, rtl, pp_rtl, Options, Servers),
+ LinearRTL1 = hipe_rtl_cfg:linearize(RtlCfg5),
LinearRTL2 = hipe_rtl_cleanup_const:cleanup(LinearRTL1),
%% hipe_rtl:pp(standard_io, LinearRTL2),
- LinearRTL2.
+ {LinearRTL2, Roots}.
translate_to_rtl(Icode, Options) ->
%% GC tests should have been added in the conversion to Icode.
@@ -540,6 +557,17 @@ rtl_to_native(MFA, LinearRTL, Options, DebugState) ->
put(hipe_debug, DebugState),
LinearNativeCode.
+%% Translate Linear RTL to binary code using LLVM.
+rtl_to_llvm_to_binary(MFA, LinearRTL, Roots, Options, DebugState) ->
+ ?opt_start_timer("LLVM native code"),
+ %% BinaryCode is a tuple, as defined in llvm/hipe_llvm_main module, which
+ %% contains the binary code together with info needed by the loader, e.g.
+ %% ConstTab, Refs, LabelMap, etc.
+ BinaryCode = hipe_llvm_main:rtl_to_native(MFA, LinearRTL, Roots, Options),
+ ?opt_stop_timer("LLVM native code"),
+ put(hipe_debug, DebugState),
+ {llvm_binary, BinaryCode}.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Debugging stuff ...
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl
index c381e6a057..2b02f54b5c 100644
--- a/lib/hipe/misc/hipe_consttab.erl
+++ b/lib/hipe/misc/hipe_consttab.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -462,7 +462,7 @@ update_referred_labels(Table, LabelMap) ->
tree_keys(T) ->
dict:fetch_keys(T).
--spec tree_to_list(dict()) -> [{_, _}].
+-spec tree_to_list(dict:dict()) -> [{_, _}].
tree_to_list(T) ->
dict:to_list(T).
@@ -486,11 +486,11 @@ tree_lookup(Key, T) ->
none
end.
--spec tree_empty() -> dict().
+-spec tree_empty() -> dict:dict().
tree_empty() ->
dict:new().
--spec tree_lookup_key_for_value(ctdata(), dict()) -> 'none' | {'value', _}.
+-spec tree_lookup_key_for_value(ctdata(), dict:dict()) -> 'none' | {'value', _}.
tree_lookup_key_for_value(Val, T) ->
tree_lookup_key_for_value_1(tree_to_list(T), Val).
diff --git a/lib/hipe/misc/hipe_consttab.hrl b/lib/hipe/misc/hipe_consttab.hrl
index 39018dac34..aea3c5bc88 100644
--- a/lib/hipe/misc/hipe_consttab.hrl
+++ b/lib/hipe/misc/hipe_consttab.hrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -22,6 +22,6 @@
-type ct_alignment() :: 4 | 8.
-type hipe_constlbl() :: non_neg_integer().
--type hipe_consttab() :: {dict(), [hipe_constlbl()], hipe_constlbl()}.
+-type hipe_consttab() :: {dict:dict(), [hipe_constlbl()], hipe_constlbl()}.
%%-----------------------------------------------------------------------------
diff --git a/lib/hipe/misc/hipe_gensym.erl b/lib/hipe/misc/hipe_gensym.erl
index 84fc8fa7e8..4d2a237188 100644
--- a/lib/hipe/misc/hipe_gensym.erl
+++ b/lib/hipe/misc/hipe_gensym.erl
@@ -44,7 +44,7 @@
%% Types of allowable entities to set global variables for
%%-----------------------------------------------------------------------
--type gvarname() :: 'icode' | 'rtl' | 'arm' | 'ppc' | 'sparc' | 'x86'.
+-type gvarname() :: 'icode' | 'rtl' | 'arm' | 'ppc' | 'sparc' | 'x86' | 'llvm'.
%%-----------------------------------------------------------------------
diff --git a/lib/hipe/misc/hipe_pack_constants.erl b/lib/hipe/misc/hipe_pack_constants.erl
index e214d7ebbc..300f9ae43a 100644
--- a/lib/hipe/misc/hipe_pack_constants.erl
+++ b/lib/hipe/misc/hipe_pack_constants.erl
@@ -3,7 +3,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -20,30 +20,48 @@
%%
-module(hipe_pack_constants).
--export([pack_constants/2, slim_refs/1, slim_constmap/1]).
+-export([pack_constants/2, slim_refs/1, slim_constmap/1,
+ find_const/2, mk_data_relocs/2, slim_sorted_exportmap/3]).
-include("hipe_consttab.hrl").
-include("../../kernel/src/hipe_ext_format.hrl").
+-include("../main/hipe.hrl"). % Needed for the EXIT macro in find_const/2.
%%-----------------------------------------------------------------------------
--type raw_data() :: binary() | number() | list() | tuple().
--type tbl_ref() :: {hipe_constlbl(), non_neg_integer()}.
+-type const_num() :: non_neg_integer().
+-type raw_data() :: binary() | number() | list() | tuple().
+
+-type addr() :: non_neg_integer().
+-type ref_p() :: {DataPos :: hipe_constlbl(), CodeOffset :: addr()}.
+-type ref() :: ref_p() | {'sorted', Base :: addr(), [ref_p()]}.
+
+-type mfa_refs() :: {mfa(), [ref()]}.
+
+%% XXX: these types may not belong here: FIX!
+-type fa() :: {atom(), arity()}.
+-type export_map() :: [{addr(), module(), atom(), arity()}].
-record(pcm_entry, {mfa :: mfa(),
label :: hipe_constlbl(),
- const_num :: non_neg_integer(),
- start :: non_neg_integer(),
+ const_num :: const_num(),
+ start :: addr(),
type :: 0 | 1 | 2,
raw_data :: raw_data()}).
+-type pcm_entry() :: #pcm_entry{}.
+
+-type label_map() :: gb_trees:tree({mfa(), hipe_constlbl()}, addr()).
+
+%% Some of the following types may possibly need to be exported
+-type data_relocs() :: [ref()].
+-type packed_const_map() :: [pcm_entry()].
+-type mfa_refs_map() :: [mfa_refs()].
+-type slim_export_map() :: [addr() | module() | atom() | arity() | boolean()].
%%-----------------------------------------------------------------------------
-spec pack_constants([{mfa(),[_],hipe_consttab()}], ct_alignment()) ->
- {ct_alignment(),
- non_neg_integer(),
- [#pcm_entry{}],
- [{mfa(),[tbl_ref() | {'sorted',non_neg_integer(),[tbl_ref()]}]}]}.
+ {ct_alignment(), non_neg_integer(), packed_const_map(), mfa_refs_map()}.
pack_constants(Data, Align) ->
pack_constants(Data, 0, Align, 0, [], []).
@@ -194,13 +212,12 @@ compact_dests([], Dest, AccofDest, Acc) ->
%% to the slimmed and flattened format ConstMap which is put in object
%% files.
%%
--spec slim_constmap([#pcm_entry{}]) -> [raw_data()].
+-spec slim_constmap(packed_const_map()) -> [raw_data()].
slim_constmap(Map) ->
slim_constmap(Map, gb_sets:new(), []).
--spec slim_constmap([#pcm_entry{}], gb_set(), [raw_data()]) -> [raw_data()].
-slim_constmap([#pcm_entry{const_num=ConstNo, start=Offset,
- type=Type, raw_data=Term}|Rest], Inserted, Acc) ->
+slim_constmap([#pcm_entry{const_num = ConstNo, start = Offset,
+ type = Type, raw_data = Term}|Rest], Inserted, Acc) ->
case gb_sets:is_member(ConstNo, Inserted) of
true ->
slim_constmap(Rest, Inserted, Acc);
@@ -209,3 +226,60 @@ slim_constmap([#pcm_entry{const_num=ConstNo, start=Offset,
slim_constmap(Rest, NewInserted, [ConstNo, Offset, Type, Term|Acc])
end;
slim_constmap([], _Inserted, Acc) -> Acc.
+
+%%
+%% Lookup a constant in a ConstMap.
+%%
+-spec find_const({mfa(), hipe_constlbl()}, packed_const_map()) -> const_num().
+
+find_const({MFA, Label}, [E = #pcm_entry{mfa = MFA, label = Label}|_]) ->
+ E#pcm_entry.const_num;
+find_const(N, [_|R]) ->
+ find_const(N, R);
+find_const(C, []) ->
+ ?EXIT({constant_not_found, C}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%
+%% Functions to build and handle Refs, ExportMap and LabelMap.
+%% Note: Moved here because they are used by all backends in
+%% hipe_{arm,sparc,ppc,x86}_assemble.erl
+%% XXX: Is this the right place for them?
+%%
+
+-spec mk_data_relocs(mfa_refs_map(), label_map()) -> data_relocs().
+
+mk_data_relocs(RefsFromConsts, LabelMap) ->
+ lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
+
+mk_data_relocs([{MFA, Labels} | Rest], LabelMap, Acc) ->
+ Map = [case Label of
+ {L,Pos} ->
+ Offset = find({MFA,L}, LabelMap),
+ {Pos,Offset};
+ {sorted,Base,OrderedLabels} ->
+ {sorted, Base, [begin
+ Offset = find({MFA,L}, LabelMap),
+ {Order, Offset}
+ end
+ || {L,Order} <- OrderedLabels]}
+ end
+ || Label <- Labels],
+ %% msg("Map: ~w Map\n", [Map]),
+ mk_data_relocs(Rest, LabelMap, [Map,Acc]);
+mk_data_relocs([], _, Acc) -> Acc.
+
+find({MFA,L}, LabelMap) ->
+ gb_trees:get({MFA,L}, LabelMap).
+
+-spec slim_sorted_exportmap(export_map(), [mfa()], [fa()]) -> slim_export_map().
+
+slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
+ IsClosure = lists:member({M,F,A}, Closures),
+ IsExported = is_exported(F, A, Exports),
+ [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
+slim_sorted_exportmap([], _, _) -> [].
+
+is_exported(F, A, Exports) ->
+ lists:member({F,A}, Exports).
diff --git a/lib/hipe/misc/hipe_sdi.erl b/lib/hipe/misc/hipe_sdi.erl
index ef1b5b48c5..9a2ff78ecf 100644
--- a/lib/hipe/misc/hipe_sdi.erl
+++ b/lib/hipe/misc/hipe_sdi.erl
@@ -3,7 +3,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%% Copyright Ericsson AB 2004-2014. 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
@@ -50,7 +50,7 @@
-record(pass1, {prevSdi :: integer(),
preS = [] :: [#pre_sdi_data{}],
- labelMap = gb_trees:empty() :: gb_tree()}).
+ labelMap = gb_trees:empty() :: gb_trees:tree()}).
-record(sdi_data, {address :: address(),
label_address :: address(),
@@ -105,11 +105,11 @@ pass1_add_sdi(Pass1, Address, Label, SdiInfo) ->
PreSdiData = #pre_sdi_data{address=Address, label=Label, si=SdiInfo},
Pass1#pass1{prevSdi=PrevSdi+1, preS=[PreSdiData|PreS]}.
--spec pass1_finalise(#pass1{}) -> {non_neg_integer(),tuple(),gb_tree()}.
+-spec pass1_finalise(#pass1{}) -> {non_neg_integer(),tuple(),gb_trees:tree()}.
pass1_finalise(#pass1{prevSdi=PrevSdi, preS=PreS, labelMap=LabelMap}) ->
{PrevSdi+1, pass1_finalise_preS(PreS, LabelMap, []), LabelMap}.
--spec pass1_finalise_preS([#pre_sdi_data{}], gb_tree(), [#sdi_data{}]) ->
+-spec pass1_finalise_preS([#pre_sdi_data{}], gb_trees:tree(), [#sdi_data{}]) ->
tuple().
pass1_finalise_preS([], _LabelMap, S) -> vector_from_list(S);
pass1_finalise_preS([PreSdiData|PreS], LabelMap, S) ->
@@ -122,7 +122,7 @@ pass1_finalise_preS([PreSdiData|PreS], LabelMap, S) ->
%%% Pass2.
--spec pass2(#pass1{}) -> {gb_tree(), non_neg_integer()}.
+-spec pass2(#pass1{}) -> {gb_trees:tree(), non_neg_integer()}.
pass2(Pass1) ->
{N,SDIS,LabelMap} = pass1_finalise(Pass1),
LONG = mk_long(N),
@@ -339,13 +339,14 @@ initINCR(SdiNr, PrevIncr, N, LONG, INCREMENT) ->
%%% a and previous sdi i is remapped to a+incr(i), where
%%% incr(i) = if i < 0 then 0 else INCREMENT[i].
--spec adjust_label_map(gb_tree(), hipe_array()) -> gb_tree().
+-spec adjust_label_map(gb_trees:tree(), hipe_array()) -> gb_trees:tree().
adjust_label_map(LabelMap, INCREMENT) ->
applyIncr(gb_trees:to_list(LabelMap), INCREMENT, gb_trees:empty()).
-type label_pair() :: {label(), #label_data{}}.
--spec applyIncr([label_pair()], hipe_array(), gb_tree()) -> gb_tree().
+-spec applyIncr([label_pair()], hipe_array(), gb_trees:tree()) ->
+ gb_trees:tree().
applyIncr([], _INCREMENT, LabelMap) -> LabelMap;
applyIncr([{Label,LabelData}|List], INCREMENT, LabelMap) ->
#label_data{address=Address, prevSdi=PrevSdi} = LabelData,
diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl
index b2fd50517b..3ad91f4051 100644
--- a/lib/hipe/ppc/hipe_ppc_assemble.erl
+++ b/lib/hipe/ppc/hipe_ppc_assemble.erl
@@ -46,8 +46,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
print("Total num bytes=~w\n", [CodeSize], Options),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -288,7 +288,7 @@ do_pseudo_li(I, MFA, ConstMap) ->
%%% end,
%%% {load_address, {Tag,untag_mfa_or_prim(MFAorPrim)}};
{Label,constant} ->
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{load_address, {constant,ConstNo}};
{Label,closure} ->
{load_address, {closure,Label}};
@@ -574,37 +574,6 @@ mk_y(Pred, BD) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({_MFA,_L} = MFAL,LabelMap) ->
- gb_trees:get(MFAL, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -642,14 +611,3 @@ fill_spaces(N) when N > 0 ->
fill_spaces(N-1);
fill_spaces(0) ->
[].
-
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label}, [{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N, [_|R]) ->
- find_const(N, R);
-find_const(C, []) ->
- ?EXIT({constant_not_found,C}).
diff --git a/lib/hipe/regalloc/hipe_ig_moves.erl b/lib/hipe/regalloc/hipe_ig_moves.erl
index 186c87a690..ebc6ebc20d 100644
--- a/lib/hipe/regalloc/hipe_ig_moves.erl
+++ b/lib/hipe/regalloc/hipe_ig_moves.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -36,7 +36,7 @@
-record(ig_moves, {movelist :: hipe_vector(),
nrmoves = 0 :: non_neg_integer(),
moveinsns = [] :: [{_,_}],
- moveset = gb_sets:empty() :: gb_set()}).
+ moveset = gb_sets:empty() :: gb_sets:set()}).
%%-----------------------------------------------------------------------------
diff --git a/lib/hipe/regalloc/hipe_ls_regalloc.erl b/lib/hipe/regalloc/hipe_ls_regalloc.erl
index 4276b8f968..7a00a0534a 100644
--- a/lib/hipe/regalloc/hipe_ls_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_ls_regalloc.erl
@@ -722,7 +722,7 @@ is_free(R, Free) ->
is_free(R, Free, []).
is_free(R, [{R,_}|Rest], Acc) ->
- {true,lists:reverse(Acc)++Rest};
+ {true, lists:reverse(Acc, Rest)};
is_free(R, [X|Rs],Acc) ->
is_free(R, Rs, [X|Acc]);
is_free(_, [], _) ->
@@ -733,7 +733,7 @@ exists_free_register(Start, Regs) ->
exists_free_register(Start, [{Phys, Start0}|Rest], Acc)
when Start > Start0 ->
- {true, Phys, lists:reverse(Acc)++Rest};
+ {true, Phys, lists:reverse(Acc, Rest)};
exists_free_register(Start, [Free|Rest], Acc) ->
exists_free_register(Start, Rest, [Free|Acc]);
exists_free_register(_, [], _) ->
diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
index 5bad31ade9..0278a896d2 100644
--- a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
@@ -958,9 +958,9 @@ splits_2({Cols, NonCols, OldSpillCost}, L, SpillCost) ->
%% Merge two ordered sub-splits into one.
spillCostOrderedMerge(Spl1, [], Spl) ->
- lists:reverse(Spl) ++ Spl1;
+ lists:reverse(Spl, Spl1);
spillCostOrderedMerge([], Spl2, Spl) ->
- lists:reverse(Spl) ++ Spl2;
+ lists:reverse(Spl, Spl2);
spillCostOrderedMerge(Spl1, Spl2, Spl) ->
{_, _, SpillCost1} = hd(Spl1),
{_, _, SpillCost2} = hd(Spl2),
diff --git a/lib/hipe/rtl/hipe_icode2rtl.erl b/lib/hipe/rtl/hipe_icode2rtl.erl
index 034153a3cb..483d0b37f7 100644
--- a/lib/hipe/rtl/hipe_icode2rtl.erl
+++ b/lib/hipe/rtl/hipe_icode2rtl.erl
@@ -427,8 +427,6 @@ gen_type_test([X], Type, TrueLbl, FalseLbl, Pred, ConstTab) ->
hipe_rtl:mk_branch(X, eq, TmpF, TrueLbl, FalseLbl, Pred)], ConstTab};
cons ->
{hipe_tagscheme:test_cons(X, TrueLbl, FalseLbl, Pred), ConstTab};
- constant ->
- {hipe_tagscheme:test_constant(X, TrueLbl, FalseLbl, Pred), ConstTab};
fixnum ->
{hipe_tagscheme:test_fixnum(X, TrueLbl, FalseLbl, Pred), ConstTab};
float ->
@@ -439,6 +437,8 @@ gen_type_test([X], Type, TrueLbl, FalseLbl, Pred, ConstTab) ->
{hipe_tagscheme:test_integer(X, TrueLbl, FalseLbl, Pred), ConstTab};
list ->
{hipe_tagscheme:test_list(X, TrueLbl, FalseLbl, Pred), ConstTab};
+ map ->
+ {hipe_tagscheme:test_map(X, TrueLbl, FalseLbl, Pred), ConstTab};
nil ->
{hipe_tagscheme:test_nil(X, TrueLbl, FalseLbl, Pred), ConstTab};
number ->
diff --git a/lib/hipe/rtl/hipe_rtl.erl b/lib/hipe/rtl/hipe_rtl.erl
index 4bf4eb6bd7..bc61bec0bd 100644
--- a/lib/hipe/rtl/hipe_rtl.erl
+++ b/lib/hipe/rtl/hipe_rtl.erl
@@ -29,7 +29,7 @@
%% <li> {alu, Dst, Src1, Op, Src2} </li>
%% <li> {alub, Dst, Src1, Op, Src2, RelOp, TrueLabel, FalseLabel, P} </li>
%% <li> {branch, Src1, Src2, RelOp, TrueLabel, FalseLabel, P} </li>
-%% <li> {call, DsListt, Fun, ArgList, Type, Continuation, FailContinuation}
+%% <li> {call, DsListt, Fun, ArgList, Type, Continuation, FailContinuation, NormalContinuation}
%% Type is one of {local, remote, primop, closure} </li>
%% <li> {comment, Text} </li>
%% <li> {enter, Fun, ArgList, Type}
@@ -106,7 +106,7 @@
%% rtl_data_update/2,
%% rtl_var_range/1,
%% rtl_var_range_update/2,
- %% rtl_label_range/1,
+ rtl_label_range/1,
%% rtl_label_range_update/2,
rtl_info/1,
rtl_info_update/2]).
@@ -226,6 +226,7 @@
%% goto_label_update/2,
mk_call/6,
+ mk_call/7,
call_fun/1,
call_dstlist/1,
call_dstlist_update/2,
@@ -233,8 +234,10 @@
call_continuation/1,
call_fail/1,
call_type/1,
+ call_normal/1,
+ call_normal_update/2,
%% call_continuation_update/2,
- %% call_fail_update/2,
+ call_fail_update/2,
is_call/1,
mk_enter/3,
@@ -290,10 +293,13 @@
%% fconv_src_update/2,
%% is_fconv/1,
- %% mk_var/1,
+ mk_var/1,
+ mk_var/2,
mk_new_var/0,
is_var/1,
var_index/1,
+ var_liveness/1,
+ var_liveness_update/2,
%% change_vars_to_regs/1,
@@ -350,10 +356,15 @@
%% move_dst_update/2,
fixnumop_dst_update/2,
pp_instr/2,
- %% pp_arg/2,
+ %% Uber hack!
+ pp_var/2,
+ pp_reg/2,
+ pp_arg/2,
phi_arglist_update/2,
phi_redirect_pred/3]).
+-export([subst_uses_llvm/2]).
+
-export_type([alub_cond/0]).
%%
@@ -387,7 +398,7 @@ rtl_data(#rtl{data=Data}) -> Data.
%% rtl_data_update(Rtl, Data) -> Rtl#rtl{data=Data}.
%% rtl_var_range(#rtl{var_range=VarRange}) -> VarRange.
%% rtl_var_range_update(Rtl, VarRange) -> Rtl#rtl{var_range=VarRange}.
-%% rtl_label_range(#rtl{label_range=LabelRange}) -> LabelRange.
+rtl_label_range(#rtl{label_range=LabelRange}) -> LabelRange.
%% rtl_label_range_update(Rtl, LabelRange) -> Rtl#rtl{label_range=LabelRange}.
rtl_info(#rtl{info=Info}) -> Info.
rtl_info_update(Rtl, Info) -> Rtl#rtl{info=Info}.
@@ -643,6 +654,17 @@ is_goto(_) -> false.
%% call
%%
+%% LLVM: Call with normal continuation
+mk_call(DstList, Fun, ArgList, Continuation, FailContinuation,
+ NormalContinuation, Type) ->
+ case Type of
+ remote -> ok;
+ not_remote -> ok
+ end,
+ #call{dstlist=DstList, 'fun'=Fun, arglist=ArgList, type=Type,
+ continuation=Continuation, failcontinuation=FailContinuation,
+ normalcontinuation=NormalContinuation}.
+
mk_call(DstList, Fun, ArgList, Continuation, FailContinuation, Type) ->
case Type of
remote -> ok;
@@ -651,6 +673,10 @@ mk_call(DstList, Fun, ArgList, Continuation, FailContinuation, Type) ->
#call{dstlist=DstList, 'fun'=Fun, arglist=ArgList, type=Type,
continuation=Continuation,
failcontinuation=FailContinuation}.
+
+call_normal(#call{normalcontinuation=NormalContinuation}) -> NormalContinuation.
+call_normal_update(C, NewNormalContinuation) ->
+ C#call{normalcontinuation=NewNormalContinuation}.
call_dstlist(#call{dstlist=DstList}) -> DstList.
call_dstlist_update(C, NewDstList) -> C#call{dstlist=NewDstList}.
call_fun(#call{'fun'=Fun}) -> Fun.
@@ -853,11 +879,14 @@ reg_is_gcsafe(#rtl_reg{is_gc_safe=IsGcSafe}) -> IsGcSafe.
is_reg(#rtl_reg{}) -> true;
is_reg(_) -> false.
--record(rtl_var, {index :: non_neg_integer()}).
+-record(rtl_var, {index :: non_neg_integer(), liveness=live :: dead | live}).
mk_var(Num) when is_integer(Num), Num >= 0 -> #rtl_var{index=Num}.
+mk_var(Num, Liveness) when is_integer(Num), Num>=0 -> #rtl_var{index=Num, liveness=Liveness}.
mk_new_var() -> mk_var(hipe_gensym:get_next_var(rtl)).
var_index(#rtl_var{index=Index}) -> Index.
+var_liveness(#rtl_var{liveness=Liveness}) -> Liveness.
+var_liveness_update(RtlVar, Liveness) -> RtlVar#rtl_var{liveness=Liveness}.
is_var(#rtl_var{}) -> true;
is_var(_) -> false.
@@ -1077,6 +1106,131 @@ subst_uses(Subst, I) ->
switch_src_update(I, subst1(Subst, switch_src(I)))
end.
+subst_uses_llvm(Subst, I) ->
+ case I of
+ #alu{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, alu_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, alu_src1(I)),
+ I0 = alu_src1_update(I, NewSrc1),
+ alu_src2_update(I0, NewSrc2);
+ #alub{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, alub_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, alub_src1(I)),
+ I0 = alub_src1_update(I, NewSrc1),
+ alub_src2_update(I0, NewSrc2);
+ #branch{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, branch_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, branch_src1(I)),
+ I0 = branch_src1_update(I, NewSrc1),
+ branch_src2_update(I0, NewSrc2);
+ #call{} ->
+ case call_is_known(I) of
+ false ->
+ {NewFun, Subst1} = subst1_llvm(Subst, call_fun(I)),
+ {NewArgList, _} = subst_list_llvm(Subst1, call_arglist(I)),
+ I0 = call_fun_update(I, NewFun),
+ call_arglist_update(I0, NewArgList);
+ true ->
+ {NewArgList, _} = subst_list_llvm(Subst, call_arglist(I)),
+ call_arglist_update(I, NewArgList)
+ end;
+ #comment{} ->
+ I;
+ #enter{} ->
+ case enter_is_known(I) of
+ false ->
+ {NewFun, Subst1} = subst1_llvm(Subst, enter_fun(I)),
+ {NewArgList, _} = subst_list_llvm(Subst1, enter_arglist(I)),
+ I0 = enter_fun_update(I, NewFun),
+ enter_arglist_update(I0, NewArgList);
+ true ->
+ {NewArgList, _} = subst_list_llvm(Subst, enter_arglist(I)),
+ enter_arglist_update(I, NewArgList)
+ end;
+ #fconv{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fconv_src(I)),
+ fconv_src_update(I, NewSrc);
+ #fixnumop{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fixnumop_src(I)),
+ fixnumop_src_update(I, NewSrc);
+ #fload{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, fload_src(I)),
+ {NewOffset, _ } = subst1_llvm(Subst1, fload_offset(I)),
+ I0 = fload_src_update(I, NewSrc),
+ fload_offset_update(I0, NewOffset);
+ #fmove{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fmove_src(I)),
+ fmove_src_update(I, NewSrc);
+ #fp{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, fp_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, fp_src1(I)),
+ I0 = fp_src1_update(I, NewSrc1),
+ fp_src2_update(I0, NewSrc2);
+ #fp_unop{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fp_unop_src(I)),
+ fp_unop_src_update(I, NewSrc);
+ #fstore{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, fstore_src(I)),
+ {NewBase, Subst2} = subst1_llvm(Subst1, fstore_base(I)),
+ {NewOffset, _ } = subst1_llvm(Subst2, fstore_offset(I)),
+ I0 = fstore_src_update(I, NewSrc),
+ I1 = fstore_base_update(I0, NewBase),
+ fstore_offset_update(I1, NewOffset);
+ #goto{} ->
+ I;
+ #goto_index{} ->
+ I;
+ #gctest{} ->
+ {NewWords, _ } = subst1_llvm(Subst, gctest_words(I)),
+ gctest_words_update(I, NewWords);
+ #label{} ->
+ I;
+ #load{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, load_src(I)),
+ {NewOffset, _ } = subst1_llvm(Subst1, load_offset(I)),
+ I0 = load_src_update(I, NewSrc),
+ load_offset_update(I0, NewOffset);
+ #load_address{} ->
+ I;
+ #load_atom{} ->
+ I;
+ #load_word_index{} ->
+ I;
+ #move{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, move_src(I)),
+ move_src_update(I, NewSrc);
+ #multimove{} ->
+ {NewSrcList, _} = subst_list_llvm(Subst, multimove_srclist(I)),
+ multimove_srclist_update(I, NewSrcList);
+ #phi{} ->
+ phi_argvar_subst(I, Subst);
+ #return{} ->
+ {NewVarList, _} = subst_list_llvm(Subst, return_varlist(I)),
+ return_varlist_update(I, NewVarList);
+ #store{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, store_src(I)),
+ {NewBase, Subst2} = subst1_llvm(Subst1, store_base(I)),
+ {NewOffset, _ } = subst1_llvm(Subst2, store_offset(I)),
+ I0 = store_src_update(I, NewSrc),
+ I1 = store_base_update(I0, NewBase),
+ store_offset_update(I1, NewOffset);
+ #switch{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, switch_src(I)),
+ switch_src_update(I, NewSrc)
+ end.
+
+subst_list_llvm(S,X) -> subst_list_llvm(S, lists:reverse(X), []).
+subst_list_llvm(S, [], Acc) -> {Acc, S};
+subst_list_llvm(S, [X|Xs], Acc) ->
+ {NewX, RestS} = subst1_llvm(S, X),
+ subst_list_llvm(RestS, Xs, [NewX|Acc]).
+
+subst1_llvm(A,B) -> subst1_llvm(A,B,[]).
+
+subst1_llvm([], X, Acc) -> {X, Acc};
+subst1_llvm([{X,Y}|Rs], X, Acc) -> {Y, Acc++Rs};
+subst1_llvm([R|Xs], X, Acc) -> subst1_llvm(Xs,X,[R|Acc]).
+
subst_defines(Subst, I)->
case I of
#alu{} ->
@@ -1614,7 +1768,11 @@ pp_var(Dev, Arg) ->
true ->
pp_hard_reg(Dev, var_index(Arg));
false ->
- io:format(Dev, "v~w", [var_index(Arg)])
+ io:format(Dev, "v~w", [var_index(Arg)]),
+ case var_liveness(Arg) of
+ dead -> io:format(Dev, "(dead)", []);
+ _ -> ok
+ end
end.
pp_arg(Dev, A) ->
diff --git a/lib/hipe/rtl/hipe_rtl.hrl b/lib/hipe/rtl/hipe_rtl.hrl
index 974e40f830..fbdf9ac524 100644
--- a/lib/hipe/rtl/hipe_rtl.hrl
+++ b/lib/hipe/rtl/hipe_rtl.hrl
@@ -28,7 +28,8 @@
-record(alu, {dst, src1, op, src2}).
-record(alub, {dst, src1, op, src2, 'cond', true_label, false_label, p}).
-record(branch, {src1, src2, 'cond', true_label, false_label, p}).
--record(call, {dstlist, 'fun', arglist, type, continuation, failcontinuation}).
+-record(call, {dstlist, 'fun', arglist, type, continuation,
+ failcontinuation, normalcontinuation = []}).
-record(comment, {text}).
-record(enter, {'fun', arglist, type}).
-record(fconv, {dst, src}).
diff --git a/lib/hipe/rtl/hipe_rtl_liveness.erl b/lib/hipe/rtl/hipe_rtl_liveness.erl
index 3cfada9d6c..0c4b6b2e11 100644
--- a/lib/hipe/rtl/hipe_rtl_liveness.erl
+++ b/lib/hipe/rtl/hipe_rtl_liveness.erl
@@ -34,7 +34,8 @@
-module(hipe_rtl_liveness).
-%% -define(LIVEOUT_NEEDED,true). % needed for liveness.inc below.
+%% -define(DEBUG_LIVENESS,true).
+-define(LIVEOUT_NEEDED,true). % needed for liveness.inc below.
-define(PRETTY_PRINT,false).
-include("hipe_rtl.hrl").
diff --git a/lib/hipe/rtl/hipe_tagscheme.erl b/lib/hipe/rtl/hipe_tagscheme.erl
index f1e8d1ef41..c27c682915 100644
--- a/lib/hipe/rtl/hipe_tagscheme.erl
+++ b/lib/hipe/rtl/hipe_tagscheme.erl
@@ -39,14 +39,13 @@
test_tuple/4, test_atom/4, test_bignum/4, test_pos_bignum/4,
test_any_pid/4, test_any_port/4,
test_ref/4, test_fun/4, test_fun2/5, test_matchstate/4,
- test_binary/4, test_bitstr/4, test_list/4,
- test_integer/4, test_number/4, test_constant/4, test_tuple_N/5]).
+ test_binary/4, test_bitstr/4, test_list/4, test_map/4,
+ test_integer/4, test_number/4, test_tuple_N/5]).
-export([realtag_fixnum/2, tag_fixnum/2, realuntag_fixnum/2, untag_fixnum/2]).
-export([test_two_fixnums/3, test_fixnums/4, unsafe_fixnum_add/3,
unsafe_fixnum_sub/3,
fixnum_gt/5, fixnum_lt/5, fixnum_ge/5, fixnum_le/5, fixnum_val/1,
- fixnum_mul/4,
- fixnum_addsub/5, fixnum_andorxor/4, fixnum_not/2,
+ fixnum_mul/4, fixnum_addsub/5, fixnum_andorxor/4, fixnum_not/2,
fixnum_bsr/3, fixnum_bsl/3]).
-export([unsafe_car/2, unsafe_cdr/2,
unsafe_constant_element/3, unsafe_update_element/3, element/6]).
@@ -113,13 +112,15 @@
-define(TAG_HEADER_EXTERNAL_PID, ((16#C bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
-define(TAG_HEADER_EXTERNAL_PORT,((16#D bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
-define(TAG_HEADER_EXTERNAL_REF, ((16#E bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
+-define(TAG_HEADER_MAP, ((16#F bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
-define(TAG_HEADER_MASK, 16#3F).
-define(HEADER_ARITY_OFFS, 6).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_header(SZ,TAG) -> (SZ bsl ?HEADER_ARITY_OFFS) + TAG.
+mk_header(SZ, TAG) -> (SZ bsl ?HEADER_ARITY_OFFS) + TAG.
+
mk_arityval(SZ) -> mk_header(SZ, ?TAG_HEADER_ARITYVAL).
size_from_header(Sz, Header) ->
@@ -133,9 +134,9 @@ mk_var_header(Header, Size, Tag) ->
mk_fixnum(X) -> (X bsl ?TAG_IMMED1_SIZE) + ?TAG_IMMED1_SMALL.
-define(NIL, ((-1 bsl ?TAG_IMMED2_SIZE) bor ?TAG_IMMED2_NIL)).
-mk_nil() -> ?NIL.
-%% mk_atom(X) -> (X bsl ?TAG_IMMED2_SIZE) + ?TAG_IMMED2_ATOM.
-mk_non_value() -> ?THE_NON_VALUE.
+mk_nil() -> ?NIL.
+%% mk_atom(X) -> (X bsl ?TAG_IMMED2_SIZE) + ?TAG_IMMED2_ATOM.
+mk_non_value() -> ?THE_NON_VALUE.
-spec is_fixnum(integer()) -> boolean().
is_fixnum(N) when is_integer(N) ->
@@ -253,6 +254,15 @@ test_tuple_N(X, N, TrueLab, FalseLab, Pred) ->
hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(mk_arityval(N)),
TrueLab, FalseLab, Pred)].
+test_map(X, TrueLab, FalseLab, Pred) ->
+ Tmp = hipe_rtl:mk_new_reg_gcsafe(),
+ HalfTrueLab = hipe_rtl:mk_new_label(),
+ MapMask = ?TAG_HEADER_MASK,
+ [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred),
+ HalfTrueLab,
+ get_header(Tmp, X),
+ mask_and_compare(Tmp, MapMask, ?TAG_HEADER_MAP, TrueLab, FalseLab, Pred)].
+
test_ref(X, TrueLab, FalseLab, Pred) ->
Hdr = hipe_rtl:mk_new_reg_gcsafe(),
Tag = hipe_rtl:mk_new_reg_gcsafe(),
@@ -405,17 +415,6 @@ test_number(X, TrueLab, FalseLab, Pred) ->
hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(HeaderFlonum),
TrueLab, FalseLab, Pred)].
-%% CONS, NIL, and TUPLE are not constants, everything else is
-test_constant(X, TrueLab, FalseLab, Pred) ->
- Lab1 = hipe_rtl:mk_new_label(),
- Lab2 = hipe_rtl:mk_new_label(),
- Pred1 = 1-Pred,
- [test_cons(X, FalseLab, hipe_rtl:label_name(Lab1), Pred1),
- Lab1,
- test_nil(X, FalseLab, hipe_rtl:label_name(Lab2), Pred1),
- Lab2,
- test_tuple(X, FalseLab, TrueLab, Pred1)].
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
tag_fixnum(DestVar, SrcReg) ->
diff --git a/lib/hipe/sparc/hipe_sparc_assemble.erl b/lib/hipe/sparc/hipe_sparc_assemble.erl
index b534fe20ec..68a4e1b349 100644
--- a/lib/hipe/sparc/hipe_sparc_assemble.erl
+++ b/lib/hipe/sparc/hipe_sparc_assemble.erl
@@ -45,8 +45,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
print("Total num bytes=~w\n", [CodeSize], Options),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -222,7 +222,7 @@ do_pseudo_set(I, MFA, ConstMap) ->
%%% end,
%%% {load_address, {Tag,untag_mfa_or_prim(MFAorPrim)}};
{Label,constant} ->
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{load_address, {constant,ConstNo}};
{Label,closure} ->
{load_address, {closure,Label}};
@@ -507,37 +507,6 @@ px({pred,Pred}) -> % XXX: use pt/pn throughout entire backend
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({_MFA,_L} = MFAL, LabelMap) ->
- gb_trees:get(MFAL, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -575,14 +544,3 @@ fill_spaces(N) when N > 0 ->
fill_spaces(N-1);
fill_spaces(0) ->
[].
-
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N,[_|R]) ->
- find_const(N,R);
-find_const(C,[]) ->
- ?EXIT({constant_not_found,C}).
diff --git a/lib/hipe/ssa/hipe_ssa_const_prop.inc b/lib/hipe/ssa/hipe_ssa_const_prop.inc
index 2fce384197..0876fca34a 100644
--- a/lib/hipe/ssa/hipe_ssa_const_prop.inc
+++ b/lib/hipe/ssa/hipe_ssa_const_prop.inc
@@ -3,7 +3,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -72,10 +72,10 @@ visit_expressions([Inst | Insts], Environment, FlowWork, SSAWork) ->
%%-----------------------------------------------------------------------------
-record(env, {cfg :: #cfg{},
- executable_flags = gb_sets:empty() :: gb_set(),
- handled_blocks = gb_sets:empty() :: gb_set(),
- lattice_values = gb_trees:empty() :: gb_tree(),
- ssa_edges = gb_trees:empty() :: gb_tree()
+ executable_flags = gb_sets:empty() :: gb_sets:set(),
+ handled_blocks = gb_sets:empty() :: gb_sets:set(),
+ lattice_values = gb_trees:empty() :: gb_trees:tree(),
+ ssa_edges = gb_trees:empty() :: gb_trees:tree()
}).
create_env(CFG) ->
diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile
new file mode 100644
index 0000000000..cedb150b5d
--- /dev/null
+++ b/lib/hipe/test/Makefile
@@ -0,0 +1,73 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ hipe_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+# ----------------------------------------------------
+# Files
+# ----------------------------------------------------
+EMAKEFILE = Emakefile
+AUXILIARY_FILES = hipe.spec hipe_testsuite_driver.erl $(EMAKEFILE)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/hipe_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_docs_spec:
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(AUXILIARY_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
+ @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ cd "$(RELSYSDIR)";\
+ erlc hipe_testsuite_driver.erl;\
+ erl -noshell -run hipe_testsuite_driver create_all_suites -s erlang halt
diff --git a/lib/hipe/test/bs_SUITE_data/bs_add.erl b/lib/hipe/test/bs_SUITE_data/bs_add.erl
new file mode 100644
index 0000000000..af5a3b2f23
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_add.erl
@@ -0,0 +1,18 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------------
+%% The guard in f/3 revealed a problem in the translation of the 'bs_add'
+%% BEAM instruction to Icode. The fail label was not properly translated.
+%% Fixed 3/2/2011.
+%%-------------------------------------------------------------------------
+-module(bs_add).
+
+-export([test/0]).
+
+test() ->
+ 42 = f(<<12345:16>>, 4711, <<42>>),
+ ok.
+
+f(Bin, A, B) when <<A:9, B:7/binary>> == Bin ->
+ gazonk;
+f(Bin, _, _) when is_binary(Bin) ->
+ 42.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_bincomp.erl b/lib/hipe/test/bs_SUITE_data/bs_bincomp.erl
new file mode 100644
index 0000000000..082b83bab9
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_bincomp.erl
@@ -0,0 +1,79 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_bincomp.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Test bit comprehensions
+%%% Created : 13 Sep 2006
+%%%-------------------------------------------------------------------
+-module(bs_bincomp).
+
+-export([test/0]).
+
+test() ->
+ ok = byte_aligned(),
+ ok = bit_aligned(),
+ ok = extended_byte_aligned(),
+ ok = extended_bit_aligned(),
+ ok = mixed(),
+ ok.
+
+byte_aligned() ->
+ <<"abcdefg">> = << <<(X+32)>> || <<X>> <= <<"ABCDEFG">> >>,
+ <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || <<X:32>> <= <<1:32,2:32,3:32,4:32>> >>,
+ <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || <<X:16>> <= <<1:16,2:16,3:16,4:16>> >>,
+ ok.
+
+bit_aligned() ->
+ <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ << <<(X+32):7>> || <<X>> <= <<"ABCDEFG">> >>,
+ <<"ABCDEFG">> =
+ << <<(X-32)>> || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> >>,
+ <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || <<X:31>> <= <<1:31,2:31,3:31,4:31>> >>,
+ <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || <<X:15>> <= <<1:15,2:15,3:15,4:15>> >>,
+ ok.
+
+extended_byte_aligned() ->
+ <<"abcdefg">> = << <<(X+32)>> || X <- "ABCDEFG" >>,
+ "abcdefg" = [(X+32) || <<X>> <= <<"ABCDEFG">>],
+ <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || X <- [1,2,3,4] >>,
+ [256,512,768,1024] =
+ [X || <<X:16/little>> <= <<1:16,2:16,3:16,4:16>>],
+ ok.
+
+extended_bit_aligned() ->
+ <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ << <<(X+32):7>> || X <- "ABCDEFG" >>,
+ "ABCDEFG" = [(X-32) || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>],
+ <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || X <- [1,2,3,4] >>,
+ [256,512,768,1024] =
+ [X || <<X:15/little>> <= <<1:15,2:15,3:15,4:15>>],
+ ok.
+
+mixed() ->
+ <<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>> >>,
+ <<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, Y <- [1,2] >>,
+ <<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || X <- [1,2,3,4], Y <- [1,2] >>,
+ [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>>],
+ [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X>> <= <<1,2,3,4>>, Y <- [1,2]],
+ <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>> >>,
+ <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2] >>,
+ <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || X <- [1,2,3,4], Y <- [1,2] >>,
+ [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>>],
+ [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2]],
+ ok.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_bits.erl b/lib/hipe/test/bs_SUITE_data/bs_bits.erl
new file mode 100644
index 0000000000..ef9a6bb137
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_bits.erl
@@ -0,0 +1,150 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_bits.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Tests for bit stream operations including matching,
+%%% construction, binary_to_list and list_to_binary
+%%% Created : 6 Sep 2006
+%%%-------------------------------------------------------------------
+-module(bs_bits).
+
+-export([test/0]).
+
+test() ->
+ <<1:100>> = <<1:100>>,
+ ok = match(7),
+ ok = match(9),
+ ok = match1(15),
+ ok = match1(31),
+ ok = horrid_match(),
+ ok = test_bitstr(),
+ ok = test_is_bitstr(<<1:1>>,<<8>>),
+ ok = test_is_binary(<<1:1>>,<<8>>),
+ ok = test_bitsize(),
+ ok = asymmetric_tests(),
+ ok = big_asymmetric_tests(),
+ ok = bitstr_to_and_from_list(),
+ ok = big_bitstr_to_and_from_list(),
+ ok = send_and_receive(),
+ ok = send_and_receive_alot(),
+ ok.
+
+match(N) ->
+ <<0:N>> = <<0:N>>,
+ ok.
+
+match1(N) ->
+ <<42:N/little>> = <<42:N/little>>,
+ ok.
+
+test_is_bitstr(Bitstr, Binary) ->
+ true = is_bitstring(Bitstr),
+ true = is_bitstring(Binary),
+ ok = if is_bitstring(Bitstr) -> ok end,
+ ok = if is_bitstring(Binary) -> ok end.
+
+test_is_binary(Bitstr, Binary) ->
+ false = is_binary(Bitstr),
+ true = is_binary(Binary),
+ ok = if is_binary(Bitstr) -> not_ok; true -> ok end,
+ ok = if is_binary(Binary) -> ok end.
+
+test_bitsize() ->
+ 101 = erlang:bit_size(<<1:101>>),
+ 1001 = erlang:bit_size(<<1:1001>>),
+ 80 = erlang:bit_size(<<1:80>>),
+ 800 = erlang:bit_size(<<1:800>>),
+ Bin = <<0:16#1000000>>,
+ BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]),
+ 16#10000001 = bit_size(BigBin),
+ %% Only run these on computers with lots of memory
+ %% HugeBin = list_to_bitstring([BigBin||_ <- lists:seq(1,16#10)]++[<<1:1>>]),
+ %% 16#100000011 = bit_size(HugeBin),
+ 0 = erlang:bit_size(<<>>),
+ ok.
+
+horrid_match() ->
+ <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>,
+ <<42:24/little>> = B,
+ ok.
+
+test_bitstr() ->
+ <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>,
+ <<1:1,6>> = B,
+ B = <<1:1,6>>,
+ ok.
+
+asymmetric_tests() ->
+ <<1:12>> = <<0,1:4>>,
+ <<0,1:4>> = <<1:12>>,
+ <<1:1,X/bitstring>> = <<128,255,0,0:2>>,
+ <<1,254,0,0:1>> = X,
+ X = <<1,254,0,0:1>>,
+ <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>,
+ <<1,254,0,0:1>> = X1,
+ X1 = <<1,254,0,0:1>>,
+ ok.
+
+big_asymmetric_tests() ->
+ <<1:875,1:12>> = <<1:875,0,1:4>>,
+ <<1:875,0,1:4>> = <<1:875,1:12>>,
+ <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>,
+ <<1,254,0,0:1,1:875>> = X,
+ X = <<1,254,0,0:1,1:875>>,
+ <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>,
+ <<1,254,0,0:1,1:875>> = X1,
+ X1 = <<1,254,0,0:1,1:875>>,
+ ok.
+
+bitstr_to_and_from_list() ->
+ <<1:7>> = list_to_bitstring(bitstring_to_list(<<1:7>>)),
+ <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)),
+ [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>),
+ <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]),
+ [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>),
+ ok.
+
+big_bitstr_to_and_from_list() ->
+ <<1:800,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)),
+ [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>),
+ <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]),
+ ok.
+
+send_and_receive() ->
+ Bin = <<1,2:7>>,
+ Pid = spawn(fun() -> receiver(Bin) end),
+ Pid ! {self(),<<1:7,8:5,Bin/bitstring>>},
+ receive
+ ok ->
+ ok
+ end.
+
+receiver(Bin) ->
+ receive
+ {Pid,<<1:7,8:5,Bin/bitstring>>} ->
+ Pid ! ok
+ end.
+
+send_and_receive_alot() ->
+ Bin = <<1:1000001>>,
+ Pid = spawn(fun() -> receiver_alot(Bin) end),
+ send_alot(100,Bin,Pid).
+
+send_alot(N,Bin,Pid) when N > 0 ->
+ Pid ! {self(),<<1:7,8:5,Bin/bitstring>>},
+ receive
+ ok ->
+ ok
+ end,
+ send_alot(N-1,Bin,Pid);
+send_alot(0,_Bin,Pid) ->
+ Pid ! no_more,
+ ok.
+
+receiver_alot(Bin) ->
+ receive
+ {Pid,<<1:7,8:5,Bin/bitstring>>} ->
+ Pid ! ok;
+ no_more -> ok
+ end,
+ receiver_alot(Bin).
diff --git a/lib/hipe/test/bs_SUITE_data/bs_bitsize.erl b/lib/hipe/test/bs_SUITE_data/bs_bitsize.erl
new file mode 100644
index 0000000000..c0774e7279
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_bitsize.erl
@@ -0,0 +1,23 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------
+-module(bs_bitsize).
+
+-export([test/0]).
+
+test() ->
+ true = bitsize_in_body(<<1:42>>),
+ true = bitsize_in_guard(<<1:7>>),
+ 8 = constant_binary(42),
+ ok.
+
+bitsize_in_body(Bin) ->
+ 42 =:= erlang:bit_size(Bin).
+
+bitsize_in_guard(Bin) when erlang:bit_size(Bin) rem 7 =:= 0 ->
+ true;
+bitsize_in_guard(Bin) when is_bitstring(Bin) ->
+ false.
+
+%% Tests that binary constants can properly be treated in Icode
+constant_binary(N) when N > 0 ->
+ bit_size(<<42>>).
diff --git a/lib/hipe/test/bs_SUITE_data/bs_bugs_R08.erl b/lib/hipe/test/bs_SUITE_data/bs_bugs_R08.erl
new file mode 100644
index 0000000000..7b62a17cfb
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_bugs_R08.erl
@@ -0,0 +1,32 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------
+%% When executing this in R8 (and compiled with R8) the result was
+%% {ok,[148,129,0,0]} but should be {ok,[145,148,113,129,0,0,0,0]}
+%% Thanks to Kenneth Lundin for sending this to us.
+%%-------------------------------------------------------------------
+
+-module(bs_bugs_R08).
+
+-export([test/0]).
+
+test() ->
+ List = [145,148,113,129,0,0,0,0],
+ {ok, List} = msisdn_internal_storage(<<145,148,113,129,0,0,0,0>>, []),
+ ok.
+
+%% msisdn_internal_storage/3
+%% Convert MSISDN binary to internal datatype (TBCD-octet list)
+
+msisdn_internal_storage(<<>>, MSISDN) ->
+ {ok, lists:reverse(MSISDN)};
+msisdn_internal_storage(<<2#11111111:8,_Rest/binary>>, MSISDN) ->
+ {ok, lists:reverse(MSISDN)};
+msisdn_internal_storage(<<2#1111:4,DigitN:4,_Rest/binary>>, MSISDN) when
+ DigitN < 10 ->
+ {ok, lists:reverse([(DigitN bor 2#11110000)|MSISDN])};
+msisdn_internal_storage(<<DigitNplus1:4,DigitN:4,Rest/binary>>, MSISDN) when
+ DigitNplus1 < 10, DigitN < 10 ->
+ NewMSISDN = [((DigitNplus1 bsl 4) bor DigitN)|MSISDN],
+ msisdn_internal_storage(Rest, NewMSISDN);
+msisdn_internal_storage(_Rest, _MSISDN) ->
+ {fault}. %% Mandatory IE incorrect
diff --git a/lib/hipe/test/bs_SUITE_data/bs_bugs_R09.erl b/lib/hipe/test/bs_SUITE_data/bs_bugs_R09.erl
new file mode 100644
index 0000000000..670f2a08bb
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_bugs_R09.erl
@@ -0,0 +1,35 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Date: Mon, 7 Jun 2004 13:07:39 +0300
+%% From: Einar Karttunen
+%% To: Erlang ML <[email protected]>
+%% Subject: Apparent binary matching bug with native compilation
+%%
+%% It seems that there is a problem with binary matching when
+%% compiling native code. A length prefixed field matches one
+%% byte too short in the native case.
+%%
+%% The test module works when compiled with no options, but
+%% crashes with case_clause when compiled with [native].
+%% This has been confirmed with R9C-0 and hipe snapshot 5/4/2004.
+%%--------------------------------------------------------------------
+
+-module(bs_bugs_R09).
+
+-export([test/0]).
+
+test() ->
+ ["rei",".",[]] = pp(<<3,$r,$e,$i,0>>),
+ ok.
+
+pp(Bin) ->
+ %% io:format("PP with ~p~n", [Bin]),
+ case Bin of
+ <<>> ->
+ ["."];
+ <<_:2, Len:6, Part:Len/binary>> ->
+ [binary_to_list(Part)];
+ <<_:2, Len:6, Part:Len/binary, Rest/binary>> ->
+ %% io:format("Len ~p Part ~p Rest ~p~n", [Len,Part,Rest]),
+ [binary_to_list(Part), "." | pp(Rest)]
+ end.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_bugs_R12.erl b/lib/hipe/test/bs_SUITE_data/bs_bugs_R12.erl
new file mode 100644
index 0000000000..43ee9eb85b
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_bugs_R12.erl
@@ -0,0 +1,133 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Contains three cases of bugs that were reported for R12B
+%%--------------------------------------------------------------------
+-module(bs_bugs_R12).
+
+-export([test/0]).
+
+test() ->
+ ok = test_beam_bug(),
+ ok = test_v3_codegen(),
+ ok = test_hipe_bug(),
+ ok.
+
+%%--------------------------------
+%% First test case: a bug in BEAM
+%%--------------------------------
+test_beam_bug() ->
+ lists:foreach(fun (_) -> ok = run(100) end, [1,2,3,4]).
+
+%% For testing - runs scanner N number of times with same input
+run(N) ->
+ lists:foreach(fun(_) -> scan(<<"region:whatever">>, []) end, lists:seq(1, N)).
+
+scan(<<>>, TokAcc) ->
+ lists:reverse(['$thats_all_folks$' | TokAcc]);
+scan(<<D, Z, Rest/binary>>, TokAcc)
+ when (D =:= $D orelse D =:= $d) and
+ ((Z =:= $\s) or (Z =:= $() or (Z =:= $))) ->
+ scan(<<Z, Rest/binary>>, ['AND' | TokAcc]);
+scan(<<D>>, TokAcc) when (D =:= $D) or (D =:= $d) ->
+ scan(<<>>, ['AND' | TokAcc]);
+scan(<<N, Z, Rest/binary>>, TokAcc)
+ when (N =:= $N orelse N =:= $n) and
+ ((Z =:= $\s) or (Z =:= $() or (Z =:= $))) ->
+ scan(<<Z, Rest/binary>>, ['NOT' | TokAcc]);
+scan(<<C, Rest/binary>>, TokAcc) when (C >= $A) and (C =< $Z);
+ (C >= $a) and (C =< $z);
+ (C >= $0) and (C =< $9) ->
+ case Rest of
+ <<$:, R/binary>> ->
+ scan(R, [{'FIELD', C} | TokAcc]);
+ _ ->
+ scan(Rest, [{'KEYWORD', C} | TokAcc])
+ end.
+
+%%---------------------------------------------------
+%% Second test case: an internal error in v3_codegen
+%% Reported by Mateusz Berezecki on 19/1/2008
+%%---------------------------------------------------
+-define(S, {42, 4242, 4711}).
+-define(R, <<90,164,116>>).
+
+test_v3_codegen() ->
+ _ = random:seed(?S),
+ B0 = gen_bit(120, <<>>),
+ B1 = set_bit(B0, 5),
+ B2 = clr_bit(B1, 5),
+ ?R = set_bit(B2, 5),
+ ok.
+
+gen_bit(0, Acc) -> Acc;
+gen_bit(N, Acc) when is_integer(N), N > 0 ->
+ gen_bit(N-1, <<Acc/bits, (random:uniform(2)-1):1>>).
+
+%% sets bit K in the Bitmap
+set_bit(<<_Start:32/unsigned-little-integer, Bitmap/bits>>, K)
+ when is_integer(K), 0 < K, K =< bit_size(Bitmap) ->
+ Before = K-1,
+ After = bit_size(Bitmap) - K,
+ <<BeforeBits:Before/bits, _:1, AfterBits:After/bits>> = Bitmap,
+ <<BeforeBits/bits, 1:1, AfterBits/bits>>.
+
+%% clears bit K in the Bitmap
+clr_bit(<<_Start:32/unsigned-little-integer, Bitmap/bits>>, K)
+ when is_integer(K), 0 < K, K =< bit_size(Bitmap) ->
+ Before = K-1,
+ After = bit_size(Bitmap) - K,
+ <<BeforeBits:Before/bits, _:1, AfterBits:After/bits>> = Bitmap,
+ <<BeforeBits/bits, 0:1, AfterBits/bits>>.
+
+%%--------------------------------------------------------------------
+%% Third test case: a bug in HiPE
+%% Reported by Steve Vinoski on 1/3/2008
+%%
+%% Below find the results of compiling and running the example code at
+%% the bottom of this message. Using "c" to compile gives the right
+%% answer; using "hipe:c" gives the wrong answer. This is with R12B-1.
+%%
+%% Within the code, on the second instance of function check/2 you'll
+%% find a commented-out guard. If you uncomment the guard, then the
+%% code works correctly with both "c" and "hipe:c".
+%%---------------------------------------------------------------------
+
+test_hipe_bug() ->
+ String = "2006/10/02/Linux-Journal",
+ Binary = list_to_binary(String),
+ StringToMatch = "200x/" ++ String ++ " ",
+ BinaryToMatch = list_to_binary(StringToMatch),
+ {ok, Binary} = match(BinaryToMatch),
+ ok.
+
+match(<<>>) ->
+ nomatch;
+match(Bin) ->
+ <<Front:16/binary, Tail/binary>> = Bin,
+ case Front of
+ <<_:3/binary,"x/",Y:4/binary,$/,M:2/binary,$/,D:2/binary,$/>> ->
+ case check(Tail) of
+ {ok, Match} ->
+ {ok, <<Y/binary,$/,M/binary,$/,D/binary,$/,Match/binary>>};
+ {nomatch, Skip} ->
+ {skip, Skip+size(Front)};
+ _ ->
+ wrong_answer
+ end;
+ _ ->
+ nomatch
+ end.
+
+check(Bin) ->
+ check(Bin, 0).
+check(<<$ , _/binary>>, 0) ->
+ {nomatch, 0};
+check(Bin, Len) -> %when Len < size(Bin) ->
+ case Bin of
+ <<Front:Len/binary, $ , _/binary>> ->
+ {ok, Front};
+ <<_:Len/binary, $., _/binary>> ->
+ {nomatch, Len};
+ _ ->
+ check(Bin, Len+1)
+ end.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_build.erl b/lib/hipe/test/bs_SUITE_data/bs_build.erl
new file mode 100644
index 0000000000..256cea9403
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_build.erl
@@ -0,0 +1,41 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_build.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose :
+%%%
+%%% Created : 12 Sep 2007
+%%%-------------------------------------------------------------------
+-module(bs_build).
+
+-export([test/0]).
+
+test() ->
+ <<0,1,2,3,4,5,6>> = Bin = << <<X>> || X <- lists:seq(0, 6)>>,
+ test(Bin).
+
+test(Bin) ->
+ <<0,1,2,3,4,5,6,0,1,2,3,4,5,6>> = RealBin = multiply(Bin, 2),
+ <<6,5,4,3,2,1,0,6,5,4,3,2,1,0>> = reverse(RealBin),
+ RealBin = copy(RealBin),
+ RealBin = bc(RealBin),
+ ok.
+
+multiply(Bin, 1) ->
+ Bin;
+multiply(Bin, N) when N > 0 ->
+ <<(multiply(Bin, N-1))/binary, Bin/binary>>.
+
+bc(Bin) ->
+ << <<X>> || <<X>> <= Bin >>.
+
+reverse(<<X, Rest/binary>>) ->
+ <<(reverse(Rest))/binary, X>>;
+reverse(<<>>) -> <<>>.
+
+copy(Bin) ->
+ copy(Bin, <<>>).
+
+copy(<<X, Rest/binary>>, Bin) ->
+ copy(Rest, <<Bin/binary, X>>);
+copy(<<>>, Bin) -> Bin.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_catch_bug.erl b/lib/hipe/test/bs_SUITE_data/bs_catch_bug.erl
new file mode 100644
index 0000000000..6125f8f87f
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_catch_bug.erl
@@ -0,0 +1,25 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_catch_bug.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Tests a catch-related bug which might destroy properties
+%%% of ICode CFGs which are assumed by the subsequent ICode
+%%% binary pass.
+%%% Created : 22 Jan 2004
+%%% -------------------------------------------------------------------
+-module(bs_catch_bug).
+
+-export([test/0]).
+
+test() ->
+ test(foo, <<>>).
+
+%% Introduced auxiliary test/2 function so that constant propagation
+%% does not destroy the properties of the test. - Kostis 26/1/2004
+test(X, Bin) ->
+ catch (<<_/binary>> = X),
+ X = case Bin of
+ <<42,_/binary>> -> weird_bs_match;
+ _ -> X
+ end,
+ ok.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_checksum.erl b/lib/hipe/test/bs_SUITE_data/bs_checksum.erl
new file mode 100644
index 0000000000..ca4f254f12
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_checksum.erl
@@ -0,0 +1,35 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Code from Zoltan Toth that crashed the HiPE compiler (in R11B-3).
+%% The problem was that the binary matching produces a pretty large
+%% integer and we tried to find the range for this integer in a bad way.
+%% Fixed on the same day -- 6th March 2007.
+%%--------------------------------------------------------------------
+
+-module(bs_checksum).
+
+-export([test/0]).
+
+test() ->
+ "3389DAE361AF79B04C9C8E7057F60CC6" = checksum(<<42>>),
+ ok.
+
+checksum(Bin) ->
+ Context = erlang:md5_init(),
+ checksum(Context, Bin).
+
+checksum(Context, <<>>) ->
+ bin_to_hex(erlang:md5_final(Context));
+checksum(Context, <<Bin:20480/binary,Rest/binary>>) ->
+ checksum(erlang:md5_update(Context, Bin), Rest);
+checksum(Context,Bin) ->
+ checksum(erlang:md5_update(Context, Bin), <<>>).
+
+bin_to_hex(Bin) ->
+ lists:flatten([byte_to_hex(X) || X <- binary_to_list(Bin)]).
+
+byte_to_hex(Byte) ->
+ [int_to_hex(Byte div 16), int_to_hex(Byte rem 16)].
+
+int_to_hex(Int) when Int < 10 -> $0 + Int;
+int_to_hex(Int) when Int > 9 -> $A + Int - 10.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_construct.erl b/lib/hipe/test/bs_SUITE_data/bs_construct.erl
new file mode 100644
index 0000000000..9cc9ac848c
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_construct.erl
@@ -0,0 +1,128 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Tests that basic cases of binary construction work
+%%--------------------------------------------------------------------
+-module(bs_construct).
+
+-export([test/0]).
+
+test() ->
+ <<42>> = sz(8),
+ <<42:8/little>> = sz_little(8),
+ <<55>> = take_five(1, 3, 1, 7, 4),
+ ok = bs5(),
+ 16#10000008 = bit_size(large_bin(1, 2, 3, 4)),
+ ok = bad_ones(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Taken from a bug report submitted by Dan Wallin (24 Oct 2003), the
+%% following cases test construction of binaries whose segments have
+%% sizes that are statically unknown.
+
+sz(S) ->
+ <<42:S>>.
+
+sz_little(S) ->
+ <<42:S/little>>.
+
+take_five(A, Head, FB, C, Tail) ->
+ <<A:Head, FB:1, C:Tail>>.
+
+%%--------------------------------------------------------------------
+
+bs5() ->
+ Const = mk_constant(),
+ Pairs = mk_pairs(),
+ true = are_same(Const, Pairs),
+ true = lists:all(fun ({B, L}) -> binary_to_list(B) =:= L end, Pairs),
+ ok.
+
+are_same(C, L) ->
+ C =:= L.
+
+mk_constant() ->
+ [{<<213>>,[213]},
+ {<<56>>,[56]},
+ {<<1,2>>,[1,2]},
+ {<<71>>,[71]},
+ {<<8,1>>,[8,1]},
+ {<<3,9>>,[3,9]},
+ {<<9,3>>,[9,3]},
+ {<<0,0,0,0>>,[0,0,0,0]},
+ {<<62,0,0,0>>,[62,0,0,0]},
+ {<<0,0,0,62>>,[0,0,0,62]},
+ {<<138,99,0,147>>,[138,99,0,147]},
+ {<<138,99,0,148>>,[138,99,0,148]},
+ {<<147,0,99,138>>,[147,0,99,138]},
+ {<<255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255>>,
+ [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]},
+ {<<13>>,[13]},
+ {<<0,4,0,5>>,[0,4,0,5]},
+ {<<129>>,[129]},
+ {<<129>>,[129]},
+ {<<1,2>>,[1,2]},
+ {<<1>>,[1]},
+ {<<4,3,1>>,[4,3,1]},
+ {<<47>>,[47]},
+ {<<>>,[]},
+ {<<97,112,97>>,[97,112,97]},
+ {<<46,110,142,77,45,204,233>>,[46,110,142,77,45,204,233]},
+ {<<>>,[]}].
+
+mk_pairs() ->
+ L4 = [138,99,0,147],
+ [{<<-43>>,[256-43]},
+ {<<56>>,[56]},
+ {<<1,2>>,[1,2]},
+ {<<4:4,7:4>>,[4*16+7]},
+ {<<1:5,1:11>>,[1*8,1]},
+ {<<777:16/big>>,[3,9]},
+ {<<777:16/little>>,[9,3]},
+ {<<0.0:32/float>>,[0,0,0,0]},
+ {<<0.125:32/float>>,[62,0,0,0]},
+ {<<0.125:32/little-float>>,[0,0,0,62]},
+ {<<57285702734876389752897683:32>>,L4},
+ {<<57285702734876389752897684:32>>,[138,99,0,148]},
+ {<<57285702734876389752897683:32/little>>,lists:reverse(L4)},
+ {<<-1:17/unit:8>>,lists:duplicate(17,255)},
+ {<<13>>,[13]},
+ {<<4:8/unit:2,5:2/unit:8>>,[0,4,0,5]},
+ {<<1:1,0:6,1:1>>,[129]},
+ {<<1:1/little,0:6/little,1:1/little>>,[129]},
+ {<<<<1,2>>/binary>>,[1,2]},
+ {<<<<1,2>>:1/binary>>,[1]},
+ {<<4,3,<<1,2>>:1/binary>>,[4,3,1]},
+ {<<(256*45+47)>>,[47]},
+ {<<57:0>>,[]},
+ {<<"apa">>,"apa"},
+ {<<1:3,"string",9:5>>,[46,110,142,77,45,204,233]},
+ {<<>>,[]}].
+
+%%--------------------------------------------------------------------
+%% Constructs a big enough binary to have a bit size that needs a
+%% bignum on 32-bit architectures
+
+large_bin(X1, X2, X3, X4) ->
+ Sz = 16#4000000,
+ <<1, <<X1:Sz, X2:Sz, X3:Sz, X4:Sz>>/bits>>.
+
+%%--------------------------------------------------------------------
+%% Test construction of "bad" binaries
+
+-define(FAIL(Expr), {'EXIT', {badarg, _}} = (catch Expr)).
+
+bad_ones() ->
+ PI = math:pi(),
+ ?FAIL(<<PI>>),
+ Bin12 = <<1,2>>,
+ ?FAIL(<<Bin12>>),
+ E = 2.71,
+ ?FAIL(<<E/binary>>),
+ Int = 24334,
+ ?FAIL(<<Int/binary>>),
+ BigInt = 24334344294788947129487129487219847,
+ ?FAIL(<<BigInt/binary>>),
+ Bin123 = <<1,2,3>>,
+ ?FAIL(<<Bin123/float>>),
+ ok.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_decode.erl b/lib/hipe/test/bs_SUITE_data/bs_decode.erl
new file mode 100644
index 0000000000..d12654a1e3
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_decode.erl
@@ -0,0 +1,980 @@
+%% -*- erlang-indent-level: 2 -*-
+
+-module(bs_decode).
+
+-export([test/0]).
+
+-include("bs_decode_extract.hrl").
+
+-define(PDU, <<30,16,0,90,0,1,0,0,255,255,255,255,81,67,101,7,0,0,0,96,
+ 6,12,146,18,14,0,15,252,16,0,0,17,0,0,128,0,2,241,33,131,
+ 0,20,7,97,112,110,48,49,51,97,8,101,114,105,99,115,115,
+ 111,110,2,115,101,132,0,20,128,192,35,16,1,5,0,16,5,117,
+ 115,101,114,53,5,112,97,115,115,53,133,0,4,172,28,12,1,
+ 133,0,4,172,28,12,3,134,0,8,145,148,113,129,0,0,0,0>>).
+
+-define(RES, {ok,{sesT_createReqV0,
+ {mvsgT_tid,{mvsgT_imsi,<<81,67,101,7,0,0,0,240>>},6},
+ [81,67,101,7,0,0,0,96],
+ {sesT_qualityOfServiceV0,1,4,9,2,18},
+ 0,subscribed,0,0,
+ {mvsgT_pdpAddressType,ietf_ipv4,[]},
+ [<<"apn013a">>,<<"ericsson">>,<<"se">>],
+ {masT_protocolConfigOptions,[],
+ {masT_pap,true,1,5,"user5","pass5"},
+ []},
+ {mvsgT_ipAddress,ipv4,172,28,12,1,0,0,0,0},
+ {mvsgT_ipAddress,ipv4,172,28,12,3,0,0,0,0},
+ {mvsT_msisdn,<<145,148,113,129,0,0,0,0>>}},
+ 1}).
+
+test() ->
+ ?RES = decode_v0_opt(42, ?PDU),
+ ok.
+
+decode_v0_opt(0, Pdu) ->
+ decode_gtpc_msg(Pdu);
+decode_v0_opt(N, Pdu) ->
+ decode_gtpc_msg(Pdu),
+ decode_v0_opt(N-1, Pdu).
+
+%%% --------------------------------------------------------------
+%%% #3.1.2 DECODE GTP-C MESSAGE
+%%% --------------------------------------------------------------
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% Function : decode_gtpc_msg(GTP_C_Message)->
+%%% {ok,Request,ControlDataUs} |
+%%% {fault,Cause,Request,ControlDataUs}
+%%%
+%%% Types : GTP_C_Message = binary(), GTP-C message from SGSN
+%%% Request = record(), Containing decoded request
+%%% ControlDataUS = record(), Containing header info
+%%% Cause = integer(), Error code
+%%%
+%%% Description: This function decodes a binary GTP-C message and
+%%% stores it in a record. Different records are used
+%%% for different message types.
+%%%
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+%%% Create PDP Context Request
+%%% GTP97, SNN=0
+%%% (No SNDCP N-PDU number)
+decode_gtpc_msg(<<0:3,_:4,0:1,16:8,_Length:16,SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,InformationElements/binary>>) ->
+ Errors = #protocolErrors{},
+ {ok,TID2} = tid_internal_storage(TID,[]),
+ EmptyCreateReq = #sesT_createReqV0{tid = TID2,
+ tidRaw = binary_to_list(TID)},
+ case catch decode_ie_create(InformationElements,0,Errors,EmptyCreateReq) of
+ {ok,CreateReq} ->
+ {ok,CreateReq,SequenceNumber};
+ {fault,Cause,CreateReq} ->
+ {fault,Cause,CreateReq,SequenceNumber};
+ {'EXIT',_Reason} ->
+ {fault,193,EmptyCreateReq,SequenceNumber}
+ end;
+
+%%% Update PDP Context Request
+%%% GTP97, SNN=0
+%%% (No SNDCP N-PDU number)
+decode_gtpc_msg(<<0:3,_:4,0:1,18:8,_Length:16,SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,InformationElements/binary>>) ->
+ io:format("hej", []),
+ Errors = #protocolErrors{},
+ {ok,TID2}=tid_internal_storage(TID,[]),
+ EmptyUpdateReq=#sesT_updateReqV0{tid=TID2,
+ tidRaw=binary_to_list(TID)},
+ case catch decode_ie_update(InformationElements,0,Errors,
+ EmptyUpdateReq) of
+ {ok,UpdateReq} ->
+ {ok,UpdateReq,SequenceNumber};
+ {fault,Cause,UpdateReq} ->
+ {fault,Cause,UpdateReq,SequenceNumber};
+ {'EXIT',Reason} ->
+ io:format("hej", []),
+ {fault,193,EmptyUpdateReq,SequenceNumber, Reason}
+ end;
+
+%%% Delete PDP Context Request
+%%% GTP97, SNN=0
+%%% (No SNDCP N-PDU number)
+decode_gtpc_msg(<<0:3,_:4,0:1,20:8,_Length:16,SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,_InformationElements/binary>>) ->
+ {ok,TID2} = tid_internal_storage(TID,[]),
+ DeleteReq = #sesT_deleteReqV0{tid=TID2},
+ {ok,DeleteReq,SequenceNumber};
+
+%%% Delete PDP Context Response
+%%% GTP97, SNN=0
+%%% (No SNDCP N-PDU number)
+decode_gtpc_msg(<<0:3,_:4,0:1,21:8,_Length:16,SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,InformationElements/binary>>) ->
+ {ok,TID2} = tid_internal_storage(TID,[]),
+ EmptyDeleteRes = #sesT_deleteResV0{tid=TID2},
+ case catch decode_ie_delete_res(InformationElements,0,EmptyDeleteRes) of
+ {ok, DeleteRes} ->
+ {ok,DeleteRes,SequenceNumber};
+ {fault,Cause,DeleteRes} ->
+ {fault,Cause,DeleteRes,SequenceNumber};
+ {'EXIT',_Reason} ->
+ {fault,193,EmptyDeleteRes,SequenceNumber}
+ end;
+
+%%% Error handling
+decode_gtpc_msg(_GTP_C_Message) ->
+ {fault}.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% decode_ie_create/4
+%%% Decode information elements for Create PDP Context Request
+
+%%% All elements decoded
+decode_ie_create(<<>>,PresentIEs,Errors,CreateReq) ->
+ %% Check mandatory IE's
+ if
+ (PresentIEs band 16#77D) =/= 16#77D ->
+ {fault,202,CreateReq}; %Mandatory IE missing
+ true -> %OK
+ %% Check errors during decoding
+ case Errors of
+ #protocolErrors{invalidManIE=true} -> %Invalid mandatory IE
+ {fault,201,CreateReq}; %Mandatory IE incorrect
+ #protocolErrors{outOfSequence=true} -> %Out of sequence
+ {fault,193,CreateReq}; %Invalid message format
+ #protocolErrors{incorrectOptIE=true} -> %Incorrect optional IE
+ {fault,203,CreateReq}; %Optional IE incorrect
+ _ -> %OK
+ {ok,CreateReq}
+ end
+ end;
+
+%%% Quality of Service Profile, Mandatory
+decode_ie_create(<<6:8,QoSElement:3/binary-unit:8,Rest/binary>>,PresentIEs,
+ Errors,CreateReq) ->
+ if
+ (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE's, ignore
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000001 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ <<_:2,DelayClass:3,ReliabilityClass:3,
+ PeakThroughput:4,_:1,PrecedenceClass:3,
+ _:3,MeanThroughput:5>> = QoSElement,
+ QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
+ reliabilityClass=ReliabilityClass,
+ peakThroughput=PeakThroughput,
+ precedenceClass=PrecedenceClass,
+ meanThroughput=MeanThroughput},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{qos=QoS},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000001),
+ UpdatedErrors,UpdatedCreateReq);
+ true -> %OK
+ <<_:2,DelayClass:3,ReliabilityClass:3,
+ PeakThroughput:4,_:1,PrecedenceClass:3,
+ _:3,MeanThroughput:5>> = QoSElement,
+ QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
+ reliabilityClass=ReliabilityClass,
+ peakThroughput=PeakThroughput,
+ precedenceClass=PrecedenceClass,
+ meanThroughput=MeanThroughput},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{qos=QoS},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000001),
+ Errors,UpdatedCreateReq)
+ end;
+
+%%% Recovery, Optional
+decode_ie_create(<<14:8,Recovery:8,Rest/binary>>,
+ PresentIEs,Errors,CreateReq) ->
+ if
+ (PresentIEs band 16#00000002) =:= 16#00000002 -> %Repeated IE, ignored
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000002 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{recovery=Recovery},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000002),
+ UpdatedErrors,UpdatedCreateReq);
+ true -> %OK
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{recovery=Recovery},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000002),Errors,
+ UpdatedCreateReq)
+ end;
+
+%%% Selection mode, Mandatory
+decode_ie_create(<<15:8,_:6,SelectionMode:2,Rest/binary>>,PresentIEs,
+ Errors,CreateReq) ->
+ if
+ (PresentIEs band 16#00000004) =:= 16#00000004 -> %Repeated IE, ignored
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000004 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{
+ selMode=selection_mode_internal_storage(SelectionMode)},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000004),
+ UpdatedErrors,UpdatedCreateReq);
+ true -> %OK
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{
+ selMode=selection_mode_internal_storage(SelectionMode)},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000004),Errors,
+ UpdatedCreateReq)
+ end;
+
+%%% Flow Label Data I, Mandatory
+decode_ie_create(<<16:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,CreateReq) ->
+ if
+ (PresentIEs band 16#00000008) =:= 16#00000008 -> %Repeated IE, ignored
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000008 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblData=FlowLabel},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000008),
+ UpdatedErrors,UpdatedCreateReq);
+ true -> %OK
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblData=FlowLabel},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000008),Errors,
+ UpdatedCreateReq)
+ end;
+
+%%% Flow Label Signalling, Mandatory
+decode_ie_create(<<17:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,CreateReq) ->
+ if
+ (PresentIEs band 16#00000010) =:= 16#00000010 -> %Repeated IE, ignored
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000010 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblSig=FlowLabel},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000010),
+ UpdatedErrors,UpdatedCreateReq);
+ true -> %OK
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblSig=FlowLabel},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000010),Errors,
+ UpdatedCreateReq)
+ end;
+
+%%% End User Address, Mandatory
+decode_ie_create(<<128:8,Length:16,More/binary>>,PresentIEs,
+ Errors,CreateReq) ->
+ <<PDPElement:Length/binary-unit:8,Rest/binary>> = More,
+ if
+ (PresentIEs band 16#00000020) =:= 16#00000020 -> %Repeated IE, ignore
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000020 -> %Out of sequence
+ case pdp_addr_internal_storage(PDPElement) of
+ {ok,PDPAddress} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{endUserAdd=PDPAddress},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000020),
+ UpdatedErrors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
+ outOfSequence=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000020),
+ UpdatedErrors,CreateReq)
+ end;
+ true -> %OK
+ case pdp_addr_internal_storage(PDPElement) of
+ {ok,PDPAddress} ->
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{endUserAdd=PDPAddress},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000020),
+ Errors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000020),
+ UpdatedErrors,CreateReq)
+ end
+ end;
+
+%%% Access Point Name, Mandatory
+decode_ie_create(<<131:8,Length:16,More/binary>>,PresentIEs,
+ Errors,CreateReq) ->
+ <<APNElement:Length/binary-unit:8,Rest/binary>> = More,
+ if
+ (PresentIEs band 16#00000040) =:= 16#00000040 -> %Repeated IE, ignore
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000040 -> %Out of sequence
+ case catch apn_internal_storage(APNElement,[]) of
+ {ok,APN} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{accPointName=APN},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000040),
+ UpdatedErrors,UpdatedCreateReq);
+ _ ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true,
+ invalidManIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000040),
+ UpdatedErrors,CreateReq)
+ end;
+ true -> %OK
+ case catch apn_internal_storage(APNElement,[]) of
+ {ok,APN} ->
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{accPointName=APN},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000040),
+ Errors,UpdatedCreateReq);
+ _ ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000040),
+ UpdatedErrors,CreateReq)
+ end
+ end;
+
+%%% Protocol Configuration Options, Optional
+decode_ie_create(<<132:8,Length:16,More/binary>>,PresentIEs,Errors,CreateReq) ->
+ <<ConfigurationElement:Length/binary-unit:8,Rest/binary>> = More,
+ if
+ (PresentIEs band 16#00000080) =:= 16#00000080 -> %Repeated IE, ignore
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000080 -> %Out of sequence
+ case catch pco_internal_storage(ConfigurationElement) of
+ {ok,PCO} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{protConOpt=PCO},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000080),
+ UpdatedErrors,UpdatedCreateReq);
+ _ ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true,
+ incorrectOptIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000080),
+ UpdatedErrors,CreateReq)
+ end;
+ true -> %OK
+ case catch pco_internal_storage(ConfigurationElement) of
+ {ok,PCO} ->
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{protConOpt=PCO},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000080),
+ Errors,UpdatedCreateReq);
+ _ ->
+ UpdatedErrors=Errors#protocolErrors{incorrectOptIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000080),
+ UpdatedErrors,CreateReq)
+ end
+ end;
+
+%%% SGSN Address for signalling, Mandatory OR SGSN Address for user traffic, Mandatory
+decode_ie_create(<<133:8,Length:16,More/binary>>,PresentIEs,
+ Errors,CreateReq) ->
+ <<AddressElement:Length/binary-unit:8,Rest/binary>> = More,
+ if
+ (PresentIEs band 16#00000300) =:= 16#00000300 -> %Repeated IE, ignore
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000200 -> %Out of sequence
+ if
+ (PresentIEs band 16#00000100) =:= 16#00000000 -> %Signalling
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddSig=GSNAddr},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000100),
+ UpdatedErrors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
+ outOfSequence=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000100),
+ UpdatedErrors,CreateReq)
+ end;
+ true -> % User traffic
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddUser=GSNAddr},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000200),
+ UpdatedErrors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
+ outOfSequence=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000200),
+ UpdatedErrors,CreateReq)
+ end
+ end;
+ PresentIEs < 16#00000100 -> %OK, SGSN Address for signalling
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddSig=GSNAddr},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000100),
+ Errors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000100),
+ UpdatedErrors,CreateReq)
+ end;
+ true -> %OK, SGSN Address for user traffic
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddUser=GSNAddr},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000200),
+ Errors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000200),
+ UpdatedErrors,CreateReq)
+ end
+ end;
+
+%%% MSISDN, Mandatory
+decode_ie_create(<<134:8,Length:16,More/binary>>,PresentIEs,
+ Errors,CreateReq) ->
+ <<MSISDNElement:Length/binary-unit:8,Rest/binary>> = More,
+ if
+ (PresentIEs band 16#00000400) =:= 16#00000400 -> %Repeated IE, ignore
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ PresentIEs > 16#00000400 -> %Out of sequence
+ case msisdn_internal_storage(MSISDNElement,[]) of
+ {ok,MSISDN} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{msisdn=MSISDN},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000400),
+ UpdatedErrors,UpdatedCreateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true,invalidManIE=true},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000400),
+ UpdatedErrors,CreateReq)
+ end;
+ true -> %OK
+ UpdatedCreateReq=CreateReq#sesT_createReqV0{msisdn=#mvsT_msisdn{value=MSISDNElement}},
+ decode_ie_create(Rest,(PresentIEs bor 16#00000400),
+ Errors,UpdatedCreateReq)
+
+ end;
+
+%%% Private Extension, Optional
+%%% Not implemented
+
+%%% Error handling, Unexpected or unknown IE
+decode_ie_create(UnexpectedIE,PresentIEs,Errors,CreateReq) ->
+ case check_ie(UnexpectedIE) of
+ {defined_ie,Rest} -> %OK, ignored
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ {handled_ie,Rest} -> %OK, ignored
+ decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
+ {unhandled_ie} -> %Error, abort decoding
+ {fault,193,CreateReq} %Invalid message format
+ end.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% decode_ie_update/4
+%%% Decode information elements for Update PDP Context Request
+
+%%% All elements decoded
+decode_ie_update(<<>>,PresentIEs,Errors,UpdateReq) ->
+ %% Check mandatory IE's
+ if
+ (PresentIEs band 16#3D) =/= 16#3D ->
+ {fault,202,UpdateReq}; %Mandatory IE missing
+ true -> %OK
+ %% Check errors during decoding
+ case Errors of
+ #protocolErrors{invalidManIE=true} -> %Invalid mandatory IE
+ {fault,201,UpdateReq}; %Mandatory IE incorrect
+ #protocolErrors{outOfSequence=true} -> %Out of sequence
+ {fault,193,UpdateReq}; %Invalid message format
+ _ -> %OK
+ {ok,UpdateReq}
+ end
+ end;
+
+%%% Quality of Service Profile, Mandatory
+decode_ie_update(<<6:8,QoSElement:3/binary-unit:8,Rest/binary>>,PresentIEs,
+ Errors,UpdateReq) ->
+ if
+ (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE's, ignore
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ PresentIEs > 16#00000001 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ <<_:2,DelayClass:3,ReliabilityClass:3,
+ PeakThroughput:4,_:1,PrecedenceClass:3,
+ _:3,MeanThroughput:5>> = QoSElement,
+ QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
+ reliabilityClass=ReliabilityClass,
+ peakThroughput=PeakThroughput,
+ precedenceClass=PrecedenceClass,
+ meanThroughput=MeanThroughput},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{qos=QoS},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000001),
+ UpdatedErrors,UpdatedUpdateReq);
+ true -> %OK
+ <<_:2,DelayClass:3,ReliabilityClass:3,
+ PeakThroughput:4,_:1,PrecedenceClass:3,
+ _:3,MeanThroughput:5>> = QoSElement,
+ QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
+ reliabilityClass=ReliabilityClass,
+ peakThroughput=PeakThroughput,
+ precedenceClass=PrecedenceClass,
+ meanThroughput=MeanThroughput},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{qos=QoS},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000001),
+ Errors,UpdatedUpdateReq)
+ end;
+
+%%% Recovery, Optional
+decode_ie_update(<<14:8,Recovery:8,Rest/binary>>,PresentIEs,Errors,UpdateReq) ->
+ if
+ (PresentIEs band 16#00000002) =:= 16#00000002 -> %Repeated IE, ignored
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ PresentIEs > 16#00000002 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{recovery=Recovery},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000002),
+ UpdatedErrors,UpdatedUpdateReq);
+ true -> %OK
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{recovery=Recovery},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000002),Errors,
+ UpdatedUpdateReq)
+ end;
+
+%%% Flow Label Data I, Mandatory
+decode_ie_update(<<16:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,UpdateReq) ->
+ if
+ (PresentIEs band 16#00000004) =:= 16#00000004 -> %Repeated IE, ignored
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ PresentIEs > 16#00000004 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblData=FlowLabel},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000004),
+ UpdatedErrors,UpdatedUpdateReq);
+ true -> %OK
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblData=FlowLabel},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000004),Errors,
+ UpdatedUpdateReq)
+ end;
+
+%%% Flow Label Signalling, Mandatory
+decode_ie_update(<<17:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,UpdateReq) ->
+ if
+ (PresentIEs band 16#00000008) =:= 16#00000008 -> %Repeated IE, ignored
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ PresentIEs > 16#00000008 -> %Out of sequence
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblSig=FlowLabel},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000008),
+ UpdatedErrors,UpdatedUpdateReq);
+ true -> %OK
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblSig=FlowLabel},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000008),Errors,
+ UpdatedUpdateReq)
+ end;
+
+%%% SGSN Address for signalling, Mandatory OR SGSN Address for user traffic, Mandatory
+decode_ie_update(<<133:8,Length:16,More/binary>>,PresentIEs,
+ Errors,UpdateReq) ->
+ <<AddressElement:Length/binary-unit:8,Rest/binary>> = More,
+ if
+ (PresentIEs band 16#00000030) =:= 16#00000030 -> %Repeated IE, ignore
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ PresentIEs > 16#00000020 -> %Out of sequence
+ if
+ (PresentIEs band 16#00000010) =:= 16#00000000 -> %Signalling
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddSig=GSNAddr},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000010),
+ UpdatedErrors,UpdatedUpdateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
+ outOfSequence=true},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000010),
+ UpdatedErrors,UpdateReq)
+ end;
+ true -> % User traffic
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddUser=GSNAddr},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000020),
+ UpdatedErrors,UpdatedUpdateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
+ outOfSequence=true},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000020),
+ UpdatedErrors,UpdateReq)
+ end
+ end;
+ PresentIEs < 16#00000010 -> %OK, SGSN Address for signalling
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddSig=GSNAddr},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000010),
+ Errors,UpdatedUpdateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000010),
+ UpdatedErrors,UpdateReq)
+ end;
+ true -> %OK, SGSN Address for user traffic
+ case gsn_addr_internal_storage(AddressElement) of
+ {ok,GSNAddr} ->
+ UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddUser=GSNAddr},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000020),
+ Errors,UpdatedUpdateReq);
+ {fault} ->
+ UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
+ decode_ie_update(Rest,(PresentIEs bor 16#00000020),
+ UpdatedErrors,UpdateReq)
+ end
+ end;
+
+%%% Private Extension, Optional
+%%% Not implemented
+
+%%% Error handling, Unexpected or unknown IE
+decode_ie_update(UnexpectedIE,PresentIEs,Errors,UpdateReq) ->
+ case check_ie(UnexpectedIE) of
+ {defined_ie,Rest} -> %OK, ignored
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ {handled_ie,Rest} -> %OK, ignored
+ decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
+ {unhandled_ie} -> %Error, abort decoding
+ {fault,193,UpdateReq} %Invalid message format
+ end.
+
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% decode_ie_delete_req/4
+%%% Decode information elements for Delete PDP Context Request
+
+%%% Private Extension, Optional
+%%% Not implemented
+
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% decode_ie_delete_res/4
+%%% Decode information elements for Delete PDP Context Response
+
+%%% All elements decoded
+decode_ie_delete_res(<<>>,PresentIEs,DeleteRes) ->
+ %% Check mandatory IE's
+ if
+ (PresentIEs band 16#0001) =/= 16#0001 ->
+ {fault,202,DeleteRes}; %Mandatory IE missing
+ true -> %OK
+ {ok,DeleteRes}
+ end;
+
+%%% Cause, Mandatory
+decode_ie_delete_res(<<1:8,Cause:8,Rest/binary>>,PresentIEs,DeleteRes) ->
+ if
+ (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE, ignored
+ decode_ie_delete_res(Rest,PresentIEs,DeleteRes);
+ true -> %OK
+ UpdatedDeleteRes=DeleteRes#sesT_deleteResV0{cause=Cause},
+ decode_ie_delete_res(Rest,(PresentIEs bor 16#00000001),
+ UpdatedDeleteRes)
+ end;
+
+%%% Private Extension, Optional
+%%% Not implemented
+
+%%% Error handling, Unexpected or unknown IE
+decode_ie_delete_res(UnexpectedIE,PresentIEs,DeleteRes) ->
+ case check_ie(UnexpectedIE) of
+ {defined_ie,Rest} -> %OK, ignored
+ decode_ie_delete_res(Rest,PresentIEs,DeleteRes);
+ {handled_ie,Rest} -> %OK, ignored
+ decode_ie_delete_res(Rest,PresentIEs,DeleteRes);
+ {unhandled_ie} -> %Error, abort decoding
+ {fault,193,DeleteRes} %Invalid message format
+ end.
+
+%%% --------------------------------------------------------------
+%%% #3.2 COMMON INTERNAL FUNCTIONS
+%%% --------------------------------------------------------------
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% check_ie/1
+%%% Check Information Element, Unexpected or Unknown
+check_ie(<<1:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% IMSI
+check_ie(<<2:8,_:8/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% RAI
+check_ie(<<3:8,_:6/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% TTLI
+check_ie(<<4:8,_:4/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% P-TMSI
+check_ie(<<5:8,_:4/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Quality of Service Profile
+check_ie(<<6:8,_:3/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Reordering Required
+check_ie(<<8:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Authentication Triplet
+check_ie(<<9:8,_:28/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% MAP Cause
+check_ie(<<11:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% P-TMSI Signature
+check_ie(<<12:8,_:3/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% MS Validated
+check_ie(<<13:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Recovery
+check_ie(<<14:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Selection Mode
+check_ie(<<15:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Flow Label Data I
+check_ie(<<16:8,_:16,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Flow Label Signalling
+check_ie(<<17:8,_:16,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Flow Label Data II
+check_ie(<<18:8,_:32,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% MS Not Reachable Reason
+check_ie(<<19:8,_:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% Charging ID
+check_ie(<<127:8,_:4/binary-unit:8,Rest/binary>>) ->
+ {defined_ie,Rest};
+%%% TLV element, skipped using Length
+check_ie(<<1:1,_:7,Length:16,More/binary>>) ->
+ if
+ Length > byte_size(More) ->
+ {unhandled_ie};
+ true ->
+ <<_:Length/binary-unit:8,Rest/binary>> = More,
+ {handled_ie,Rest}
+ end;
+%%% TV element, unknown size. Can not be handled.
+check_ie(_UnhandledIE) ->
+ {unhandled_ie}.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% tid_internal_storage/3
+%%% Convert TID binary to internal datatype
+tid_internal_storage(Bin,_) ->
+ Size = byte_size(Bin) - 1,
+ <<Front:Size/binary,NSAPI:4,DigitN:4>> = Bin,
+ Result =
+ case DigitN of
+ 2#1111 ->
+ #mvsgT_tid{imsi = #mvsgT_imsi{value = Front}, nsapi = NSAPI};
+ _ ->
+ Value = <<Front/binary,2#1111:4,DigitN:4>>,
+ #mvsgT_tid{imsi = #mvsgT_imsi{value = Value}, nsapi = NSAPI}
+ end,
+ {ok,Result}.
+%% tid_internal_storage(<<NSAPI:4,2#1111:4>>,IMSI) ->
+%% {ok,#mvsgT_tid{imsi=#mvsgT_imsi{value=lists:reverse(IMSI)},
+%% nsapi=NSAPI}};
+%% tid_internal_storage(<<NSAPI:4,DigitN:4>>,IMSI) when
+%% DigitN < 10 ->
+%% {ok,#mvsgT_tid{imsi=#mvsgT_imsi{value=lists:reverse([(DigitN bor 2#11110000)|IMSI])},
+%% nsapi=NSAPI}};
+%% tid_internal_storage(<<2#11111111:8,Rest/binary>>,IMSI) ->
+%% tid_internal_storage(Rest,IMSI);
+%% tid_internal_storage(<<2#1111:4,DigitN:4,Rest/binary>>,IMSI) when
+%% DigitN < 10 ->
+%% tid_internal_storage(Rest,[(DigitN bor 2#11110000)|IMSI]);
+%% tid_internal_storage(<<DigitNplus1:4,DigitN:4,Rest/binary>>,IMSI) when
+%% DigitNplus1 < 10,
+%% DigitN < 10 ->
+%% tid_internal_storage(Rest,[((DigitNplus1 bsl 4) bor DigitN)|IMSI]);
+%% tid_internal_storage(_Rest,_IMSI) ->
+%% {fault}. %% Mandatory IE incorrect
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% selection_mode_internal_storage/1
+%%% Convert Selection Mode integer to internal datatype (enum)
+selection_mode_internal_storage(0) ->
+ subscribed;
+selection_mode_internal_storage(1) ->
+ msRequested;
+selection_mode_internal_storage(2) ->
+ sgsnSelected;
+selection_mode_internal_storage(3) ->
+ sgsnSelected.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% pdp_addr_internal_storage/1
+%%% Convert PDP address to internal datatype (record containing
+%%% addresstype and value)
+pdp_addr_internal_storage(<<_:4,0:4,1:8>>) ->
+ {ok,#mvsgT_pdpAddressType{pdpTypeNbr=etsi_ppp,address=[]}};
+pdp_addr_internal_storage(<<_:4,0:4,2:8>>) ->
+ {ok,#mvsgT_pdpAddressType{pdpTypeNbr=etsi_osp_ihoss,address=[]}};
+pdp_addr_internal_storage(<<_:4,1:4,16#21:8>>) ->
+ {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv4,address=[]}};
+pdp_addr_internal_storage(<<_:4,1:4,16#21:8,IP_A:8,IP_B:8,IP_C:8,IP_D:8>>) ->
+ {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv4,
+ address=[IP_A,IP_B,IP_C,IP_D]}};
+pdp_addr_internal_storage(<<_:4,1:4,16#57:8,IP_A:16,IP_B:16,IP_C:16,IP_D:16,
+ IP_E:16,IP_F:16,IP_G:16,IP_H:16>>) ->
+ {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv6,
+ address=[IP_A,IP_B,IP_C,IP_D,IP_E,IP_F,IP_G,IP_H]}};
+pdp_addr_internal_storage(_PDP_ADDR) ->
+ {fault}.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% apn_internal_storage/2
+%%% Convert APN to internal datatype (List containing APN labels)
+apn_internal_storage(<<>>,APN) ->
+ {ok,lists:reverse(APN)};
+apn_internal_storage(<<Length:8,Rest/binary>>,APN) ->
+ <<Label:Length/binary-unit:8,MoreAPNLabels/binary>> = Rest,
+ apn_internal_storage(MoreAPNLabels,[Label|APN]).
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% pco_internal_storage/1
+%%% Convert Protocol Configuration Options to internal datatype.
+%%% Implemented configuration options:
+%%% For PPP:
+%%% LCP - Not implemented
+%%% PAP - Authenticate request
+%%% CHAP - Challenge
+%%% - Response
+%%% IPCP - IP-Address
+%%% For OSP:IHOSS
+%%% Nothing implemented
+pco_internal_storage(<<1:1,_:4,0:3,PPPConfigurationOptions/binary>>) ->
+ case ppp_configuration_options(PPPConfigurationOptions,
+ #masT_pap{exists=false},[],[]) of
+ {ok,PAP,CHAP,IPCP} ->
+ {ok,#masT_protocolConfigOptions{pap=PAP,chap=CHAP,ipcp=IPCP}};
+ {fault} ->
+ {fault}
+ end;
+pco_internal_storage(<<1:1,_:4,1:3,_OSP_IHOSSConfigurationOptions/binary>>) ->
+ {ok,osp_ihoss};
+pco_internal_storage(_UnknownConfigurationOptions) ->
+ {fault}. %% Optional IE incorrect
+
+ppp_configuration_options(<<>>,PAP,CHAP,IPCP) ->
+ {ok,PAP,CHAP,IPCP};
+ppp_configuration_options(<<16#C021:16,Length:8,More/binary>>,PAP,CHAP,IPCP) ->
+ %% LCP - Not implemented
+ <<_LCP:Length/binary-unit:8,Rest/binary>> = More,
+ ppp_configuration_options(Rest,PAP,CHAP,IPCP);
+ppp_configuration_options(<<16#C023:16,_Length:8,1:8,Identifier:8,DataLength:16,
+ More/binary>>,_PAP,CHAP,IPCP) ->
+ %% PAP - Authenticate request
+ ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself
+ <<Data:ActualDataLength/binary-unit:8,Rest/binary>> = More,
+ <<PeerIDLength:8,PeerData/binary>> = Data,
+ <<PeerID:PeerIDLength/binary-unit:8,PasswdLength:8,PasswordData/binary>> = PeerData,
+ <<Password:PasswdLength/binary,_Padding/binary>> = PasswordData,
+ ppp_configuration_options(Rest,#masT_pap{exists=true,code=1,id=Identifier,
+ username=binary_to_list(PeerID),
+ password=binary_to_list(Password)},CHAP,IPCP);
+
+ppp_configuration_options(<<16#C023:16,Length:8,More/binary>>,PAP,CHAP,IPCP) ->
+ %% PAP - Other, not implemented
+ <<_PAP:Length/binary-unit:8,Rest/binary>> = More,
+ ppp_configuration_options(Rest,PAP,CHAP,IPCP);
+ppp_configuration_options(<<16#C223:16,_Length:8,1:8,Identifier:8,DataLength:16,
+ More/binary>>,PAP,CHAP,IPCP) ->
+ %% CHAP - Challenge
+ ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself
+ <<Data:ActualDataLength/binary-unit:8,Rest/binary>> = More,
+ <<ValueSize:8,ValueAndName/binary>> = Data,
+ <<Value:ValueSize/binary-unit:8,Name/binary>> = ValueAndName,
+ ppp_configuration_options(Rest,PAP,[#masT_chap{code=1,id=Identifier,
+ value=binary_to_list(Value),
+ name=binary_to_list(Name)}|CHAP],
+ IPCP);
+ppp_configuration_options(<<16#C223:16,_Length:8,2:8,Identifier:8,DataLength:16,
+ More/binary>>,PAP,CHAP,IPCP) ->
+ %% CHAP - Response
+ ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself
+ <<Data:ActualDataLength/binary-unit:8,Rest/binary>> = More,
+ <<ValueSize:8,ValueAndName/binary>> = Data,
+ <<Value:ValueSize/binary-unit:8,Name/binary>> = ValueAndName,
+ ppp_configuration_options(Rest,PAP,[#masT_chap{code=2,id=Identifier,
+ value=binary_to_list(Value),
+ name=binary_to_list(Name)}|CHAP],
+ IPCP);
+ppp_configuration_options(<<16#C223:16,Length:8,More/binary>>,PAP,CHAP,IPCP) ->
+ %% CHAP - Other, not implemented
+ <<_CHAP:Length/binary-unit:8,Rest/binary>> = More,
+ ppp_configuration_options(Rest,PAP,CHAP,IPCP);
+ppp_configuration_options(<<16#8021:16,_Length:8,1:8,Identifier:8,OptionsLength:16,
+ More/binary>>,PAP,CHAP,IPCP) ->
+ %% IPCP - Configure request
+ ActualOptionsLength=OptionsLength-4, %% OptionsLength includes Code, Identifier and itself
+ <<Options:ActualOptionsLength/binary-unit:8,Rest/binary>> = More,
+ case Options of
+ <<3:8,6:8,A1:8,A2:8,A3:8,A4:8>> ->
+ %% IP Address, version 4
+ ppp_configuration_options(Rest,PAP,CHAP,
+ [#masT_ipcp{exists=true,code=1,
+ id=Identifier,
+ ipcpList=[#masT_ipcpData{type=3,ipAddress=
+ #mvsgT_ipAddress{version=ipv4,
+ a1=A1,a2=A2,
+ a3=A3,a4=A4,
+ a5=0,a6=0,
+ a7=0,a8=0},
+ rawMessage=binary_to_list(Options)}]}|IPCP]);
+ <<129:8,6:8,B1:8,B2:8,B3:8,B4:8>> ->
+ %% IP Address, version 4
+ ppp_configuration_options(Rest,PAP,CHAP,
+ [#masT_ipcp{exists=true,code=1,
+ id=Identifier,
+ ipcpList=[#masT_ipcpData{type=129,ipAddress=
+ #mvsgT_ipAddress{version=ipv4,
+ a1=B1,a2=B2,
+ a3=B3,a4=B4},
+ rawMessage=binary_to_list(Options)}]}|IPCP]);
+
+ <<131:8,6:8,C1:8,C2:8,C3:8,C4:8>> ->
+ %% IP Address, version 4
+ ppp_configuration_options(Rest,PAP,CHAP,
+ [#masT_ipcp{exists=true,code=1,
+ id=Identifier,
+ ipcpList=[#masT_ipcpData{type=131,ipAddress=
+ #mvsgT_ipAddress{version=ipv4,
+ a1=C1,a2=C2,
+ a3=C3,a4=C4},
+ rawMessage=binary_to_list(Options)}]}|IPCP]);
+ _ ->
+ ppp_configuration_options(Rest,PAP,CHAP,IPCP)
+ end;
+ppp_configuration_options(<<_UnknownProtocolID:16,Length:8,More/binary>>,
+ PAP,CHAP,IPCP) ->
+ <<_Skipped:Length/binary-unit:8,Rest/binary>> = More,
+ ppp_configuration_options(Rest,PAP,CHAP,IPCP);
+ppp_configuration_options(_Unhandled,_PAP,_CHAP,_IPCP) ->
+ {fault}.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% gsn_addr_internal_storage/1
+%%% Convert GSN Address to internal datatype
+gsn_addr_internal_storage(<<IP_A:8,IP_B:8,IP_C:8,IP_D:8>>) ->
+ {ok,#mvsgT_ipAddress{version=ipv4,a1=IP_A,a2=IP_B,a3=IP_C,a4=IP_D,a5=0,a6=0,a7=0,a8=0}};
+gsn_addr_internal_storage(<<IP_A:16,IP_B:16,IP_C:16,IP_D:16,
+ IP_E:16,IP_F:16,IP_G:16,IP_H:16>>) ->
+ {ok,#mvsgT_ipAddress{version=ipv6,a1=IP_A,a2=IP_B,a3=IP_C,a4=IP_D,
+ a5=IP_E,a6=IP_F,a7=IP_G,a8=IP_H}};
+gsn_addr_internal_storage(_GSN_ADDR) ->
+ {fault}.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% msisdn_internal_storage/3
+%%% Convert MSISDN binary to internal datatype (TBCD-octet list)
+
+msisdn_internal_storage(<<>>,MSISDN) ->
+ {ok,#mvsT_msisdn{value=lists:reverse(MSISDN)}};
+msisdn_internal_storage(<<2#11111111:8,_Rest/binary>>,MSISDN) ->
+ {ok,#mvsT_msisdn{value=lists:reverse(MSISDN)}};
+msisdn_internal_storage(<<2#1111:4,DigitN:4,_Rest/binary>>,MSISDN) when
+ DigitN < 10 ->
+ {ok,#mvsT_msisdn{value=lists:reverse([(DigitN bor 2#11110000)|MSISDN])}};
+msisdn_internal_storage(<<DigitNplus1:4,DigitN:4,Rest/binary>>,MSISDN) when
+ DigitNplus1 < 10,
+ DigitN < 10 ->
+ NewMSISDN=[((DigitNplus1 bsl 4) bor DigitN)|MSISDN],
+ msisdn_internal_storage(Rest,NewMSISDN);
+msisdn_internal_storage(_Rest,_MSISDN) ->
+ {fault}. %% Mandatory IE incorrect
diff --git a/lib/hipe/test/bs_SUITE_data/bs_decode_extract.hrl b/lib/hipe/test/bs_SUITE_data/bs_decode_extract.hrl
new file mode 100644
index 0000000000..80add514a0
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_decode_extract.hrl
@@ -0,0 +1,91 @@
+-ifndef(EXTDEC_HRL).
+-define(EXTDEC_HRL, true).
+
+-record(protocolErrors,{
+ invalidManIE=false,
+ outOfSequence=false,
+ incorrectOptIE=false}).
+-record(mvsT_msisdn, {value}).
+-record(mvsT_isdnAddress, {value}).
+-record(mvsT_hlrAddress, {value}).
+-record(mvsT_authenticationTriplet, {rand, sres, kc}).
+-record(mvsT_authenticationQuintuplet, {rand, xres, ck, ik, autn}).
+-record(mvsT_resynchInfo, {rand, auts}).
+-record(mvsT_resynch, {label, value}).
+-record(mvsT_storeImsiFault, {label, value}).
+-record(mvsT_additionalImsisResults, {roamingStatus, defaultApnOperatorId, misc1, misc2, misc3}).
+-record(mvsT_pdpActiveRecord, {contextId, nsapi, pdpTypeReq, pdpAddrReq, apnReq, qosReq, pdpTypeInUse, pdpAddressNature, pdpAddressInUse, apnInUse, ggsnAddrInUse, qosNegotiated}).
+-record(mvsgT_rai, {mcc, mnc, lac, rac}).
+-record(mvsgT_lai, {mcc, mnc, lac}).
+-record(mvsgT_errorInd, {dummyElement}).
+-record(mvsgT_deleteRes, {cause}).
+-record(mvsgT_deleteReq, {dummyElement}).
+-record(mvsgT_ptmsi, {value}).
+-record(mvsgT_ddRef, {cid, extId, validity}).
+-record(mvsgT_dpRef, {cid, devId}).
+-record(mvsgT_qualityOfService, {delayClass, relClass, peakThrput, precClass, meanThrput}).
+-record(mvsgT_pdpAddressType, {pdpTypeNbr, address}).
+-record(mvsgT_msNetworkCapability, {gea1, smCapDediccatedChannel, smCapGprsChannel, ucs2Support, ssScreenInd}).
+-record(mvsgT_cellId, {mcc, mnc, lac, rac, ci}).
+-record(mvsgT_ipAddress, {version, a1, a2, a3, a4, a5, a6, a7, a8}).
+-record(mvsgT_restartContextData, {gsn_address, restart_counter}).
+-record(mvsgT_updateRes, {cause, qos, ggsnAddSig, ggsnAddUser, recovery, flowLabDataI, flowLabSig, chargId, optFlags}).
+-record(mvsgT_updateReq, {qos, sgsnAddSig, sgsnAddUser, recovery, flowLabDataI, flowLabSig, otpFlags}).
+-record(mvsgT_imsi, {value}).
+-record(mvsgT_tid, {imsi, nsapi}).
+-record(mvsgT_extQualityOfService, {allocRetention, trfClass, delOrder, delOfErrSDU, maxSDUSize, maxBRUp, maxBRDown, residualBER, sduErrorRatio, transferDelay, traffHandlPrio, guarBRUp, guarBRDown}).
+-record(mvsgT_qualServ, {label, value}).
+-record(sesT_gnDevContextData, {numberOfContext, recoveryInfoArray}).
+-record(sesT_tid, {imsi, nsapi}).
+-record(sesT_gnDevContextDataInfo, {dummy}).
+-record(sesT_teid, {value}).
+-record(sesT_qualityOfServiceV1, {allocRetPriority, delayClass, reliabilityClass, peakThroughput, precedenceClass, meanThroughput, trafficClass, deliveryOrder, delivOfErrSDU, maxSDUsize, maxBrUp, maxBrDown, residualBER, sduErrorRatio, transferDelay, trafficHandlPrio, guaranteedBrUp, guaranteedBrDown}).
+-record(sesT_flowLbl, {value}).
+-record(sesT_qualityOfServiceV0, {delayClass, reliabilityClass, peakThroughput, precedenceClass, meanThroughput}).
+-record(sesT_createReq, {dummy}).
+-record(sesT_createRes, {dummy}).
+-record(sesT_deleteReq, {dummy}).
+-record(sesT_deleteRes, {dummy}).
+-record(sesT_gtid, {imsi, nsapi}).
+-record(sesT_updateReq, {dummy}).
+-record(sesT_updateRes, {dummy}).
+-record(sesT_gcontrolDataUs, {gtpSeqNr, gsnAddress, gtunnelId, gsnPort}).
+-record(sesT_gcontrolDataDs, {gtpSeqNr, gsnAddress, protocol, gtunnelId, flowLabSig, gsnPort}).
+-record(sesT_createResV1, {cause, teidSignalling, teidData, ggsnAddSig, ggsnAddUser, reorderingReq, recovery, chargId, endUserAdd, optFlags, protConOpt, qos}).
+-record(sesT_createReqV1, {qos, sgsnAddSig, sgsnAddUser, selMode, recovery, msisdn, endUserAdd, accPointName, optFlags, protConOpt, imsi, teidData, teidSignalling, nsapi}).
+-record(sesT_deleteReqV1, {teardownInd, nsapi}).
+-record(sesT_deleteResV1, {cause}).
+-record(sesT_updateReqV1, {imsi, recovery, teidData, teidSignalling, nsapi, sgsnAddSig, sgsnAddUser, qos}).
+-record(sesT_updateResV1, {cause, recovery, teidData, teidSignalling, chargId, ggsnAddSig, ggsnAddUser, qos}).
+-record(sesT_deleteReqV0, {tid}).
+-record(sesT_deleteResV0, {tid, cause}).
+-record(sesT_createReqV0, {tid, tidRaw, qos, recovery, selMode, flowLblData, flowLblSig, endUserAdd, accPointName, protConOpt, sgsnAddSig, sgsnAddUser, msisdn}).
+-record(sesT_createResV0, {tid, cause, qos, reorderingReq, recovery, flowLblData, flowLblSig, chargId, endUserAdd, protConOpt, ggsnAddSig, ggsnAddUser}).
+-record(sesT_updateReqV0, {tid, tidRaw, qos, recovery, flowLblData, flowLblSig, sgsnAddSig, sgsnAddUser}).
+-record(sesT_updateResV0, {tid, cause, qos, recovery, flowLblData, flowLblSig, chargId, ggsnAddSig, ggsnAddUser}).
+-record(sesT_echoReq, {dummy}).
+-record(sesT_echoRes, {dummy}).
+-record(sesT_echoReqV1, {dummy}).
+-record(sesT_echoResV1, {recovery}).
+-record(sesT_echoReqV0, {dummy}).
+-record(sesT_echoResV0, {recovery}).
+-record(masT_apnSecurity, {sgsnSel, subscribedSel, userSel, ipSpoofing}).
+-record(masT_radiusServer, {radiusApn, radiusAddress, radiusMepAddress, timer, tries, secret}).
+-record(masT_ipSegment, {startSegAddress, stopSegAddress, netmask}).
+-record(masT_llf, {name, metric, id}).
+-record(masT_apnLink, {ggsnAddress, ipSegList, ipAddressOrigin, llfConnName, mepAddress}).
+-record(masT_ispSubObj, {label, value}).
+-record(masT_ipcpData, {type, ipAddress, rawMessage}).
+-record(masT_ipcp, {exists, code, id, ipcpList}).
+-record(masT_pap, {exists, code, id, username, password}).
+-record(masT_chap, {code, id, value, name}).
+-record(masT_ispDevContextData, {nsapi, ipAddress, apnhandle}).
+-record(masT_protocolConfigOptions, {chap, pap, ipcp}).
+-record(masT_apnRadius, {radiusAddress, timer, tries, secret}).
+-record(masT_outbandRadius, {gwAddress, llfConnName, primRadius, secRadius}).
+-record(masT_radiusPair, {primRadius, secRadius}).
+-record(masT_radiusOpt, {dummyMsisdnAuth, dummyMsisdnAcct, msisdnInAuth, msisdnInAcct, sendFullImsi, sendMccMnc, sendSelMode, sendChargingId, asynchAcct}).
+-record(masT_radiusConfig, {hostApn, authPair, acctList, radiusOptions}).
+-record(masT_apnConfig, {link, security, radiusConfig, primDns, secDns, dhcpAddress, indAcct, indAuth, userNameBasedSelection}).
+
+-endif.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_des.erl b/lib/hipe/test/bs_SUITE_data/bs_des.erl
new file mode 100644
index 0000000000..9c495d37ad
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_des.erl
@@ -0,0 +1,734 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_des.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : An implementation of the DES Encryption/Descryption
+%%% algorithm using Erlang binaries.
+%%%
+%%% Created : 14 Apr 2004
+%%%-------------------------------------------------------------------
+-module(bs_des).
+
+-export([encode/2, decode/2, test/0]).
+
+-define(ITERATIONS, 42). %% for benchmarking use a higher number
+
+test() ->
+ Bin = <<1:64>>,
+ Size= byte_size(Bin),
+ Key = <<4704650607608769871263876:64>>,
+ Jumbled = run_encode(?ITERATIONS, Bin, Key),
+ Unjumbled = run_decode(?ITERATIONS, Jumbled, Key),
+ <<Bin:Size/binary,_/binary>> = Unjumbled,
+ ok.
+
+run_encode(1, Bin, Key) ->
+ encode(Bin, Key);
+run_encode(N, Bin, Key) ->
+ encode(Bin, Key),
+ run_encode(N-1, Bin, Key).
+
+run_decode(1, Bin, Key) ->
+ decode(Bin, Key);
+run_decode(N, Bin, Key) ->
+ decode(Bin, Key),
+ run_decode(N-1, Bin, Key).
+
+encode(Data, Key) ->
+ Keys = schedule(Key),
+ list_to_binary(encode_data(Data, Keys)).
+
+decode(Data, Key) ->
+ Keys = lists:reverse(schedule(Key)),
+ list_to_binary(decode_data(Data, Keys)).
+
+encode_data(<<Data:8/binary, Rest/binary>>, Keys) ->
+ [ipinv(des_core(ip(Data), Keys))|encode_data(Rest, Keys)];
+encode_data(<<Rest/binary>>, Keys) ->
+ case byte_size(Rest) of
+ 0 -> [];
+ X ->
+ Y = 8 - X,
+ Data = <<Rest/binary, 0:Y/integer-unit:8>>,
+ [ipinv(des_core(ip(Data), Keys))]
+ end.
+
+decode_data(<<Data:8/binary, Rest/binary>>, Keys) ->
+ [ipinv(dechiper(ip(Data), Keys))|decode_data(Rest, Keys)];
+decode_data(_, _Keys) ->
+ [].
+
+schedule(Key) ->
+ NewKey = pc1(Key),
+ subkeys(NewKey, 1).
+
+subkeys(_Key, 17) ->
+ [];
+subkeys(Key, N) ->
+ TmpKey =
+ case rotate(N) of
+ 1 ->
+ <<X1:1, L:27, X2:1, R:27>> = Key,
+ <<L:27, X1:1, R:27, X2:1>>;
+ 2 ->
+ <<X1:2, L:26, X2:2, R:26>> = Key,
+ <<L:26, X1:2, R:26, X2:2>>
+ end,
+ [pc2(TmpKey)|subkeys(TmpKey, N+1)].
+
+pc2(<<I1:1, I2:1, I3:1, I4:1, I5:1, I6:1, I7:1, I8:1,
+ _I9:1, I10:1, I11:1, I12:1, I13:1, I14:1, I15:1, I16:1,
+ I17:1, _I18:1, I19:1, I20:1, I21:1, _I22:1, I23:1, I24:1,
+ _I25:1, I26:1, I27:1, I28:1, I29:1, I30:1, I31:1, I32:1,
+ I33:1, I34:1, _I35:1, I36:1, I37:1, _I38:1, I39:1, I40:1,
+ I41:1, I42:1, _I43:1, I44:1, I45:1, I46:1, I47:1, I48:1,
+ I49:1, I50:1, I51:1, I52:1, I53:1, _I54:1, I55:1, I56:1>>) ->
+ <<I14:1, I17:1, I11:1, I24:1, I1:1, I5:1, I3:1, I28:1,
+ I15:1, I6:1, I21:1, I10:1, I23:1, I19:1, I12:1, I4:1,
+ I26:1, I8:1, I16:1, I7:1, I27:1, I20:1, I13:1, I2:1,
+ I41:1, I52:1, I31:1, I37:1, I47:1, I55:1, I30:1, I40:1,
+ I51:1, I45:1, I33:1, I48:1, I44:1, I49:1, I39:1, I56:1,
+ I34:1, I53:1, I46:1, I42:1, I50:1, I36:1, I29:1, I32:1>>.
+
+pc1(<<I1:1, I2:1, I3:1, I4:1, I5:1, I6:1, I7:1, _:1,
+ I9:1, I10:1, I11:1, I12:1, I13:1, I14:1, I15:1, _:1,
+ I17:1, I18:1, I19:1, I20:1, I21:1, I22:1, I23:1, _:1,
+ I25:1, I26:1, I27:1, I28:1, I29:1, I30:1, I31:1, _:1,
+ I33:1, I34:1, I35:1, I36:1, I37:1, I38:1, I39:1, _:1,
+ I41:1, I42:1, I43:1, I44:1, I45:1, I46:1, I47:1, _:1,
+ I49:1, I50:1, I51:1, I52:1, I53:1, I54:1, I55:1, _:1,
+ I57:1, I58:1, I59:1, I60:1, I61:1, I62:1, I63:1, _:1>>) ->
+ <<I57:1, I49:1, I41:1, I33:1, I25:1, I17:1, I9:1, I1:1,
+ I58:1, I50:1, I42:1, I34:1, I26:1, I18:1, I10:1, I2:1,
+ I59:1, I51:1, I43:1, I35:1, I27:1, I19:1, I11:1, I3:1,
+ I60:1, I52:1, I44:1, I36:1, I63:1, I55:1, I47:1, I39:1,
+ I31:1, I23:1, I15:1, I7:1, I62:1, I54:1, I46:1, I38:1,
+ I30:1, I22:1, I14:1, I6:1, I61:1, I53:1, I45:1, I37:1,
+ I29:1, I21:1, I13:1, I5:1, I28:1, I20:1, I12:1, I4:1>>.
+
+ip(<<I1:1, I2:1, I3:1, I4:1, I5:1, I6:1, I7:1, I8:1,
+ I9:1, I10:1, I11:1, I12:1, I13:1, I14:1, I15:1, I16:1,
+ I17:1, I18:1, I19:1, I20:1, I21:1, I22:1, I23:1, I24:1,
+ I25:1, I26:1, I27:1, I28:1, I29:1, I30:1, I31:1, I32:1,
+ I33:1, I34:1, I35:1, I36:1, I37:1, I38:1, I39:1, I40:1,
+ I41:1, I42:1, I43:1, I44:1, I45:1, I46:1, I47:1, I48:1,
+ I49:1, I50:1, I51:1, I52:1, I53:1, I54:1, I55:1, I56:1,
+ I57:1, I58:1, I59:1, I60:1, I61:1, I62:1, I63:1, I64:1>>) ->
+ <<I58:1, I50:1, I42:1, I34:1, I26:1, I18:1, I10:1, I2:1,
+ I60:1, I52:1, I44:1, I36:1, I28:1, I20:1, I12:1, I4:1,
+ I62:1, I54:1, I46:1, I38:1, I30:1, I22:1, I14:1, I6:1,
+ I64:1, I56:1, I48:1, I40:1, I32:1, I24:1, I16:1, I8:1,
+ I57:1, I49:1, I41:1, I33:1, I25:1, I17:1, I9:1, I1:1,
+ I59:1, I51:1, I43:1, I35:1, I27:1, I19:1, I11:1, I3:1,
+ I61:1, I53:1, I45:1, I37:1, I29:1, I21:1, I13:1, I5:1,
+ I63:1, I55:1, I47:1, I39:1, I31:1, I23:1, I15:1, I7:1>>.
+
+ipinv(<<I58:1, I50:1, I42:1, I34:1, I26:1, I18:1, I10:1, I2:1,
+ I60:1, I52:1, I44:1, I36:1, I28:1, I20:1, I12:1, I4:1,
+ I62:1, I54:1, I46:1, I38:1, I30:1, I22:1, I14:1, I6:1,
+ I64:1, I56:1, I48:1, I40:1, I32:1, I24:1, I16:1, I8:1,
+ I57:1, I49:1, I41:1, I33:1, I25:1, I17:1, I9:1, I1:1,
+ I59:1, I51:1, I43:1, I35:1, I27:1, I19:1, I11:1, I3:1,
+ I61:1, I53:1, I45:1, I37:1, I29:1, I21:1, I13:1, I5:1,
+ I63:1, I55:1, I47:1, I39:1, I31:1, I23:1, I15:1, I7:1>>) ->
+ <<I1:1, I2:1, I3:1, I4:1, I5:1, I6:1, I7:1, I8:1,
+ I9:1, I10:1, I11:1, I12:1, I13:1, I14:1, I15:1, I16:1,
+ I17:1, I18:1, I19:1, I20:1, I21:1, I22:1, I23:1, I24:1,
+ I25:1, I26:1, I27:1, I28:1, I29:1, I30:1, I31:1, I32:1,
+ I33:1, I34:1, I35:1, I36:1, I37:1, I38:1, I39:1, I40:1,
+ I41:1, I42:1, I43:1, I44:1, I45:1, I46:1, I47:1, I48:1,
+ I49:1, I50:1, I51:1, I52:1, I53:1, I54:1, I55:1, I56:1,
+ I57:1, I58:1, I59:1, I60:1, I61:1, I62:1, I63:1, I64:1>>.
+
+dechiper(<<L:4/binary, R:4/binary>>, Keys) ->
+ dechiper(L, R, Keys, 16).
+
+dechiper(L, R, [], 0) ->
+ <<L:4/binary, R:4/binary>>;
+dechiper(L, R, [Key|Rest], I) ->
+ NewL = ebit(L),
+ XorL = xor48(NewL, Key),
+ Sboxed = sboxing(XorL),
+ Ped = p(Sboxed),
+ EndL = xor32(Ped, R),
+ dechiper(EndL, L, Rest, I-1).
+
+des_core(<<L:4/binary, R:4/binary>>, Keys) ->
+ des_core(L, R, Keys, 0).
+
+des_core(L, R, [], 16) ->
+ <<L:4/binary, R:4/binary>>;
+des_core(L, R, [Key|Rest], I) when I<16 ->
+ NewR = ebit(R),
+ XorR = xor48(NewR, Key),
+ Sboxed = sboxing(XorR),
+ Ped = p(Sboxed),
+ EndR = xor32(Ped, L),
+ des_core(R, EndR, Rest, I+1).
+
+ebit(<<I1:1, I2:2, I3:2,I4:2,I5:2,I6:2,
+ I7:2,I8:2,I9:2,I10:2,I11:2,I12:2,
+ I13:2,I14:2,I15:2,I16:2,I17:1>>) ->
+ <<I17:1, I1:1, I2:2, I3:2, I3:2,
+ I4:2, I5:2, I5:2, I6:2,
+ I7:2, I7:2, I8:2, I9:2,
+ I9:2, I10:2, I11:2, I11:2,
+ I12:2, I13:2, I13:2, I14:2,
+ I15:2, I15:2, I16:2, I17:1, I1:1>>.
+
+p(<<I1:1, I2:1, I3:1, I4:1, I5:1, I6:1, I7:1, I8:1,
+ I9:1, I10:1, I11:1, I12:1, I13:1, I14:1, I15:1, I16:1,
+ I17:1, I18:1, I19:1, I20:1, I21:1, I22:1, I23:1, I24:1,
+ I25:1, I26:1, I27:1, I28:1, I29:1, I30:1, I31:1, I32:1>>) ->
+ <<I16:1, I7:1, I20:1, I21:1, I29:1, I12:1, I28:1, I17:1,
+ I1:1, I15:1, I23:1, I26:1, I5:1, I18:1, I31:1, I10:1,
+ I2:1, I8:1, I24:1, I14:1, I32:1, I27:1, I3:1, I9:1,
+ I19:1, I13:1, I30:1, I6:1, I22:1, I11:1, I4:1, I25:1>>.
+
+rotate(1) -> 1;
+rotate(2) -> 1;
+rotate(9) -> 1;
+rotate(16) -> 1;
+rotate(N) when N>0, N<17 -> 2.
+
+%% xor64(<<I1:16, I2:16, I3:16, I4:16>>,<<J1:16, J2:16, J3:16, J4:16>>) ->
+%% K1 = I1 bxor J1,
+%% K2 = I2 bxor J2,
+%% K3 = I3 bxor J3,
+%% K4 = I4 bxor J4,
+%% <<K1:16, K2:16, K3:16, K4:16>>.
+
+xor48(<<I1:16, I2:16, I3:16>>,<<J1:16, J2:16, J3:16>>) ->
+ K1 = I1 bxor J1,
+ K2 = I2 bxor J2,
+ K3 = I3 bxor J3,
+ <<K1:16, K2:16, K3:16>>.
+
+xor32(<<I1:16, I2:16>>,<<J1:16, J2:16>>) ->
+ K1 = I1 bxor J1,
+ K2 = I2 bxor J2,
+ <<K1:16, K2:16>>.
+
+sboxing(<<A1:6, A2:6, A3:6, A4:6, A5:6, A6:6, A7:6, A8:6>>) ->
+ S1 = sbox(A1, 1),
+ S2 = sbox(A2, 2),
+ S3 = sbox(A3, 3),
+ S4 = sbox(A4, 4),
+ S5 = sbox(A5, 5),
+ S6 = sbox(A6, 6),
+ S7 = sbox(A7, 7),
+ S8 = sbox(A8, 8),
+ <<S1:4,S2:4,S3:4,S4:4,S5:4,S6:4,S7:4,S8:4>>.
+
+sbox(0,1) -> 14;
+sbox(1,1) -> 0;
+sbox(2,1) -> 4;
+sbox(3,1) -> 15;
+sbox(4,1) -> 13;
+sbox(5,1) -> 7;
+sbox(6,1) -> 1;
+sbox(7,1) -> 4;
+sbox(8,1) -> 2;
+sbox(9,1) -> 14;
+sbox(10,1) -> 15;
+sbox(11,1) -> 2;
+sbox(12,1) -> 11;
+sbox(13,1) -> 13;
+sbox(14,1) -> 8;
+sbox(15,1) -> 1;
+sbox(16,1) -> 3;
+sbox(17,1) -> 10;
+sbox(18,1) -> 10;
+sbox(19,1) -> 6;
+sbox(20,1) -> 6;
+sbox(21,1) -> 12;
+sbox(22,1) -> 12;
+sbox(23,1) -> 11;
+sbox(24,1) -> 5;
+sbox(25,1) -> 9;
+sbox(26,1) -> 9;
+sbox(27,1) -> 5;
+sbox(28,1) -> 0;
+sbox(29,1) -> 3;
+sbox(30,1) -> 7;
+sbox(31,1) -> 8;
+sbox(32,1) -> 4;
+sbox(33,1) -> 15;
+sbox(34,1) -> 1;
+sbox(35,1) -> 12;
+sbox(36,1) -> 14;
+sbox(37,1) -> 8;
+sbox(38,1) -> 8;
+sbox(39,1) -> 2;
+sbox(40,1) -> 13;
+sbox(41,1) -> 4;
+sbox(42,1) -> 6;
+sbox(43,1) -> 9;
+sbox(44,1) -> 2;
+sbox(45,1) -> 1;
+sbox(46,1) -> 11;
+sbox(47,1) -> 7;
+sbox(48,1) -> 15;
+sbox(49,1) -> 5;
+sbox(50,1) -> 12;
+sbox(51,1) -> 11;
+sbox(52,1) -> 9;
+sbox(53,1) -> 3;
+sbox(54,1) -> 7;
+sbox(55,1) -> 14;
+sbox(56,1) -> 3;
+sbox(57,1) -> 10;
+sbox(58,1) -> 10;
+sbox(59,1) -> 0;
+sbox(60,1) -> 5;
+sbox(61,1) -> 6;
+sbox(62,1) -> 0;
+sbox(63,1) -> 13;
+sbox(0,2) -> 15;
+sbox(1,2) -> 3;
+sbox(2,2) -> 1;
+sbox(3,2) -> 13;
+sbox(4,2) -> 8;
+sbox(5,2) -> 4;
+sbox(6,2) -> 14;
+sbox(7,2) -> 7;
+sbox(8,2) -> 6;
+sbox(9,2) -> 15;
+sbox(10,2) -> 11;
+sbox(11,2) -> 2;
+sbox(12,2) -> 3;
+sbox(13,2) -> 8;
+sbox(14,2) -> 4;
+sbox(15,2) -> 14;
+sbox(16,2) -> 9;
+sbox(17,2) -> 12;
+sbox(18,2) -> 7;
+sbox(19,2) -> 0;
+sbox(20,2) -> 2;
+sbox(21,2) -> 1;
+sbox(22,2) -> 13;
+sbox(23,2) -> 10;
+sbox(24,2) -> 12;
+sbox(25,2) -> 6;
+sbox(26,2) -> 0;
+sbox(27,2) -> 9;
+sbox(28,2) -> 5;
+sbox(29,2) -> 11;
+sbox(30,2) -> 10;
+sbox(31,2) -> 5;
+sbox(32,2) -> 0;
+sbox(33,2) -> 13;
+sbox(34,2) -> 14;
+sbox(35,2) -> 8;
+sbox(36,2) -> 7;
+sbox(37,2) -> 10;
+sbox(38,2) -> 11;
+sbox(39,2) -> 1;
+sbox(40,2) -> 10;
+sbox(41,2) -> 3;
+sbox(42,2) -> 4;
+sbox(43,2) -> 15;
+sbox(44,2) -> 13;
+sbox(45,2) -> 4;
+sbox(46,2) -> 1;
+sbox(47,2) -> 2;
+sbox(48,2) -> 5;
+sbox(49,2) -> 11;
+sbox(50,2) -> 8;
+sbox(51,2) -> 6;
+sbox(52,2) -> 12;
+sbox(53,2) -> 7;
+sbox(54,2) -> 6;
+sbox(55,2) -> 12;
+sbox(56,2) -> 9;
+sbox(57,2) -> 0;
+sbox(58,2) -> 3;
+sbox(59,2) -> 5;
+sbox(60,2) -> 2;
+sbox(61,2) -> 14;
+sbox(62,2) -> 15;
+sbox(63,2) -> 9;
+sbox(0,3) -> 10;
+sbox(1,3) -> 13;
+sbox(2,3) -> 0;
+sbox(3,3) -> 7;
+sbox(4,3) -> 9;
+sbox(5,3) -> 0;
+sbox(6,3) -> 14;
+sbox(7,3) -> 9;
+sbox(8,3) -> 6;
+sbox(9,3) -> 3;
+sbox(10,3) -> 3;
+sbox(11,3) -> 4;
+sbox(12,3) -> 15;
+sbox(13,3) -> 6;
+sbox(14,3) -> 5;
+sbox(15,3) -> 10;
+sbox(16,3) -> 1;
+sbox(17,3) -> 2;
+sbox(18,3) -> 13;
+sbox(19,3) -> 8;
+sbox(20,3) -> 12;
+sbox(21,3) -> 5;
+sbox(22,3) -> 7;
+sbox(23,3) -> 14;
+sbox(24,3) -> 11;
+sbox(25,3) -> 12;
+sbox(26,3) -> 4;
+sbox(27,3) -> 11;
+sbox(28,3) -> 2;
+sbox(29,3) -> 15;
+sbox(30,3) -> 8;
+sbox(31,3) -> 1;
+sbox(32,3) -> 13;
+sbox(33,3) -> 1;
+sbox(34,3) -> 6;
+sbox(35,3) -> 10;
+sbox(36,3) -> 4;
+sbox(37,3) -> 13;
+sbox(38,3) -> 9;
+sbox(39,3) -> 0;
+sbox(40,3) -> 8;
+sbox(41,3) -> 6;
+sbox(42,3) -> 15;
+sbox(43,3) -> 9;
+sbox(44,3) -> 3;
+sbox(45,3) -> 8;
+sbox(46,3) -> 0;
+sbox(47,3) -> 7;
+sbox(48,3) -> 11;
+sbox(49,3) -> 4;
+sbox(50,3) -> 1;
+sbox(51,3) -> 15;
+sbox(52,3) -> 2;
+sbox(53,3) -> 14;
+sbox(54,3) -> 12;
+sbox(55,3) -> 3;
+sbox(56,3) -> 5;
+sbox(57,3) -> 11;
+sbox(58,3) -> 10;
+sbox(59,3) -> 5;
+sbox(60,3) -> 14;
+sbox(61,3) -> 2;
+sbox(62,3) -> 7;
+sbox(63,3) -> 12;
+sbox(0,4) -> 7;
+sbox(1,4) -> 13;
+sbox(2,4) -> 13;
+sbox(3,4) -> 8;
+sbox(4,4) -> 14;
+sbox(5,4) -> 11;
+sbox(6,4) -> 3;
+sbox(7,4) -> 5;
+sbox(8,4) -> 0;
+sbox(9,4) -> 6;
+sbox(10,4) -> 6;
+sbox(11,4) -> 15;
+sbox(12,4) -> 9;
+sbox(13,4) -> 0;
+sbox(14,4) -> 10;
+sbox(15,4) -> 3;
+sbox(16,4) -> 1;
+sbox(17,4) -> 4;
+sbox(18,4) -> 2;
+sbox(19,4) -> 7;
+sbox(20,4) -> 8;
+sbox(21,4) -> 2;
+sbox(22,4) -> 5;
+sbox(23,4) -> 12;
+sbox(24,4) -> 11;
+sbox(25,4) -> 1;
+sbox(26,4) -> 12;
+sbox(27,4) -> 10;
+sbox(28,4) -> 4;
+sbox(29,4) -> 14;
+sbox(30,4) -> 15;
+sbox(31,4) -> 9;
+sbox(32,4) -> 10;
+sbox(33,4) -> 3;
+sbox(34,4) -> 6;
+sbox(35,4) -> 15;
+sbox(36,4) -> 9;
+sbox(37,4) -> 0;
+sbox(38,4) -> 0;
+sbox(39,4) -> 6;
+sbox(40,4) -> 12;
+sbox(41,4) -> 10;
+sbox(42,4) -> 11;
+sbox(43,4) -> 1;
+sbox(44,4) -> 7;
+sbox(45,4) -> 13;
+sbox(46,4) -> 13;
+sbox(47,4) -> 8;
+sbox(48,4) -> 15;
+sbox(49,4) -> 9;
+sbox(50,4) -> 1;
+sbox(51,4) -> 4;
+sbox(52,4) -> 3;
+sbox(53,4) -> 5;
+sbox(54,4) -> 14;
+sbox(55,4) -> 11;
+sbox(56,4) -> 5;
+sbox(57,4) -> 12;
+sbox(58,4) -> 2;
+sbox(59,4) -> 7;
+sbox(60,4) -> 8;
+sbox(61,4) -> 2;
+sbox(62,4) -> 4;
+sbox(63,4) -> 14;
+sbox(0,5) -> 2;
+sbox(1,5) -> 14;
+sbox(2,5) -> 12;
+sbox(3,5) -> 11;
+sbox(4,5) -> 4;
+sbox(5,5) -> 2;
+sbox(6,5) -> 1;
+sbox(7,5) -> 12;
+sbox(8,5) -> 7;
+sbox(9,5) -> 4;
+sbox(10,5) -> 10;
+sbox(11,5) -> 7;
+sbox(12,5) -> 11;
+sbox(13,5) -> 13;
+sbox(14,5) -> 6;
+sbox(15,5) -> 1;
+sbox(16,5) -> 8;
+sbox(17,5) -> 5;
+sbox(18,5) -> 5;
+sbox(19,5) -> 0;
+sbox(20,5) -> 3;
+sbox(21,5) -> 15;
+sbox(22,5) -> 15;
+sbox(23,5) -> 10;
+sbox(24,5) -> 13;
+sbox(25,5) -> 3;
+sbox(26,5) -> 0;
+sbox(27,5) -> 9;
+sbox(28,5) -> 14;
+sbox(29,5) -> 8;
+sbox(30,5) -> 9;
+sbox(31,5) -> 6;
+sbox(32,5) -> 4;
+sbox(33,5) -> 11;
+sbox(34,5) -> 2;
+sbox(35,5) -> 8;
+sbox(36,5) -> 1;
+sbox(37,5) -> 12;
+sbox(38,5) -> 11;
+sbox(39,5) -> 7;
+sbox(40,5) -> 10;
+sbox(41,5) -> 1;
+sbox(42,5) -> 13;
+sbox(43,5) -> 14;
+sbox(44,5) -> 7;
+sbox(45,5) -> 2;
+sbox(46,5) -> 8;
+sbox(47,5) -> 13;
+sbox(48,5) -> 15;
+sbox(49,5) -> 6;
+sbox(50,5) -> 9;
+sbox(51,5) -> 15;
+sbox(52,5) -> 12;
+sbox(53,5) -> 0;
+sbox(54,5) -> 5;
+sbox(55,5) -> 9;
+sbox(56,5) -> 6;
+sbox(57,5) -> 10;
+sbox(58,5) -> 3;
+sbox(59,5) -> 4;
+sbox(60,5) -> 0;
+sbox(61,5) -> 5;
+sbox(62,5) -> 14;
+sbox(63,5) -> 3;
+sbox(0,6) -> 12;
+sbox(1,6) -> 10;
+sbox(2,6) -> 1;
+sbox(3,6) -> 15;
+sbox(4,6) -> 10;
+sbox(5,6) -> 4;
+sbox(6,6) -> 15;
+sbox(7,6) -> 2;
+sbox(8,6) -> 9;
+sbox(9,6) -> 7;
+sbox(10,6) -> 2;
+sbox(11,6) -> 12;
+sbox(12,6) -> 6;
+sbox(13,6) -> 9;
+sbox(14,6) -> 8;
+sbox(15,6) -> 5;
+sbox(16,6) -> 0;
+sbox(17,6) -> 6;
+sbox(18,6) -> 13;
+sbox(19,6) -> 1;
+sbox(20,6) -> 3;
+sbox(21,6) -> 13;
+sbox(22,6) -> 4;
+sbox(23,6) -> 14;
+sbox(24,6) -> 14;
+sbox(25,6) -> 0;
+sbox(26,6) -> 7;
+sbox(27,6) -> 11;
+sbox(28,6) -> 5;
+sbox(29,6) -> 3;
+sbox(30,6) -> 11;
+sbox(31,6) -> 8;
+sbox(32,6) -> 9;
+sbox(33,6) -> 4;
+sbox(34,6) -> 14;
+sbox(35,6) -> 3;
+sbox(36,6) -> 15;
+sbox(37,6) -> 2;
+sbox(38,6) -> 5;
+sbox(39,6) -> 12;
+sbox(40,6) -> 2;
+sbox(41,6) -> 9;
+sbox(42,6) -> 8;
+sbox(43,6) -> 5;
+sbox(44,6) -> 12;
+sbox(45,6) -> 15;
+sbox(46,6) -> 3;
+sbox(47,6) -> 10;
+sbox(48,6) -> 7;
+sbox(49,6) -> 11;
+sbox(50,6) -> 0;
+sbox(51,6) -> 14;
+sbox(52,6) -> 4;
+sbox(53,6) -> 1;
+sbox(54,6) -> 10;
+sbox(55,6) -> 7;
+sbox(56,6) -> 1;
+sbox(57,6) -> 6;
+sbox(58,6) -> 13;
+sbox(59,6) -> 0;
+sbox(60,6) -> 11;
+sbox(61,6) -> 8;
+sbox(62,6) -> 6;
+sbox(63,6) -> 13;
+sbox(0,7) -> 4;
+sbox(1,7) -> 13;
+sbox(2,7) -> 11;
+sbox(3,7) -> 0;
+sbox(4,7) -> 2;
+sbox(5,7) -> 11;
+sbox(6,7) -> 14;
+sbox(7,7) -> 7;
+sbox(8,7) -> 15;
+sbox(9,7) -> 4;
+sbox(10,7) -> 0;
+sbox(11,7) -> 9;
+sbox(12,7) -> 8;
+sbox(13,7) -> 1;
+sbox(14,7) -> 13;
+sbox(15,7) -> 10;
+sbox(16,7) -> 3;
+sbox(17,7) -> 14;
+sbox(18,7) -> 12;
+sbox(19,7) -> 3;
+sbox(20,7) -> 9;
+sbox(21,7) -> 5;
+sbox(22,7) -> 7;
+sbox(23,7) -> 12;
+sbox(24,7) -> 5;
+sbox(25,7) -> 2;
+sbox(26,7) -> 10;
+sbox(27,7) -> 15;
+sbox(28,7) -> 6;
+sbox(29,7) -> 8;
+sbox(30,7) -> 1;
+sbox(31,7) -> 6;
+sbox(32,7) -> 1;
+sbox(33,7) -> 6;
+sbox(34,7) -> 4;
+sbox(35,7) -> 11;
+sbox(36,7) -> 11;
+sbox(37,7) -> 13;
+sbox(38,7) -> 13;
+sbox(39,7) -> 8;
+sbox(40,7) -> 12;
+sbox(41,7) -> 1;
+sbox(42,7) -> 3;
+sbox(43,7) -> 4;
+sbox(44,7) -> 7;
+sbox(45,7) -> 10;
+sbox(46,7) -> 14;
+sbox(47,7) -> 7;
+sbox(48,7) -> 10;
+sbox(49,7) -> 9;
+sbox(50,7) -> 15;
+sbox(51,7) -> 5;
+sbox(52,7) -> 6;
+sbox(53,7) -> 0;
+sbox(54,7) -> 8;
+sbox(55,7) -> 15;
+sbox(56,7) -> 0;
+sbox(57,7) -> 14;
+sbox(58,7) -> 5;
+sbox(59,7) -> 2;
+sbox(60,7) -> 9;
+sbox(61,7) -> 3;
+sbox(62,7) -> 2;
+sbox(63,7) -> 12;
+sbox(0,8) -> 13;
+sbox(1,8) -> 1;
+sbox(2,8) -> 2;
+sbox(3,8) -> 15;
+sbox(4,8) -> 8;
+sbox(5,8) -> 13;
+sbox(6,8) -> 4;
+sbox(7,8) -> 8;
+sbox(8,8) -> 6;
+sbox(9,8) -> 10;
+sbox(10,8) -> 15;
+sbox(11,8) -> 3;
+sbox(12,8) -> 11;
+sbox(13,8) -> 7;
+sbox(14,8) -> 1;
+sbox(15,8) -> 4;
+sbox(16,8) -> 10;
+sbox(17,8) -> 12;
+sbox(18,8) -> 9;
+sbox(19,8) -> 5;
+sbox(20,8) -> 3;
+sbox(21,8) -> 6;
+sbox(22,8) -> 14;
+sbox(23,8) -> 11;
+sbox(24,8) -> 5;
+sbox(25,8) -> 0;
+sbox(26,8) -> 0;
+sbox(27,8) -> 14;
+sbox(28,8) -> 12;
+sbox(29,8) -> 9;
+sbox(30,8) -> 7;
+sbox(31,8) -> 2;
+sbox(32,8) -> 7;
+sbox(33,8) -> 2;
+sbox(34,8) -> 11;
+sbox(35,8) -> 1;
+sbox(36,8) -> 4;
+sbox(37,8) -> 14;
+sbox(38,8) -> 1;
+sbox(39,8) -> 7;
+sbox(40,8) -> 9;
+sbox(41,8) -> 4;
+sbox(42,8) -> 12;
+sbox(43,8) -> 10;
+sbox(44,8) -> 14;
+sbox(45,8) -> 8;
+sbox(46,8) -> 2;
+sbox(47,8) -> 13;
+sbox(48,8) -> 0;
+sbox(49,8) -> 15;
+sbox(50,8) -> 6;
+sbox(51,8) -> 12;
+sbox(52,8) -> 10;
+sbox(53,8) -> 9;
+sbox(54,8) -> 13;
+sbox(55,8) -> 0;
+sbox(56,8) -> 15;
+sbox(57,8) -> 3;
+sbox(58,8) -> 3;
+sbox(59,8) -> 5;
+sbox(60,8) -> 5;
+sbox(61,8) -> 6;
+sbox(62,8) -> 8;
+sbox(63,8) -> 11.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_extract.erl b/lib/hipe/test/bs_SUITE_data/bs_extract.erl
new file mode 100644
index 0000000000..0492689fa8
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_extract.erl
@@ -0,0 +1,94 @@
+%% -*- erlang-indent-level: 2 -*-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Among testing other things, this module shows why performing LCM on
+%% SPARC is currently problematic. SPARC does not mark untagged values
+%% as dead when they are live over function calls which in turn causes
+%% them to be traced by the garbage collector leading to crashes.
+%%
+%% A simple way to get this behaviour is to compile just the function
+%%
+%% {bsextract,tid_internal_storage,2}
+%%
+%% with the compiler option "rtl_lcm" on and without.
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(bs_extract).
+
+-export([test/0]).
+
+-include("bs_decode_extract.hrl").
+
+-define(PDU, <<30,16,0,90,0,1,0,0,255,255,255,255,81,67,101,7,0,0,0,96,
+ 6,12,146,18,14,0,15,252,16,0,0,17,0,0,128,0,2,241,33,131,
+ 0,20,7,97,112,110,48,49,51,97,8,101,114,105,99,115,115,
+ 111,110,2,115,101,132,0,20,128,192,35,16,1,5,0,16,5,117,
+ 115,101,114,53,5,112,97,115,115,53,133,0,4,172,28,12,1,
+ 133,0,4,172,28,12,3,134,0,8,145,148,113,129,0,0,0,0>>).
+
+-define(RES, {ok, {mvsgT_imsi, <<81,67,101,7,0,0,0,240>>}}).
+
+test() ->
+ ?RES = extract_v0_opt(1000, ?PDU),
+ ok.
+
+extract_v0_opt(0, Pdu) ->
+ get_external_id(Pdu);
+extract_v0_opt(N, Pdu) ->
+ {ok,_} = get_external_id(Pdu),
+ extract_v0_opt(N-1, Pdu).
+
+get_external_id(<<0:3,_:4,0:1,1:8,_Length:16,SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ _TID:8/binary-unit:8,_InformationElements/binary>>) ->
+ {echo,#sesT_echoReqV0{},SequenceNumber};
+%% Create PDP Context Request
+%% GTP97, SNN=0
+%% (No SNDCP N-PDU number)
+get_external_id(<<0:3,_:4,0:1,16:8,_Length:16,_SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,_InformationElements/binary>>) ->
+ {ok,_IMSI} = extract_imsi(TID);
+%%% Update PDP Context Request
+%%% GTP97, SNN=0
+%%% (No SNDCP N-PDU number)
+get_external_id(<<0:3,_:4,0:1,18:8,_Length:16,_SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,_InformationElements/binary>>) ->
+ {ok,_IMSI} = extract_imsi(TID);
+%%% Delete PDP Context Request
+%%% GTP97, SNN=0
+%%% (No SNDCP N-PDU number)
+get_external_id(<<0:3,_:4,0:1,20:8,_Length:16,_SequenceNumber:16,
+ _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
+ TID:8/binary-unit:8,_InformationElements/binary>>) ->
+ {ok,_IMSI} = extract_imsi(TID);
+%%% Error handling: GTP Message Too Short
+%%% Error handling: Unknown GTP Signalling message.
+%%% Error handling: Unexpected GTP Signalling message.
+get_external_id(_GTP_Message) ->
+ fault.
+
+%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%% extract_imsi/1
+%% Get the IMSI element from TID
+extract_imsi(TID) ->
+ {ok,#mvsgT_tid{imsi=IMSI}} = tid_internal_storage(TID,[]),
+ {ok,IMSI}.
+
+%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+%%% tid_internal_storage/3
+%%% Convert TID binary to internal datatype
+tid_internal_storage(Bin,_) ->
+ Size = byte_size(Bin) - 1,
+ <<Front:Size/binary,NSAPI:4,DigitN:4>> = Bin,
+ Result =
+ case DigitN of
+ 2#1111 ->
+ #mvsgT_tid{imsi = #mvsgT_imsi{value=Front}, nsapi = NSAPI};
+ _ ->
+ Value = <<Front/binary,2#1111:4,DigitN:4>>,
+ #mvsgT_tid{imsi = #mvsgT_imsi{value = Value}, nsapi = NSAPI}
+ end,
+ {ok,Result}.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_flatb.erl b/lib/hipe/test/bs_SUITE_data/bs_flatb.erl
new file mode 100644
index 0000000000..6163917965
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_flatb.erl
@@ -0,0 +1,29 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------------
+%% Program which resulted in a badarg crash when compiled to native code.
+%% The problem was that hipe_icode_primops was stating that the primop
+%% {bs_start_match, ok_matchstate} could not fail which made the icode_type
+%% pass removing the third clause of flatb/1.
+%%
+%% (The program was working correctly with hipe option 'no_icode_type'.)
+%%
+%% Reported by Andreas Sandberg on 3/1/2011 and fixed by Kostis on 5/1/2011
+%% with the help of Per Gustafsson.
+%% --------------------------------------------------------------------------
+-module(bs_flatb).
+
+-export([hipe_options/0, test/0]).
+
+hipe_options() ->
+ [icode_type].
+
+test() ->
+ [] = flatb([<<>>], []),
+ ok.
+
+flatb(<<X:8, Rest/binary>>, Acc) ->
+ flatb(Rest, [X|Acc]);
+flatb(<<>>, Acc) ->
+ Acc;
+flatb([V], Acc) ->
+ flatb(V, Acc).
diff --git a/lib/hipe/test/bs_SUITE_data/bs_id3.erl b/lib/hipe/test/bs_SUITE_data/bs_id3.erl
new file mode 100644
index 0000000000..a6152f05cd
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_id3.erl
@@ -0,0 +1,75 @@
+%% -*- erlang-indent-level: 2 -*-
+%%==========================================================================
+%% From: Tomas Stejskal -- 23/02/2008
+%% I've found some strange behavior regarding binary matching. The module's
+%% purpose is reading an id3 version 1 or version 1.1 tag from an mp3 bin.
+%% When I use the function read_v1_or_v11_tag on a mp3 binary containing
+%% version 1 tag, it returns an error. However, when the function
+%% read_only_v1_tag is applied on the same file, it reads the tag data
+%% correctly. The only difference between these two functions is that the
+%% former has an extra branch to read version 1.1 tag.
+%% This was a BEAM compiler bug which was fixed by a patch to beam_dead.
+%%==========================================================================
+
+-module(bs_id3).
+
+-export([test/0]).
+
+-define(BIN, <<84,65,71,68,117,154,105,232,107,121,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,68,97,110,105,101,108,32,76,97,110,
+ 100,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,101,115,116,
+ 32,79,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 50,48,48,48,50,48,48,48,32,45,32,66,101,115,116,32,79,102,
+ 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,12>>).
+
+test() ->
+ R1 = parse_only_v1_tag(?BIN),
+ R2 = parse_v1_or_v11_tag(?BIN),
+ %% io:format("~p\n~p\n", [R1, R2]),
+ R1 = R2, % crash if not equal
+ ok.
+
+parse_only_v1_tag(<<"TAG", Title:30/binary,
+ Artist:30/binary, Album:30/binary,
+ _Year:4/binary, _Comment:30/binary,
+ _Genre:8>>) ->
+ {ok,
+ {"ID3v1",
+ [{title, trim(Title)},
+ {artist, trim(Artist)},
+ {album, trim(Album)}]}};
+parse_only_v1_tag(_) ->
+ error.
+
+parse_v1_or_v11_tag(<<"TAG", Title:30/binary,
+ Artist:30/binary, Album:30/binary,
+ _Year:4/binary, _Comment:28/binary,
+ 0:8, Track:8, _Genre:8>>) ->
+ {ok,
+ {"ID3v1.1",
+ [{track, Track}, {title, trim(Title)},
+ {artist, trim(Artist)}, {album, trim(Album)}]}};
+parse_v1_or_v11_tag(<<"TAG", Title:30/binary,
+ Artist:30/binary, Album:30/binary,
+ _Year:4/binary, _Comment:30/binary,
+ _Genre:8>>) ->
+ {ok,
+ {"ID3v1",
+ [{title, trim(Title)},
+ {artist, trim(Artist)},
+ {album, trim(Album)}]}};
+parse_v1_or_v11_tag(_) ->
+ error.
+
+trim(Bin) ->
+ list_to_binary(trim_blanks(binary_to_list(Bin))).
+
+trim_blanks(L) ->
+ lists:reverse(skip_blanks_and_zero(lists:reverse(L))).
+
+skip_blanks_and_zero([$\s|T]) ->
+ skip_blanks_and_zero(T);
+skip_blanks_and_zero([0|T]) ->
+ skip_blanks_and_zero(T);
+skip_blanks_and_zero(L) ->
+ L.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_match.erl b/lib/hipe/test/bs_SUITE_data/bs_match.erl
new file mode 100644
index 0000000000..8194d878b8
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_match.erl
@@ -0,0 +1,175 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_match.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Performs simple matching and construction of binaries
+%%% TODO : Add binary and float tests
+%%% Created : 20 Feb 2004
+%%%-------------------------------------------------------------------
+-module(bs_match).
+
+-export([test/0]).
+
+test() ->
+ Funs = [fun test_aligned/0, fun test_unaligned/0,
+ fun test_zero_tail/0, fun test_integer_matching/0],
+ lists:foreach(fun (F) -> ok = F() end, Funs).
+
+%%-------------------------------------------------------------------
+%% Test aligned accesses
+
+test_aligned() ->
+ 10 = aligned_skip_bits_all(1, <<10,11,12>>),
+ ok = aligned().
+
+aligned_skip_bits_all(N, Bin) ->
+ <<X:N/integer-unit:8, _/binary>> = Bin,
+ X.
+
+aligned() ->
+ Tail1 = mkbin([]),
+ {258, Tail1} = al_get_tail_used(mkbin([1,2])),
+ Tail2 = mkbin(lists:seq(1, 127)),
+ {35091, Tail2} = al_get_tail_used(mkbin([137,19|Tail2])),
+ 64896 = al_get_tail_unused(mkbin([253,128])),
+ 64895 = al_get_tail_unused(mkbin([253,127|lists:seq(42, 255)])),
+ Tail3 = mkbin(lists:seq(0, 19)),
+ {0, Tail1} = get_dyn_tail_used(Tail1, 0),
+ {0, Tail3} = get_dyn_tail_used(mkbin([Tail3]), 0),
+ {73, Tail3} = get_dyn_tail_used(mkbin([73|Tail3]), 8),
+ 0 = get_dyn_tail_unused(mkbin([]), 0),
+ 233 = get_dyn_tail_unused(mkbin([233]), 8),
+ 23 = get_dyn_tail_unused(mkbin([23,22,2]), 8),
+ ok.
+
+mkbin(L) when is_list(L) -> list_to_binary(L).
+
+al_get_tail_used(<<A:16,T/binary>>) -> {A, T}.
+
+al_get_tail_unused(<<A:16,_/binary>>) -> A.
+
+%%-------------------------------------------------------------------
+%% Test unaligned accesses
+
+test_unaligned() ->
+ 10 = unaligned_skip_bits_all(8, <<10,11,12>>),
+ ok = unaligned().
+
+unaligned_skip_bits_all(N, Bin) ->
+ <<X:N, _/binary>> = Bin,
+ X.
+
+unaligned() ->
+ {'EXIT', {function_clause,_}} = (catch get_tail_used(mkbin([42]))),
+ {'EXIT', {{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)),
+ {'EXIT', {function_clause,_}} = (catch get_tail_unused(mkbin([42,33]))),
+ {'EXIT', {{badmatch,_},_}} = (catch get_dyn_tail_unused(mkbin([44]), 7)),
+ ok.
+
+get_tail_used(<<A:1, T/binary>>) -> {A, T}.
+
+get_tail_unused(<<A:15, _/binary>>) -> A.
+
+get_dyn_tail_used(Bin, Sz) ->
+ <<A:Sz, T/binary>> = Bin,
+ {A,T}.
+
+get_dyn_tail_unused(Bin, Sz) ->
+ <<A:Sz, _T/binary>> = Bin,
+ A.
+
+%%-------------------------------------------------------------------
+%% Test zero tail
+
+test_zero_tail() ->
+ 42 = zt8(mkbin([42])),
+ {'EXIT', {function_clause, _}} = (catch zt8(mkbin([1,2]))),
+ {'EXIT', {function_clause, _}} = (catch zt44(mkbin([1,2]))),
+ ok.
+
+zt8(<<A:8>>) -> A.
+
+zt44(<<_:4,_:4>>) -> ok.
+
+%%-------------------------------------------------------------------
+%% Test integer matching
+
+test_integer_matching() ->
+ ok = test_static_integer_matching_1(),
+ ok = test_static_integer_matching_2(),
+ ok = test_static_integer_matching_3(),
+ ok = test_static_integer_matching_4(),
+ DynFun = fun (N) -> ok = test_dynamic_integer_matching(N) end,
+ lists:foreach(DynFun, [28, 27, 9, 17, 25, 8, 16, 24, 32]).
+
+test_static_integer_matching_1() ->
+ <<0:6, -25:28/integer-signed, 0:6>> = s11(),
+ <<0:6, -25:28/integer-little-signed, 0:6>> = s12(),
+ <<0:6, 25:28/integer-little, 0:6>> = s13(),
+ <<0:6, 25:28, 0:6>> = s14(),
+ ok.
+
+s11() ->
+ <<0:6, -25:28/integer-signed, 0:6>>.
+s12() ->
+ <<0:6, -25:28/integer-little-signed, 0:6>>.
+s13() ->
+ <<0:6, 25:28/integer-little, 0:6>>.
+s14() ->
+ <<0:6, 25:28, 0:6>>.
+
+test_static_integer_matching_2() ->
+ <<0:6, -25:20/integer-signed, 0:6>> = s21(),
+ <<0:6, -25:20/integer-little-signed, 0:6>> = s22(),
+ <<0:6, 25:20/integer-little, 0:6>> = s23(),
+ <<0:6, 25:20, 0:6>> = s24(),
+ ok.
+
+s21() ->
+ <<0:6, -25:20/integer-signed, 0:6>>.
+s22() ->
+ <<0:6, -25:20/integer-little-signed, 0:6>>.
+s23() ->
+ <<0:6, 25:20/integer-little, 0:6>>.
+s24() ->
+ <<0:6, 25:20, 0:6>>.
+
+test_static_integer_matching_3() ->
+ <<0:6, -25:12/integer-signed, 0:6>> = s31(),
+ <<0:6, -25:12/integer-little-signed, 0:6>> = s32(),
+ <<0:6, 25:12/integer-little, 0:6>> = s33(),
+ <<0:6, 25:12, 0:6>> = s34(),
+ ok.
+
+s31() ->
+ <<0:6, -25:12/integer-signed, 0:6>>.
+s32() ->
+ <<0:6, -25:12/integer-little-signed, 0:6>>.
+s33() ->
+ <<0:6, 25:12/integer-little, 0:6>>.
+s34() ->
+ <<0:6, 25:12, 0:6>>.
+
+test_static_integer_matching_4() ->
+ <<0:6, -3:4/integer-signed, 0:6>> = s41(),
+ <<0:6, -3:4/integer-little-signed, 0:6>> = s42(),
+ <<0:6, 7:4/integer-little, 0:6>> = s43(),
+ <<0:6, 7:4, 0:6>> = s44(),
+ ok.
+
+s41() ->
+ <<0:6, -3:4/integer-signed, 0:6>>.
+s42() ->
+ <<0:6, -3:4/integer-little-signed, 0:6>>.
+s43() ->
+ <<0:6, 7:4/integer-little, 0:6>>.
+s44() ->
+ <<0:6, 7:4, 0:6>>.
+
+test_dynamic_integer_matching(N) ->
+ S = 32 - N,
+ <<-12:N/integer-signed, 0:S>> = <<-12:N/integer-signed, 0:S>>,
+ <<-12:N/integer-little-signed, 0:S>> = <<-12:N/integer-little-signed, 0:S>>,
+ <<12:N/integer, 0:S>> = <<12:N/integer, 0:S>>,
+ <<12:N/integer-little, 0:S>> = <<12:N/integer-little, 0:S>>,
+ ok.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_native_float.erl b/lib/hipe/test/bs_SUITE_data/bs_native_float.erl
new file mode 100644
index 0000000000..15fe0bf0c6
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_native_float.erl
@@ -0,0 +1,22 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------
+%% File : bs_native_float.erl
+%% Author : Kostis Sagonas
+%% Description : Test sent by Bjorn Gustavsson to report a bug in the
+%% handling of the 'native' endian specifier.
+%% Created : 28 Nov 2004
+%%-------------------------------------------------------------------
+-module(bs_native_float).
+
+-export([test/0]).
+
+test() ->
+ BeamRes = mk_bin(1.0, 2.0, 3.0),
+ hipe:c(?MODULE), %% Original was: hipe:c({?MODULE,vs_to_bin,1}, [o2]),
+ HipeRes = mk_bin(1.0, 2.0, 3.0),
+ %% io:format("Beam result = ~w\nHiPE result = ~w\n", [BeamRes,HipeRes]),
+ BeamRes = HipeRes,
+ ok.
+
+mk_bin(X, Y, Z) ->
+ <<X:64/native-float, Y:64/native-float, Z:64/native-float>>.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_orber.erl b/lib/hipe/test/bs_SUITE_data/bs_orber.erl
new file mode 100644
index 0000000000..c80ab8928d
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_orber.erl
@@ -0,0 +1,26 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Checks that labels are handled properly from Core
+%%% Created : 2 Nov 2004
+%%%-------------------------------------------------------------------
+-module(bs_orber).
+
+-export([test/0]).
+
+test() ->
+ 1 = dec_giop_message_header(<<1,1:32/little-integer>>),
+ 1 = dec_giop_message_header(<<0,1:32/big-integer>>),
+ {2, 1} = dec_giop_message_header(<<2,1:32/little-integer>>),
+ {3, 1} = dec_giop_message_header(<<3,1:32/big-integer>>),
+ ok.
+
+dec_giop_message_header(<<1:8, MessSize:32/little-integer>>) ->
+ MessSize;
+dec_giop_message_header(<<0:8, MessSize:32/big-integer>>) ->
+ MessSize;
+dec_giop_message_header(<<Flags:8, MessSize:32/little-integer>>) when
+ ((Flags band 16#03) =:= 16#02) ->
+ {Flags, MessSize};
+dec_giop_message_header(<<Flags:8, MessSize:32/big-integer>>) ->
+ {Flags, MessSize}.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_pmatch.erl b/lib/hipe/test/bs_SUITE_data/bs_pmatch.erl
new file mode 100644
index 0000000000..9474ffea4a
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_pmatch.erl
@@ -0,0 +1,269 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Tests that basic cases of binary pattern matching work
+%%--------------------------------------------------------------------
+-module(bs_pmatch).
+
+-export([test/0]).
+
+test() ->
+ %% construct some binaries
+ Bin42 = <<42>>,
+ Bin = <<12,17,42,0,0,0>>,
+ BinSS = <<0,1,0,0,0>>,
+ %% do some pattern matching
+ ok = pm_const(Bin42),
+ <<17,42,0,0,0>> = pm_tail(Bin),
+ 42 = pm_little(<<0:1,42:7>>),
+ 42 = pm_rec(Bin),
+ 30 = pm_rec_acc(<<1,2,3,4,5,6,7,8,9,10>>, 0),
+ 42 = pm_binary_tuple(Bin42),
+ -1 = pm_with_illegal_float(),
+ %% do some pattern matching with bound segments
+ ok = pm_bound_var(),
+ ok = pm_bound_tail(),
+ %% do some tests with floating point numbers
+ ok = pm_float(),
+ ok = pm_float_little(),
+ %% do some pattern matching with segments of unknown sizes
+ {<<17>>, <<42,0,0,0>>} = pm_body_s(Bin, 1),
+ {<<17>>, <<42,0,0,0>>} = pm_body_ss(Bin, 1, 4),
+ {<<45>>, <<>>} = pm_size_split(<<1:16,45>>),
+ {<<45>>, <<46,47>>} = pm_size_split(<<1:16,45,46,47>>),
+ {<<45,46>>, <<47>>} = pm_size_split(<<2:16,45,46,47>>),
+ {<<45,46>>, <<47>>} = pm_size_split_2(2, <<2:16,45,46,47>>),
+ {'EXIT',{function_clause,_}} = (catch pm_size_split_2(42, <<2:16,45,46,47>>)),
+ {<<45,46,47>>, <<48>>} = pm_sizes_split(<<16:8,3:16,45,46,47,48>>),
+ <<"cdef">> = pm_skip_segment(<<2:8, "abcdef">>),
+ -1 = pm_double_size_in_head(BinSS),
+ -1 = pm_double_size_in_body(BinSS),
+ %% and finally some cases which were problematic for various reasons
+ ok = pm_bigs(),
+ ok = pm_sean(),
+ ok = pm_bin8(<<1,2,3,4,5,6,7,8>>),
+ ok = pm_bs_match_string(),
+ ok = pm_till_gc(),
+ ok.
+
+%%--------------------
+%% Test cases below
+%%--------------------
+
+pm_const(<<42>>) ->
+ ok.
+
+pm_tail(<<12, Bin/binary>>) ->
+ Bin.
+
+pm_little(<<_:1, X:15/little>>) ->
+ {wrong, X};
+pm_little(<<_:1, X:7/little>>) ->
+ X.
+
+pm_rec(<<12, Bin/binary>>) ->
+ pm_rec(Bin);
+pm_rec(<<17, Word:4/little-signed-integer-unit:8>>) ->
+ Word.
+
+pm_rec_acc(<<_:4, A:4, Rest/binary>>, Acc) ->
+ case Rest of
+ <<X, Y, 9, NewRest/binary>> ->
+ pm_rec_acc(NewRest, X+Y+Acc);
+ <<X, 5, NewRest/binary>> ->
+ pm_rec_acc(NewRest, X+Acc);
+ <<2, NewRest/binary>> ->
+ pm_rec_acc(NewRest, 1+Acc);
+ <<NewRest/binary>> ->
+ pm_rec_acc(NewRest, A+Acc)
+ end;
+pm_rec_acc(<<>>, Acc) ->
+ Acc.
+
+pm_binary_tuple(<<X>>) ->
+ X;
+pm_binary_tuple({Y, Z}) ->
+ Y + Z.
+
+pm_with_illegal_float() ->
+ Bin = <<-1:64>>, % create a binary which is illegal as float
+ pm_float_integer(Bin). % try to match it out as a float
+
+pm_float_integer(<<F:64/float>>) -> F;
+pm_float_integer(<<I:64/integer-signed>>) -> I.
+
+%%--------------------------------------------------------------------
+%% Some tests with bound variables in segments
+
+pm_bound_var() ->
+ ok = pm_bound_var(42, 13, <<42,13>>),
+ no = pm_bound_var(42, 13, <<42,255>>),
+ no = pm_bound_var(42, 13, <<154,255>>),
+ ok.
+
+pm_bound_var(A, B, <<A:8, B:8>>) -> ok;
+pm_bound_var(_, _, _) -> no.
+
+pm_bound_tail() ->
+ ok = pm_bound_tail(<<>>, <<13,14>>),
+ ok = pm_bound_tail(<<2,3>>, <<1,1,2,3>>),
+ no = pm_bound_tail(<<2,3>>, <<1,1,2,7>>),
+ no = pm_bound_tail(<<2,3>>, <<1,1,2,3,4>>),
+ no = pm_bound_tail(<<2,3>>, <<>>),
+ ok.
+
+pm_bound_tail(T, <<_:16, T/binary>>) -> ok;
+pm_bound_tail(_, _) -> no.
+
+%%--------------------------------------------------------------------
+%% Floating point tests
+
+pm_float() ->
+ F = f1(),
+ G = f_one(),
+ G = match_float(<<63,128,0,0>>, 32, 0),
+ G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0),
+ fcmp(F, match_float(<<F:32/float>>, 32, 0)),
+ fcmp(F, match_float(<<F:64/float>>, 64, 0)),
+ fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)),
+ fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)),
+ fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)),
+ fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)),
+ ok.
+
+fcmp(F1, F2) when (F1 - F2) / F2 < 0.0000001 -> ok.
+
+match_float(Bin0, Fsz, I) ->
+ Bin = make_sub_bin(Bin0),
+ Bsz = bit_size(Bin),
+ Tsz = Bsz - Fsz - I,
+ <<_:I,F:Fsz/float,_:Tsz>> = Bin,
+ F.
+
+pm_float_little() ->
+ F = f2(),
+ G = f_one(),
+ G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0),
+ G = match_float_little(<<0,0,128,63>>, 32, 0),
+ fcmp(F, match_float_little(<<F:32/float-little>>, 32, 0)),
+ fcmp(F, match_float_little(<<F:64/float-little>>, 64, 0)),
+ fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)),
+ fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)),
+ fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)),
+ fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)),
+ ok.
+
+match_float_little(Bin0, Fsz, I) ->
+ Bin = make_sub_bin(Bin0),
+ Bsz = bit_size(Bin),
+ Tsz = Bsz - Fsz - I,
+ <<_:I, F:Fsz/float-little, _:Tsz>> = Bin,
+ F.
+
+make_sub_bin(Bin0) ->
+ Sz = byte_size(Bin0),
+ Bin1 = <<37,Bin0/binary,38,39>>,
+ <<_:8,Bin:Sz/binary,_:8,_:8>> = Bin1,
+ Bin.
+
+f1() -> 3.1415.
+
+f2() -> 2.7133.
+
+f_one() -> 1.0.
+
+%%--------------------------------------------------------------------
+%% Some tests using size fields specified within the binary
+pm_body_s(Bin, S1) ->
+ <<12, B1:S1/binary, B2:4/binary>> = Bin, %% 4 is hard-coded
+ {B1, B2}.
+
+pm_body_ss(Bin, S1, S2) ->
+ <<12, B1:S1/binary, B2:S2/binary>> = Bin,
+ {B1, B2}.
+
+pm_size_split(<<N:16, B:N/binary, T/binary>>) ->
+ {B, T}.
+
+pm_size_split_2(N, <<N:16, B:N/binary, T/binary>>) ->
+ {B, T}.
+
+pm_sizes_split(<<N0:8, N:N0, B:N/binary, T/binary>>) ->
+ {B, T}.
+
+pm_skip_segment(<<N:8, _:N/binary, T/binary>>) -> T.
+
+%%--------------------------------------------------------------------
+%% Some tests using multiple occurrences of size fields
+pm_double_size_in_head(<<S:16, _:S/binary, _:S/binary, _/binary>>) ->
+ -S.
+
+pm_double_size_in_body(Bin) ->
+ <<S:16, _:S/binary, _:S/binary, _/binary>> = Bin,
+ -S.
+
+%%--------------------------------------------------------------------
+%% matching with 64-bit integers which become big nums
+-define(BIG, 16#7fffffff7fffffff).
+
+pm_bigs() ->
+ <<X:64/little>> = <<?BIG:64/little>>,
+ true = (X =:= big()),
+ <<Y:64>> = <<?BIG:64>>,
+ true = (Y =:= big()),
+ ok.
+
+big() -> ?BIG.
+
+%%--------------------------------------------------------------------
+
+pm_sean() ->
+ small = sean1(<<>>),
+ small = sean1(<<1>>),
+ small = sean1(<<1,2>>),
+ small = sean1(<<1,2,3>>),
+ large = sean1(<<1,2,3,4>>),
+ small = sean1(<<4>>),
+ small = sean1(<<4,5>>),
+ small = sean1(<<4,5,6>>),
+ {'EXIT', {function_clause, _}} = (catch sean1(<<4,5,6,7>>)),
+ ok.
+
+sean1(<<B/binary>>) when byte_size(B) < 4 -> small;
+sean1(<<1, _/binary>>) -> large.
+
+%%--------------------------------------------------------------------
+%% Crashed on SPARC due to a bug in linear scan register allocator
+pm_bin8(<<A, B, C, D, E, F, G, H>>) ->
+ 10 = add4(A, B, C, D),
+ 26 = add4(E, F, G, H),
+ ok.
+
+add4(X, Y, Z, W) ->
+ X + Y + Z + W.
+
+%%--------------------------------------------------------------------
+%% Cases that exposed bugs in the handling of bs_match_string with an
+%% empty destination list. Reported on 2013/2/12 and fixed 2013/3/10.
+
+pm_bs_match_string() ->
+ Bin = <<42,42>>,
+ Bin = pm_match_string_head(Bin),
+ ok = (pm_match_string_fun())(Bin).
+
+pm_match_string_head(<<42, _/bits>> = B) -> B.
+
+pm_match_string_fun() ->
+ fun (<<X, _/bits>>) when X =:= 42 -> ok end.
+
+%%--------------------------------------------------------------------
+%% Match a lot to force a garbage collection which exposed a bug
+
+pm_till_gc() ->
+ Bin = <<16#76543210:32>>,
+ 16#76543210 = pm_a_lot(Bin, 1000000),
+ ok.
+
+pm_a_lot(<<X:32>>, 0) ->
+ X;
+pm_a_lot(<<X:32>>, N) ->
+ pm_a_lot(<<X:32>>, N-1).
diff --git a/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl b/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl
new file mode 100644
index 0000000000..b280705a47
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl
@@ -0,0 +1,67 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+-module(bs_pmatch_bugs).
+
+-export([test/0]).
+
+test() ->
+ Bin = <<"123.123">>,
+ <<49,50,51>> = lex_digits1(Bin, 1, []),
+ <<49,50,51>> = lex_digits2(Bin, 1, []),
+ ok = var_bind_bug(<<1, 2, 3, 4, 5, 6, 7, 8>>),
+ ok.
+
+%%--------------------------------------------------------------------
+%% One of the lex_digits functions below gave incorrect results due to
+%% incorrect pattern matching compilation of binaries by the byte code
+%% compiler. Fixed by Bjorn Gustavsson on 5/3/2003.
+%% --------------------------------------------------------------------
+lex_digits1(<<$., Rest/binary>>, _Val, _Acc) ->
+ Rest;
+lex_digits1(<<N, Rest/binary>>, Val, Acc) when N >= $0, N =< $9 ->
+ lex_digits1(Rest, Val * 10 + dec(N), Acc);
+lex_digits1(_Other, _Val, _Acc) ->
+ not_ok.
+
+lex_digits2(<<N, Rest/binary>>,Val, Acc) when N >= $0, N =< $9 ->
+ lex_digits2(Rest, Val * 10 + dec(N), Acc);
+lex_digits2(<<$., Rest/binary>>, _Val, _Acc) ->
+ Rest;
+lex_digits2(_Other, _Val, _Acc) ->
+ not_ok.
+
+dec(A) ->
+ A - $0.
+
+%%--------------------------------------------------------------------
+%% From: Bernard Duggan
+%% Date: 11/3/2011
+%%
+%% I've just run into an interesting little bit of behaviour that
+%% doesn't seem quite right. erlc gives me the warning
+%%
+%% 43: Warning: this clause cannot match because a previous
+%% clause at line 42 always matches
+%% (line 42 is the "B -> wrong;" line).
+%%
+%% And sure enough, if you run test/0 you get 'wrong' back.
+%%
+%% That, in itself, is curious to me since by my understanding B should
+%% be bound by the function header, and have no guarantee of being the
+%% same as A. I can't see how it could be unbound.
+%%
+%% Doubly curious, is that if I stop using B as the size specifier of C,
+%% like this:
+%%
+%% match(<<A:1/binary, B:8/integer, _C:1/binary, _Rest/binary>>) ->
+%%
+%% the warning goes away. And the result becomes 'ok' (in spite of
+%% nothing in the body having changed, and the only thing changing in
+%% the header being the size of an unused variable at the tail of the
+%% binary).
+%%--------------------------------------------------------------------
+var_bind_bug(<<A:1/binary, B:8/integer, _C:B/binary, _Rest/binary>>) ->
+ case A of
+ B -> wrong;
+ _ -> ok
+ end.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_pmatch_in_guards.erl b/lib/hipe/test/bs_SUITE_data/bs_pmatch_in_guards.erl
new file mode 100644
index 0000000000..159227bb92
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_pmatch_in_guards.erl
@@ -0,0 +1,23 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Tests that basic cases of binary pattern matching in guards work
+%%--------------------------------------------------------------------
+-module(bs_pmatch_in_guards).
+
+-export([test/0]).
+
+test() ->
+ 1 = in_guard(<<16#74ad:16>>, 16#e95, 5),
+ 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>),
+ 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415),
+ nope = in_guard(<<1>>, 42, b),
+ nope = in_guard(<<1>>, a, b),
+ nope = in_guard(<<1,2>>, 1, 1),
+ nope = in_guard(<<4,5>>, 1, 2.71),
+ nope = in_guard(<<4,5>>, 1, <<12,13>>),
+ ok.
+
+in_guard(Bin, A, B) when <<A:13,B:3>> == Bin -> 1;
+in_guard(Bin, A, B) when <<A:16,B/binary>> == Bin -> 2;
+in_guard(Bin, A, B) when <<A:14,B/float,3:2>> == Bin -> 3;
+in_guard(_, _, _) -> nope.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_potpurri.erl b/lib/hipe/test/bs_SUITE_data/bs_potpurri.erl
new file mode 100644
index 0000000000..8bc4fe5c88
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_potpurri.erl
@@ -0,0 +1,200 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+-module(bs_potpurri).
+
+-export([test/0]).
+
+test() ->
+ ok = integer(),
+ ok = signed_integer(),
+ ok = dynamic(),
+ ok = more_dynamic(),
+ ok = mml(),
+ ok.
+
+%% compile(Opts0) ->
+%% case proplists:get_bool(core, Opts0) of
+%% true ->
+%% test:note(?MODULE, "disabling compilation from core - BUG"),
+%% Opts = [{core,false}|Opts0];
+%% false ->
+%% Opts = Opts0
+%% end,
+%% hipe:c(?MODULE, Opts).
+
+integer() ->
+ 0 = get_int(mkbin([])),
+ 0 = get_int(mkbin([0])),
+ 42 = get_int(mkbin([42])),
+ 255 = get_int(mkbin([255])),
+ 256 = get_int(mkbin([1,0])),
+ 257 = get_int(mkbin([1,1])),
+ 258 = get_int(mkbin([1,2])),
+ 258 = get_int(mkbin([1,2])),
+ 65534 = get_int(mkbin([255,254])),
+ 16776455 = get_int(mkbin([255,253,7])),
+ 4245492555 = get_int(mkbin([253,13,19,75])),
+ L = [200,1,19,128,222,42,97,111,200,1,19,128,222,42,97,111],
+ ok = cmp128(mkbin(L), uint(L)),
+ ok = fun_clause(catch get_int(mkbin(lists:seq(1,5)))),
+ ok.
+
+get_int(<<I:0>>) -> I;
+get_int(<<I:8>>) -> I;
+get_int(<<I:16>>) -> I;
+get_int(<<I:24>>) -> I;
+get_int(<<I:32>>) -> I.
+
+cmp128(<<I:128>>, I) -> ok;
+cmp128(_Bin, _I) -> not_ok.
+
+signed_integer() ->
+ {no_match,_} = sint(mkbin([])),
+ {no_match,_} = sint(mkbin([1,2,3])),
+ 127 = sint(mkbin([127])),
+ -1 = sint(mkbin([255])),
+ -128 = sint(mkbin([128])),
+ 42 = sint(mkbin([42,255])),
+ 127 = sint(mkbin([127,255])),
+ ok.
+
+sint(Bin) ->
+ case Bin of
+ <<I:8/signed>> -> I;
+ <<I:8/signed,_:3,_:5>> -> I;
+ Other -> {no_match,Other}
+ end.
+
+uint(L) -> uint(L, 0).
+
+uint([H|T], Acc) -> uint(T, Acc bsl 8 bor H);
+uint([], Acc) -> Acc.
+
+dynamic() ->
+ ok = dynamic(mkbin([255]), 8),
+ ok = dynamic(mkbin([255,255]), 16),
+ ok = dynamic(mkbin([255,255,255]), 24),
+ ok = dynamic(mkbin([255,255,255,255]), 32),
+ ok.
+
+dynamic(Bin, S1) when S1 >= 0 ->
+ S2 = bit_size(Bin) - S1,
+ dynamic(Bin, S1, S2, (1 bsl S1) - 1, (1 bsl S2) - 1),
+ dynamic(Bin, S1-1);
+dynamic(_Bin, _) -> ok.
+
+dynamic(Bin, S1, S2, A, B) ->
+ %% io:format("~p ~p ~p ~p\n", [S1,S2,A,B]),
+ case Bin of
+ <<A:S1,B:S2>> ->
+ %% io:format("~p ~p ~p ~p\n", [S1,S2,A,B]),
+ ok;
+ <<A1:S1,B2:S2>> -> erlang:error(badmatch, [Bin,S1,S2,A,B,A1,B2])
+ end.
+
+more_dynamic() ->
+ %% Unsigned big-endian numbers.
+ Unsigned = fun(Bin, List, SkipBef, N) ->
+ SkipAft = bit_size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N,_I2:SkipAft>> = Bin,
+ Int = make_int(List, N, 0)
+ end,
+ ok = more_dynamic1(Unsigned, funny_binary(42)),
+
+ %% Signed big-endian numbers.
+ Signed = fun(Bin, List, SkipBef, N) ->
+ SkipAft = bit_size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N/signed,_I2:SkipAft>> = Bin,
+ case make_signed_int(List, N) of
+ Int -> ok;
+ Other ->
+ io:format("Bin = ~p,", [Bin]),
+ io:format("SkipBef = ~p, N = ~p", [SkipBef,N]),
+ io:format("Expected ~p, got ~p", [Int,Other]),
+ exit(Other)
+ end
+ end,
+ ok = more_dynamic1(Signed, funny_binary(43)),
+
+ %% Unsigned little-endian numbers.
+ UnsLittle = fun(Bin, List, SkipBef, N) ->
+ SkipAft = bit_size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N/little,_I2:SkipAft>> = Bin,
+ Int = make_int(big_to_little(List, N), N, 0)
+ end,
+ more_dynamic1(UnsLittle, funny_binary(44)),
+
+ %% Signed little-endian numbers.
+ SignLittle = fun(Bin, List, SkipBef, N) ->
+ SkipAft = bit_size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N/signed-little,_I2:SkipAft>> = Bin,
+ Little = big_to_little(List, N),
+ Int = make_signed_int(Little, N)
+ end,
+ ok = more_dynamic1(SignLittle, funny_binary(45)),
+
+ ok.
+
+funny_binary(N) ->
+ B0 = erlang:md5([N]),
+ {B1,_B2} = split_binary(B0, byte_size(B0) div 2),
+ B1.
+
+more_dynamic1(Action, Bin) ->
+ BitList = bits_to_list(binary_to_list(Bin), 16#80),
+ more_dynamic2(Action, Bin, BitList, 0).
+
+more_dynamic2(Action, Bin, [_|T]=List, Bef) ->
+ more_dynamic3(Action, Bin, List, Bef, bit_size(Bin)),
+ more_dynamic2(Action, Bin, T, Bef+1);
+more_dynamic2(_Action, _Bin, [], _Bef) -> ok.
+
+more_dynamic3(Action, Bin, List, Bef, Aft) when Bef =< Aft ->
+ %% io:format("~p, ~p", [Bef,Aft-Bef]),
+ Action(Bin, List, Bef, Aft-Bef),
+ more_dynamic3(Action, Bin, List, Bef, Aft-1);
+more_dynamic3(_, _, _, _, _) -> ok.
+
+big_to_little(List, N) -> big_to_little(List, N, []).
+
+big_to_little([B0,B1,B2,B3,B4,B5,B6,B7|T], N, Acc) when N >= 8 ->
+ big_to_little(T, N-8, [B0,B1,B2,B3,B4,B5,B6,B7|Acc]);
+big_to_little(List, N, Acc) -> lists:sublist(List, 1, N) ++ Acc.
+
+make_signed_int(_List, 0) -> 0;
+make_signed_int([0|_]=List, N) -> make_int(List, N, 0);
+make_signed_int([1|_]=List0, N) ->
+ List1 = reversed_sublist(List0, N, []),
+ List2 = two_complement_and_reverse(List1, 1, []),
+ -make_int(List2, length(List2), 0).
+
+reversed_sublist(_List, 0, Acc) -> Acc;
+reversed_sublist([H|T], N, Acc) -> reversed_sublist(T, N-1, [H|Acc]).
+
+two_complement_and_reverse([H|T], Carry, Acc) ->
+ Sum = 1 - H + Carry,
+ two_complement_and_reverse(T, Sum div 2, [Sum rem 2|Acc]);
+two_complement_and_reverse([], Carry, Acc) -> [Carry|Acc].
+
+make_int(_List, 0, Acc) -> Acc;
+make_int([H|T], N, Acc) -> make_int(T, N-1, Acc bsl 1 bor H).
+
+bits_to_list([_|T], 0) -> bits_to_list(T, 16#80);
+bits_to_list([H|_]=List, Mask) ->
+ [case H band Mask of
+ 0 -> 0;
+ _ -> 1
+ end|bits_to_list(List, Mask bsr 1)];
+bits_to_list([], _) -> [].
+
+fun_clause({'EXIT',{function_clause,_}}) -> ok.
+
+mkbin(L) when is_list(L) -> list_to_binary(L).
+
+mml() ->
+ single_byte_binary = mml_choose(<<42>>),
+ multi_byte_binary = mml_choose(<<42,43>>),
+ ok.
+
+mml_choose(<<_:8>>) -> single_byte_binary;
+mml_choose(<<_:8, _T/binary>>) -> multi_byte_binary.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_remove3.erl b/lib/hipe/test/bs_SUITE_data/bs_remove3.erl
new file mode 100644
index 0000000000..a98b0b5b28
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_remove3.erl
@@ -0,0 +1,104 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_remove3.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose :
+%%%
+%%% Created : 13 Apr 2004 by Per Gustafsson
+%%%-------------------------------------------------------------------
+-module(bs_remove3).
+
+-export([test/0]).
+
+-define(A, <<56,0,120,0,0,31,255,255,102,42,12,0,3,3,16,5,24,3,240,0,0,32,0,196,
+ 2,128,4,0,255,255,254,33,68,96,0,8,8,213,40,192,31,196,0,4,0,0>>).
+-define(B, <<28,32,0,96,0,8,0,7,255,255,212,33,98,12,0,0,1,0,48,72,66,3,0,7,240,
+ 64,0,0,8,0,0,224,0,10,128,0,64,0,63,255,254,133,10,80,96,0,0,8,1,6,
+ 18,4,24,0,63,128,0,0,4,64,0,0>>).
+
+test() ->
+ Bin1 = <<30,16,0,90,0,1,0,0,255,255,255,255,81,67,101,7,0,
+ 0,0,96,6,12,146,18,14,0,15,252,16,0,0,17,0,0>>,
+ Bin = <<Bin1/binary, Bin1/binary>>,
+ ?A = loop(Bin, 10, fun run_list/1),
+ ?A = loop(Bin, 10, fun run_bin/1),
+ ?B = loop(Bin, 10, fun r31/1),
+ ok.
+
+loop(Arg, 0, F) ->
+ F(Arg);
+loop(Arg, N, F) ->
+ F(Arg),
+ loop(Arg, N-1, F).
+
+run_list(Bin) ->
+ List = run1(Bin),
+ list_to_binary(List).
+
+run1(<<A1:2,_:1,A2:2,_:1,A3:2,_:1,A4:2,_:1,
+ A5:2,_:1,A6:2,_:1,A7:2,_:1,A8:2,_:1,Rest/binary>>) ->
+ [<<A1:2,A2:2,A3:2,A4:2,A5:2,A6:2,A7:2,A8:2>>, run2(Rest)];
+run1(<<A1:2,_:1,A2:2,_:1,A3:2,_:1,A4:2,_:1,A5:2,_:1,A6:1>>) ->
+ [<<A1:2,A2:2,A3:2,A4:2,A5:2,A6:1,0:5>>];
+run1(<<A1:2,_:1,A2:2,_:1,A3:2>>) ->
+ [<<A1:2,A2:2,A3:2,0:2>>];
+run1(<<>>) ->
+ [].
+
+run_bin(Bin) ->
+ run2(Bin).
+
+run2(<<A1:2,_:1,A2:2,_:1,A3:2,_:1,A4:2,_:1,
+ A5:2,_:1,A6:2,_:1,A7:2,_:1,A8:2,_:1,Rest/binary>>) ->
+ Bin = run2(Rest),
+ <<A1:2,A2:2,A3:2,A4:2,A5:2,A6:2,A7:2,A8:2,Bin/binary>>;
+run2(<<A1:2,_:1,A2:2,_:1,A3:2,_:1,A4:2,_:1,A5:2,_:1,A6:1>>) ->
+ <<A1:2,A2:2,A3:2,A4:2,A5:2,A6:1,0:5>>;
+run2(<<A1:2,_:1,A2:2,_:1,A3:2>>) ->
+ <<A1:2,A2:2,A3:2,0:2>>;
+run2(<<>>) ->
+ <<>>.
+
+r31(Bin) ->
+ List = remove3rd1(0, 0, Bin, [-1]),
+ build(List, Bin, 0, <<>>).
+
+build([N1, N2|Rest], Bin, N, Present) ->
+ X = N1+1, Y = N2-X,
+ S = rest(N2),
+ <<_:X,A:Y,_:S,_/binary>> = Bin,
+ S1 = rest(N+Y),
+ NewPresent = <<Present:N/binary-unit:1, A:Y, 0:S1>>,
+ build([N2|Rest], Bin, N+Y, NewPresent);
+
+build([_], _Bin, _N, Present) ->
+ Present.
+
+rest(X) ->
+ case 8 - (X rem 8) of
+ 8 -> 0;
+ H -> H
+ end.
+
+remove3rd1(N, 2, Bin, List) ->
+ S = rest(N+1),
+ case Bin of
+ <<_:N, 1:1, _:S,_/binary>> ->
+ remove3rd1(N+1, 0, Bin, [N|List]);
+ <<_:N, 0:1, _:S,_/binary>> ->
+ remove3rd1(N+1, 2, Bin, List);
+ _ ->
+ Size = byte_size(Bin) * 8,
+ lists:reverse([Size|List])
+ end;
+remove3rd1(N, I, Bin, List) ->
+ S = rest(N+1),
+ case Bin of
+ <<_:N, 1:1, _:S,_/binary>> ->
+ remove3rd1(N+1, I+1, Bin, List);
+ <<_:N, 0:1, _:S,_/binary>> ->
+ remove3rd1(N+1, I, Bin, List);
+ _ ->
+ Size = byte_size(Bin) * 8,
+ lists:reverse([Size|List])
+ end.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_save.erl b/lib/hipe/test/bs_SUITE_data/bs_save.erl
new file mode 100644
index 0000000000..fe2b1105f2
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_save.erl
@@ -0,0 +1,21 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_save.erl
+%%% Author : Per Gustafsson
+%%% Purpose : Tests that compilation works for bs_save
+%%% Created : 1 Nov 2007
+%%%-------------------------------------------------------------------
+-module(bs_save).
+
+-export([test/0]).
+
+test() ->
+ {[16257, 1], <<0>>} = inc_on_ones(<<255,1,128,1,128,0>>, 0, [], 5),
+ ok.
+
+inc_on_ones(Buffer, _Av, Al, 0) ->
+ {lists:reverse(Al), Buffer};
+inc_on_ones(<<1:1, H:7, T/binary>>, Av, Al, Len) ->
+ inc_on_ones(T, (Av bsl 7) bor H, Al, Len-1);
+inc_on_ones(<<H, T/binary>>, Av, Al, Len) ->
+ inc_on_ones(T, 0, [((Av bsl 7) bor H)|Al], Len-1).
diff --git a/lib/hipe/test/bs_SUITE_data/bs_shell_native.erl b/lib/hipe/test/bs_SUITE_data/bs_shell_native.erl
new file mode 100644
index 0000000000..b438f8d9ef
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_shell_native.erl
@@ -0,0 +1,275 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_shell_native.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Tests that the Erlang shell works well when in native
+%%% Created : 6 Sep 2006
+%%%-------------------------------------------------------------------
+-module(bs_shell_native).
+
+-export([prepare_for_test/0, test/0]).
+%% These need to be exported so that we emulate calling them from the shell
+-export([parse_and_eval/1, receiver/1, receiver_alot/1, send_alot/3]).
+
+%% This makes sure the shell runs native code
+prepare_for_test() ->
+ lists:foreach(fun (M) -> {ok, M} = hipe:c(M) end, [erl_bits, erl_eval]).
+
+test() ->
+ ok = eval_bits_in_shell(),
+ ok = eval_bin_comp_in_shell(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Tests for bit stream operations including matching, construction
+%% and binary_to_list, list_to_binary in the shell
+eval_bits_in_shell() ->
+ <<1:100>> = parse_and_eval("<<1:100>> = <<1:100>>."),
+ ok = match(7),
+ ok = match(9),
+ ok = match1(15),
+ ok = match1(31),
+ ok = horrid_match(),
+ ok = test_bitstr(),
+ ok = test_bitsize(),
+ ok = asymmetric_tests(),
+ ok = big_asymmetric_tests(),
+ ok = binary_to_and_from_list(),
+ ok = big_binary_to_and_from_list(),
+ ok = send_and_receive(),
+ ok = send_and_receive_alot(),
+ ok.
+
+parse_and_eval(String) ->
+ {ok, Toks, _} = erl_scan:string(String),
+ {ok, Exprs} = erl_parse:parse_exprs(Toks),
+ Bnds = erl_eval:new_bindings(),
+ case erl_eval:exprs(Exprs, Bnds) of
+ {value, V, _} ->
+ V;
+ V ->
+ V
+ end.
+
+match(N) ->
+ Str = "N =" ++ integer_to_list(N) ++ ", <<0:N>> = <<0:N>>.",
+ <<0:N>> = parse_and_eval(Str),
+ ok.
+
+match1(N) ->
+ Str = "N =" ++ integer_to_list(N) ++ ", <<42:N/little>> = <<42:N/little>>.",
+ <<42:N/little>> = parse_and_eval(Str),
+ ok.
+
+test_bitsize() ->
+ 101 = parse_and_eval("101 = erlang:bit_size(<<1:101>>)."),
+ 1001 = parse_and_eval("1001 = erlang:bit_size(<<1:1001>>)."),
+ 80 = parse_and_eval("80 = erlang:bit_size(<<1:80>>)."),
+ 800 = parse_and_eval("800 = erlang:bit_size(<<1:800>>)."),
+ S =
+ "Bin = <<0:16#1000000>>,"
+ "BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)] ++ [<<1:1>>]),"
+ "16#10000001 = erlang:bit_size(BigBin).",
+ 16#10000001 = parse_and_eval(S),
+ %% Only run these on computers with lots of memory
+ %% HugeBin = list_to_bitstring([BigBin||_ <- lists:seq(1,16#10)]++[<<1:1>>]),
+ %% 16#100000011 = erlang:bit_size(HugeBin),
+ 0 = parse_and_eval("0 = erlang:bit_size(<<>>)."),
+ ok.
+
+horrid_match() ->
+ S = "<<1:4,B:24/bitstring>> = <<1:4,42:24/little>>, <<42:24/little>> = B.",
+ <<42:24/little>> = parse_and_eval(S),
+ ok.
+
+test_bitstr() ->
+ S =
+ "<<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>,"
+ "<<1:1,6>> = B,"
+ "B = <<1:1,6>>.",
+ <<1:1,6>> = parse_and_eval(S),
+ ok.
+
+asymmetric_tests() ->
+ <<1:12>> = parse_and_eval("<<1:12>> = <<0,1:4>>."),
+ <<0,1:4>> = parse_and_eval("<<0,1:4>> = <<1:12>>."),
+ S1 =
+ "<<1:1,X/bitstring>> = <<128,255,0,0:2>>,"
+ "<<1,254,0,0:1>> = X,"
+ "X = <<1,254,0,0:1>>.",
+ <<1,254,0,0:1>> = parse_and_eval(S1),
+ S2 =
+ "<<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>,"
+ "<<1,254,0,0:1>> = X1,"
+ "X1 = <<1,254,0,0:1>>.",
+ <<1,254,0,0:1>> = parse_and_eval(S2),
+ ok.
+
+big_asymmetric_tests() ->
+ <<1:875,1:12>> = parse_and_eval("<<1:875,1:12>> = <<1:875,0,1:4>>."),
+ <<1:875,0,1:4>> = parse_and_eval("<<1:875,0,1:4>> = <<1:875,1:12>>."),
+ S1 =
+ "<<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>,"
+ "<<1,254,0,0:1,1:875>> = X,"
+ "X = <<1,254,0,0:1,1:875>>.",
+ <<1,254,0,0:1,1:875>> = parse_and_eval(S1),
+ S2 =
+ "<<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>,"
+ "<<1,254,0,0:1,1:875>> = X1,"
+ "X1 = <<1,254,0,0:1,1:875>>.",
+ parse_and_eval(S2),
+ ok.
+
+binary_to_and_from_list() ->
+ <<1:7>> = parse_and_eval("list_to_bitstring(bitstring_to_list(<<1:7>>))."),
+ <<1,2,3,4,1:1>> = parse_and_eval("list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>))."),
+ [1,2,3,4,<<1:1>>] = parse_and_eval("bitstring_to_list(<<1,2,3,4,1:1>>)."),
+ <<1:1,1,2,3,4>> = parse_and_eval("list_to_bitstring([<<1:1>>,1,2,3,4])."),
+ [128,129,1,130,<<0:1>>] = parse_and_eval("bitstring_to_list(<<1:1,1,2,3,4>>)."),
+ ok.
+
+big_binary_to_and_from_list() ->
+ S1 = "erlang:list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)).",
+ <<1:800,2,3,4,1:1>> = parse_and_eval(S1),
+ S2 = "erlang:bitstring_to_list(<<1,2,3,4,1:800,1:1>>).",
+ [1,2,3,4|_Rest1] = parse_and_eval(S2),
+ S3 = "erlang:list_to_bitstring([<<1:801>>,1,2,3,4]).",
+ <<1:801,1,2,3,4>> = parse_and_eval(S3),
+ ok.
+
+send_and_receive() ->
+ S =
+ "Bin = <<1,2:7>>,"
+ "Pid = spawn(fun() -> bs_shell_native:receiver(Bin) end),"
+ "Pid ! {self(),<<1:7,8:5,Bin/bitstring>>},"
+ "receive ok -> ok end.",
+ parse_and_eval(S).
+
+receiver(Bin) ->
+ receive
+ {Pid, <<1:7,8:5,Bin/bitstring>>} ->
+ Pid ! ok
+ end.
+
+send_and_receive_alot() ->
+ S =
+ "Bin = <<1:1000001>>,"
+ "Pid = spawn(fun() -> bs_shell_native:receiver_alot(Bin) end),"
+ "bs_shell_native:send_alot(100,Bin,Pid).",
+ parse_and_eval(S).
+
+send_alot(N,Bin,Pid) when N > 0 ->
+ Pid ! {self(),<<1:7,8:5,Bin/bitstring>>},
+ receive
+ ok ->
+ ok
+ end,
+ send_alot(N-1,Bin,Pid);
+send_alot(0,_Bin,Pid) ->
+ Pid ! no_more,
+ ok.
+
+receiver_alot(Bin) ->
+ receive
+ {Pid, <<1:7,8:5,Bin/bitstring>>} ->
+ Pid ! ok;
+ no_more -> ok
+ end,
+ receiver_alot(Bin).
+
+%%--------------------------------------------------------------------
+
+eval_bin_comp_in_shell() ->
+ ok = byte_aligned(),
+ ok = bit_aligned(),
+ ok = extended_byte_aligned(),
+ ok = extended_bit_aligned(),
+ ok = mixed(),
+ ok.
+
+byte_aligned() ->
+ <<"abcdefg">> =
+ parse_and_eval("<<\"abcdefg\">> = << <<(X+32)>> || <<X>> <= <<\"ABCDEFG\">> >>."),
+ <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ parse_and_eval("<<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || <<X:32>> <= <<1:32,2:32,3:32,4:32>> >>."),
+ <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ parse_and_eval("<<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || <<X:16>> <= <<1:16,2:16,3:16,4:16>> >>."),
+ ok.
+
+bit_aligned() ->
+ <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ parse_and_eval("<<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ << <<(X+32):7>> || <<X>> <= <<\"ABCDEFG\">> >>."),
+ <<"ABCDEFG">> =
+ parse_and_eval("<<\"ABCDEFG\">> =
+ << <<(X-32)>> || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> >>."),
+ <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ parse_and_eval("<<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || <<X:31>> <= <<1:31,2:31,3:31,4:31>> >>."),
+ <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ parse_and_eval("<<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || <<X:15>> <= <<1:15,2:15,3:15,4:15>> >>."),
+ ok.
+
+extended_byte_aligned() ->
+ <<"abcdefg">> =
+ parse_and_eval("<<\"abcdefg\">> = << <<(X+32)>> || X <- \"ABCDEFG\" >>."),
+ "abcdefg" =
+ parse_and_eval("\"abcdefg\" = [(X+32) || <<X>> <= <<\"ABCDEFG\">>]."),
+ <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ parse_and_eval("<<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || X <- [1,2,3,4] >>."),
+ [256,512,768,1024] =
+ parse_and_eval("[256,512,768,1024] =
+ [X || <<X:16/little>> <= <<1:16,2:16,3:16,4:16>>]."),
+ ok.
+
+extended_bit_aligned() ->
+ <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ parse_and_eval("<<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ << <<(X+32):7>> || X <- \"ABCDEFG\" >>."),
+ "ABCDEFG" =
+ parse_and_eval("\"ABCDEFG\" = [(X-32) || <<X:7>> <=
+<<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>]."),
+ <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ parse_and_eval("<<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || X <- [1,2,3,4] >>."),
+ [256,512,768,1024] =
+ parse_and_eval("[256,512,768,1024] =
+ [X || <<X:15/little>> <= <<1:15,2:15,3:15,4:15>>]."),
+ ok.
+
+mixed() ->
+ <<2,3,3,4,4,5,5,6>> =
+ parse_and_eval("<<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>> >>."),
+ <<2,3,3,4,4,5,5,6>> =
+ parse_and_eval("<<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, Y <- [1,2] >>."),
+ <<2,3,3,4,4,5,5,6>> =
+ parse_and_eval("<<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || X <- [1,2,3,4], Y <- [1,2] >>."),
+ [2,3,3,4,4,5,5,6] =
+ parse_and_eval("[2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>>]."),
+ [2,3,3,4,4,5,5,6] =
+ parse_and_eval("[2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X>> <= <<1,2,3,4>>, Y <- [1,2]]."),
+ <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ parse_and_eval("<<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>> >>."),
+ <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ parse_and_eval("<<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2] >>."),
+ <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ parse_and_eval("<<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || X <- [1,2,3,4], Y <- [1,2] >>."),
+ [2,3,3,4,4,5,5,6] =
+ parse_and_eval("[2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>>]."),
+ [2,3,3,4,4,5,5,6] =
+ parse_and_eval("[2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2]]."),
+ ok.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_split.erl b/lib/hipe/test/bs_SUITE_data/bs_split.erl
new file mode 100644
index 0000000000..2e52308a77
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_split.erl
@@ -0,0 +1,105 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+
+-module(bs_split).
+
+-export([test/0]).
+
+test() ->
+ Funs = [fun byte_split_binary/0, fun bit_split_binary/0, fun z_split/0],
+ lists:foreach(fun (F) -> ok = F() end, Funs).
+
+%%--------------------------------------------------------------------
+
+byte_split_binary() ->
+ L = lists:seq(0, 57),
+ B = mkbin(L),
+ byte_split(L, B, byte_size(B)).
+
+byte_split(L, B, Pos) when Pos >= 0 ->
+ Sz1 = Pos,
+ Sz2 = byte_size(B) - Pos,
+ bs1(L, B, Pos, Sz1, Sz2);
+byte_split(_, _, _) -> ok.
+
+bs1(L, B, Pos, Sz1, Sz2) ->
+ <<B1:Sz1/binary, B2:Sz2/binary>> = B,
+ bs2(L, B, Pos, B1, B2).
+
+bs2(L, B, Pos, B1, B2)->
+ B1 = list_to_binary(lists:sublist(L, 1, Pos)),
+ bs3(L, B, Pos, B2).
+
+bs3(L, B, Pos, B2) ->
+ B2 = list_to_binary(lists:nthtail(Pos, L)),
+ byte_split(L, B, Pos-1).
+
+%%--------------------------------------------------------------------
+
+bit_split_binary() ->
+ Fun = fun(Bin, List, SkipBef, N) ->
+ SkipAft = bit_size(Bin) - N - SkipBef,
+ %% io:format("~p, ~p, ~p", [SkipBef,N,SkipAft]),
+ <<_I1:SkipBef,OutBin:N/binary-unit:1,_I2:SkipAft>> = Bin,
+ OutBin = make_bin_from_list(List, N)
+ end,
+ bit_split_binary1(Fun, erlang:md5(<<1,2,3>>)).
+
+bit_split_binary1(Action, Bin) ->
+ BitList = bits_to_list(binary_to_list(Bin), 16#80),
+ bit_split_binary2(Action, Bin, BitList, 0).
+
+bit_split_binary2(Action, Bin, [_|T]=List, Bef) ->
+ bit_split_binary3(Action, Bin, List, Bef, bit_size(Bin)),
+ bit_split_binary2(Action, Bin, T, Bef+1);
+bit_split_binary2(_Action, _Bin, [], _Bef) -> ok.
+
+bit_split_binary3(Action, Bin, List, Bef, Aft) when Bef =< Aft ->
+ Action(Bin, List, Bef, (Aft-Bef) div 8 * 8),
+ bit_split_binary3(Action, Bin, List, Bef, Aft-8);
+bit_split_binary3(_, _, _, _, _) -> ok.
+
+make_bin_from_list(_List, 0) ->
+ mkbin([]);
+make_bin_from_list(List, N) ->
+ list_to_binary([make_int(List, 8, 0),
+ make_bin_from_list(lists:nthtail(8, List), N-8)]).
+
+make_int(_List, 0, Acc) -> Acc;
+make_int([H|T], N, Acc) -> make_int(T, N-1, Acc bsl 1 bor H).
+
+bits_to_list([_|T], 0) -> bits_to_list(T, 16#80);
+bits_to_list([H|_]=List, Mask) ->
+ [case H band Mask of
+ 0 -> 0;
+ _ -> 1
+ end|bits_to_list(List, Mask bsr 1)];
+bits_to_list([], _) -> [].
+
+mkbin(L) when is_list(L) -> list_to_binary(L).
+
+%%--------------------------------------------------------------------
+%% Splits a series of null terminated segments of a binary without
+%% creating any new sub-binaries until the zero is found.
+
+z_split() ->
+ [<<61,62,63>>] = z_split(<<61,62,63>>),
+ [<<61,62,63>>, <<>>] = z_split(<<61,62,63,0>>),
+ [<<61,62,63>>, <<64>>] = z_split(<<61,62,63,0,64>>),
+ [<<61,62,63>>, <<64,65,66>>] = z_split(<<61,62,63,0,64,65,66>>),
+ [<<61,62>>, <<64>>, <<>>, <<65,66>>] = z_split(<<61,62,0,64,0,0,65,66>>),
+ ok.
+
+z_split(B) when is_binary(B) ->
+ z_split(B, 0).
+
+z_split(B, N) ->
+ case B of
+ <<_B1:N/binary,0,_B2/binary>> -> % use skip_bits for B1, B2
+ <<B1:N/binary,_,B2/binary>> = B, % and postpone the matching
+ [B1 | z_split(B2)];
+ <<_:N/binary>> ->
+ [B];
+ _ ->
+ z_split(B, N+1)
+ end.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_system_limit_32.erl b/lib/hipe/test/bs_SUITE_data/bs_system_limit_32.erl
new file mode 100644
index 0000000000..eccb0083bd
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_system_limit_32.erl
@@ -0,0 +1,26 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : bs_system_limit_32.erl
+%%% Author : Per Gustafsson <[email protected]>
+%%% Purpose : Checks binary system limits on 32-bit machines
+%%% Created : 14 May 2008
+%%%-------------------------------------------------------------------
+-module(bs_system_limit_32).
+
+-export([test/0]).
+
+test() ->
+ case erlang:system_info(wordsize) of
+ 4 -> system_limit_32();
+ 8 -> ok
+ end.
+
+system_limit_32() ->
+ {'EXIT', {badarg, _}} = (catch <<42:(id(-1))>>),
+ {'EXIT', {badarg, _}} = (catch <<42:(id(-389739873536870912))/unit:8>>),
+ {'EXIT', {system_limit, _}} = (catch <<32:536870912/unit:8>>),
+ {'EXIT', {system_limit, _}} = (catch <<42:(id(536870912))/unit:8>>),
+ {'EXIT', {system_limit, _}} = (catch <<42:(id(536870912))/unit:8,1:1>>),
+ ok.
+
+id(X) -> X.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_utf.erl b/lib/hipe/test/bs_SUITE_data/bs_utf.erl
new file mode 100644
index 0000000000..f50ae08964
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_utf.erl
@@ -0,0 +1,18 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------
+%% Purpose: test support for UTF datatypes in binaries - INCOMPLETE
+%%-------------------------------------------------------------------
+
+-module(bs_utf).
+
+-export([test/0]).
+
+test() ->
+ <<65>> = b65utf8(),
+ ok = m(<<65>>).
+
+m(<<65/utf8>>) ->
+ ok.
+
+b65utf8() ->
+ <<65/utf8>>.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_var_segs.erl b/lib/hipe/test/bs_SUITE_data/bs_var_segs.erl
new file mode 100644
index 0000000000..a20df04b53
--- /dev/null
+++ b/lib/hipe/test/bs_SUITE_data/bs_var_segs.erl
@@ -0,0 +1,76 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% Author : Kostis Sagonas
+%% Purpose : These tests are intended to test the construction and
+%% matching of binaries using variable sizes
+%% Notes :
+%% - Added test that crashed BEAM compiler
+%% - Added test that crashed when segments of size zero were used
+%% and one that did not convert integers to floats when constructing
+%% binaries.
+%% - Added a construction test which crashed from core because of
+%% problems with the effect flag (2004/11/15)
+%%--------------------------------------------------------------------
+-module(bs_var_segs).
+
+-export([test/0]).
+
+test() ->
+ N1 = 18,
+ A1 = 2,
+ A1 = match1(N1, <<1:12, 2:N1, A1:2>>),
+ A1 = match2(N1, <<1:12, 2:N1/integer-little, A1:2>>),
+ N3 = 3,
+ A3 = <<1,2,3>>,
+ B3 = 2,
+ {A3, B3} = match3(N3, <<1:12, A3:N3/binary, B3:4>>),
+ N4 = 12,
+ B4 = <<1,2,3>>,
+ A4 = 2,
+ {A4, B4} = match4(N4, <<1:N4, A4:4, B4/binary>>),
+ Y = <<5>>,
+ Y = match5(a, Y),
+ <<73>> = gen1(8, 0, <<73>>),
+ <<171>> = gen2(8, 7, 2#10101010101010101),
+ <<0:64>> = construct(),
+ <<0:32>> = construct2(0),
+ ok = in_guard(<<16#BCD:14,3:2>>, 16#BCD),
+ ok.
+
+construct() ->
+ <<0:64/float>>.
+
+construct2(X) ->
+ <<X:32/little>>.
+
+match1(N, Bin) ->
+ <<1:12, 2:N, A:2>>=Bin,
+ A.
+
+match2(N, Bin) ->
+ <<1:12, 2:N/integer-little, A:2>>=Bin,
+ A.
+
+match3(N, Bin) ->
+ <<1:12, A:N/binary, B:4>>=Bin,
+ {A,B}.
+
+match4(N, Bin) ->
+ <<1:N, A:4, B/binary>>=Bin,
+ {A,B}.
+
+match5(X, Y) ->
+ case X of
+ a ->
+ Y2 = 8
+ end,
+ <<5:Y2>> = Y.
+
+gen1(N, S, A) ->
+ <<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>.
+
+gen2(N, S, A) ->
+ <<A:S/little, A:(N-S)/little>>.
+
+in_guard(Bin, A) when <<A:14,3:2>> == Bin -> ok;
+in_guard(_, _) -> no.
diff --git a/lib/hipe/test/hipe.spec b/lib/hipe/test/hipe.spec
new file mode 100644
index 0000000000..2894f40354
--- /dev/null
+++ b/lib/hipe/test/hipe.spec
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+
+{alias, tests, "../hipe_test"}.
+
+{suites, tests, all}.
+
diff --git a/lib/hipe/test/hipe_SUITE.erl b/lib/hipe/test/hipe_SUITE.erl
new file mode 100644
index 0000000000..554bc972f6
--- /dev/null
+++ b/lib/hipe/test/hipe_SUITE.erl
@@ -0,0 +1,55 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(hipe_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [app, appup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> {skip, "HiPE not available or enabled"};
+ _ -> Config
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the hipe app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(hipe, tolerant).
+
+appup() ->
+ [{doc, "Test that the hipe appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ AppupFile = "hipe.appup",
+ AppupPath = filename:join([code:lib_dir(hipe), "ebin", AppupFile]),
+ {ok, [{_Vsn, [], []}]} = file:consult(AppupPath).
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
new file mode 100644
index 0000000000..c8fdf1600c
--- /dev/null
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -0,0 +1,182 @@
+-module(hipe_testsuite_driver).
+
+-export([create_all_suites/0, run/3]).
+
+-include_lib("kernel/include/file.hrl").
+
+-type testcase() :: atom().
+-type file_type() :: 'device' | 'directory' | 'regular' | 'other'.
+-type ext_posix() :: file:posix() | 'badarg'.
+
+-define(suite_suffix, "_SUITE").
+-define(data_folder, "_data").
+-define(suite_data, ?suite_suffix ++ ?data_folder).
+
+-record(suite, {suitename :: string(),
+ outputfile :: file:io_device(),
+ testcases :: [testcase()]}).
+
+-spec create_all_suites() -> 'ok'.
+
+create_all_suites() ->
+ {ok, Cwd} = file:get_cwd(),
+ Suites = get_suites(Cwd),
+ lists:foreach(fun create_suite/1, Suites).
+
+-spec get_suites(file:filename()) -> [string()].
+
+get_suites(Dir) ->
+ case file:list_dir(Dir) of
+ {error, _} -> [];
+ {ok, Filenames} ->
+ FullFilenames = [filename:join(Dir, F) || F <- Filenames],
+ Dirs = [suffix(filename:basename(F), ?suite_data) ||
+ F <- FullFilenames,
+ file_type(F) =:= {ok, 'directory'}],
+ [S || {yes, S} <- Dirs]
+ end.
+
+suffix(String, Suffix) ->
+ case string:rstr(String, Suffix) of
+ 0 -> no;
+ Index ->
+ case string:substr(String, Index) =:= Suffix of
+ true -> {yes, string:sub_string(String, 1, Index-1)};
+ false -> no
+ end
+ end.
+
+-spec file_type(file:filename()) -> {ok, file_type()} | {error, ext_posix()}.
+
+file_type(Filename) ->
+ case file:read_file_info(Filename) of
+ {ok, FI} -> {ok, FI#file_info.type};
+ Error -> Error
+ end.
+
+-spec create_suite(string()) -> 'ok'.
+
+create_suite(SuiteName) ->
+ {ok, Cwd} = file:get_cwd(),
+ SuiteDirN = filename:join(Cwd, SuiteName ++ ?suite_data),
+ OutputFile = generate_suite_file(Cwd, SuiteName),
+ generate_suite(SuiteName, OutputFile, SuiteDirN).
+
+generate_suite_file(Cwd, SuiteName) ->
+ F = filename:join(Cwd, SuiteName ++ ?suite_suffix ++ ".erl"),
+ case file:open(F, [write]) of
+ {ok, IoDevice} -> IoDevice;
+ {error, _} = E -> exit({E, F})
+ end.
+
+generate_suite(SuiteName, OutputFile, SuiteDirN) ->
+ TestCases = list_testcases(SuiteDirN),
+ Suite = #suite{suitename = SuiteName, outputfile = OutputFile,
+ testcases = TestCases},
+ write_suite(Suite),
+ file:close(OutputFile).
+
+list_testcases(Dirname) ->
+ {ok, Files} = list_dir(Dirname, ".erl", true),
+ [list_to_atom(filename:basename(F, ".erl")) || F <- Files].
+
+-spec list_dir(file:filename(), string(), boolean()) ->
+ {error, ext_posix()} | {ok, [file:filename()]}.
+
+list_dir(Dir, Extension, Dirs) ->
+ case file:list_dir(Dir) of
+ {error, _} = Error-> Error;
+ {ok, Filenames} ->
+ FullFilenames = [filename:join(Dir, F) || F <- Filenames],
+ Matches1 = case Dirs of
+ true ->
+ [F || F <- FullFilenames,
+ file_type(F) =:= {ok, 'directory'}];
+ false -> []
+ end,
+ Matches2 = [F || F <- FullFilenames,
+ file_type(F) =:= {ok, 'regular'},
+ filename:extension(F) =:= Extension],
+ {ok, lists:sort(Matches1 ++ Matches2)}
+ end.
+
+write_suite(Suite) ->
+ write_header(Suite),
+ write_testcases(Suite).
+
+write_header(#suite{suitename = SuiteName, outputfile = OutputFile,
+ testcases = TestCases}) ->
+ Exports = format_export(TestCases),
+ TimeLimit = 2, %% with 1 it fails on some slow machines...
+ io:format(OutputFile,
+ "%% ATTENTION!\n"
+ "%% This is an automatically generated file. Do not edit.\n\n"
+ "-module(~s).\n\n"
+ "-export([suite/0, init_per_suite/0, init_per_suite/1,\n"
+ " end_per_suite/1, all/0]).\n"
+ "~s\n\n"
+ "-include_lib(\"common_test/include/ct.hrl\").\n\n"
+ "suite() ->\n"
+ " [{timetrap, {minutes, ~w}}].\n\n"
+ "init_per_suite() ->\n"
+ " [].\n\n"
+ "init_per_suite(Config) ->\n"
+ " case erlang:system_info(hipe_architecture) of\n"
+ " undefined -> {skip, \"HiPE not available or enabled\"};\n"
+ " _ -> Config\n"
+ " end.\n\n"
+ "end_per_suite(_Config) ->\n"
+ " ok.\n\n"
+ "all() ->\n"
+ " ~p.\n\n"
+ "test(Config, TestCase) ->\n"
+ " Dir = ?config(data_dir, Config),\n"
+ " OutDir = ?config(priv_dir, Config),\n"
+ " hipe_testsuite_driver:run(TestCase, Dir, OutDir)."
+ "\n\n",
+ [SuiteName ++ ?suite_suffix, Exports, TimeLimit, TestCases]).
+
+format_export(TestCases) ->
+ TL = [list_to_atom(atom_to_list(N)++"/1") || N <- TestCases],
+ TestCaseString = io_lib:format("-export(~p).", [TL]),
+ strip_quotes(lists:flatten(TestCaseString), []).
+
+strip_quotes([], Result) ->
+ lists:reverse(Result);
+strip_quotes([$' |Rest], Result) ->
+ strip_quotes(Rest, Result);
+strip_quotes([$\, |Rest], Result) ->
+ strip_quotes(Rest, [$\ , $\, |Result]);
+strip_quotes([C|Rest], Result) ->
+ strip_quotes(Rest, [C|Result]).
+
+write_testcases(#suite{outputfile = OutputFile, testcases = TestCases}) ->
+ lists:foreach(fun (T) -> write_testcase(OutputFile, T) end, TestCases).
+
+write_testcase(OutputFile, TestCase) ->
+ io:format(OutputFile,
+ "~p(Config) ->\n"
+ " test(Config, ~p).\n\n",
+ [TestCase, TestCase]).
+
+-spec run(atom(), string(), string()) -> 'ok'.
+
+run(TestCase, Dir, _OutDir) ->
+ F = filename:join(Dir, atom_to_list(TestCase) ++ ".erl"),
+ {ok, TestCase} = compile:file(F),
+ ok = try TestCase:prepare_for_test() catch _:_ -> ok end,
+ %% DataFiles = try TestCase:datafiles() catch _:_ -> [] end,
+ %% lists:foreach(fun (DF) ->
+ %% Src = filename:join(Dir, DF),
+ %% Dst = filename:join(OutDir, DF),
+ %% {ok, _} = file:copy(Src, Dst)
+ %% end, DataFiles),
+ %% try
+ ok = TestCase:test(),
+ HiPEOpts = try TestCase:hipe_options() catch _:_ -> [] end,
+ {ok, TestCase} = hipe:c(TestCase, HiPEOpts),
+ ok = TestCase:test().
+ %% after
+ %% lists:foreach(fun (DF) -> ok end, % = file:delete(DF) end,
+ %% [filename:join(OutDir, D) || D <- DataFiles])
+ %% end.
diff --git a/lib/hipe/util/hipe_digraph.erl b/lib/hipe/util/hipe_digraph.erl
index fcfaa64684..01b1f8c77c 100644
--- a/lib/hipe/util/hipe_digraph.erl
+++ b/lib/hipe/util/hipe_digraph.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
@@ -36,10 +36,10 @@
-type ordset(T) :: [T]. % XXX: temporarily
--record(hipe_digraph, {edges = dict:new() :: dict(),
- rev_edges = dict:new() :: dict(),
+-record(hipe_digraph, {edges = dict:new() :: dict:dict(),
+ rev_edges = dict:new() :: dict:dict(),
leaves = ordsets:new() :: ordset(_), % ???
- nodes = sets:new() :: set()}).
+ nodes = sets:new() :: sets:set()}).
-opaque hdg() :: #hipe_digraph{}.
diff --git a/lib/hipe/util/hipe_dot.erl b/lib/hipe/util/hipe_dot.erl
index e4a47ae0c4..94f7fd60cc 100644
--- a/lib/hipe/util/hipe_dot.erl
+++ b/lib/hipe/util/hipe_dot.erl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%% Copyright Ericsson AB 2004-2014. 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
@@ -70,13 +70,13 @@
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec translate_digraph(digraph(), string(), string()) -> 'ok'.
+-spec translate_digraph(digraph:graph(), string(), string()) -> 'ok'.
translate_digraph(G, FileName, GName) ->
translate_digraph(G, FileName, GName,
fun(X) -> io_lib:format("~p", [X]) end, []).
--spec translate_digraph(digraph(), string(), string(),
+-spec translate_digraph(digraph:graph(), string(), string(),
fun((_) -> string()), [_]) -> 'ok'.
translate_digraph(G, FileName, GName, Fun, Opts) ->
diff --git a/lib/hipe/util/hipe_vectors.hrl b/lib/hipe/util/hipe_vectors.hrl
index 043faf4c91..5e24db238d 100644
--- a/lib/hipe/util/hipe_vectors.hrl
+++ b/lib/hipe/util/hipe_vectors.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -24,5 +24,5 @@
-endif.
-ifdef(USE_GBTREES).
--type hipe_vector() :: gb_tree().
+-type hipe_vector() :: gb_trees:tree().
-endif.
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index ed4b4dc8d2..fb7e4b91a0 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.10.2.2
+HIPE_VSN = 3.10.3
diff --git a/lib/hipe/x86/hipe_x86_assemble.erl b/lib/hipe/x86/hipe_x86_assemble.erl
index 7878c7219d..3f756769c4 100644
--- a/lib/hipe/x86/hipe_x86_assemble.erl
+++ b/lib/hipe/x86/hipe_x86_assemble.erl
@@ -21,7 +21,6 @@
%%%
%%% TODO:
%%% - Simplify combine_label_maps and mk_data_relocs.
-%%% - Move find_const to hipe_pack_constants?
-ifdef(HIPE_AMD64).
-define(HIPE_X86_ASSEMBLE, hipe_amd64_assemble).
@@ -80,8 +79,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
%% ?debug_msg("Constants are ~w bytes\n",[ConstSize])),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -442,7 +441,7 @@ translate_imm(#x86_imm{value=Imm}, Context, MayTrunc8) ->
case Imm of
{Label,constant} ->
{MFA,ConstMap} = Context,
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{constant,ConstNo};
{Label,closure} ->
{closure,Label};
@@ -712,7 +711,7 @@ resolve_jmp_switch_arg(I, _Context) ->
{rm64,hipe_amd64_encode:rm_mem(EA)}.
-else.
resolve_jmp_switch_arg(I, {MFA,ConstMap}) ->
- ConstNo = find_const({MFA,hipe_x86:jmp_switch_jtab(I)}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,hipe_x86:jmp_switch_jtab(I)}, ConstMap),
Disp32 = {?LOAD_ADDRESS,{constant,ConstNo}},
SINDEX = ?HIPE_X86_ENCODE:sindex(2, hipe_x86:temp_reg(hipe_x86:jmp_switch_temp(I))),
EA = ?HIPE_X86_ENCODE:ea_disp32_sindex(Disp32, SINDEX), % this creates a SIB implicitly
@@ -932,37 +931,6 @@ resolve_x87_binop_args(Src=#x86_fpreg{}, Dst=#x86_fpreg{})->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({MFA,L},LabelMap) ->
- gb_trees:get({MFA,L}, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -1001,14 +969,3 @@ fill_spaces(N) when N > 0 ->
fill_spaces(N-1);
fill_spaces(0) ->
[].
-
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N,[_|R]) ->
- find_const(N,R);
-find_const(C,[]) ->
- ?EXIT({constant_not_found,C}).
diff --git a/lib/ic/src/ic.app.src b/lib/ic/src/ic.app.src
index 29aa6def00..7dd47ac9c6 100644
--- a/lib/ic/src/ic.app.src
+++ b/lib/ic/src/ic.app.src
@@ -46,7 +46,8 @@
{registered, []},
{applications, [stdlib, kernel]},
{env, []},
- {mod, {ic, []}}
+ {mod, {ic, []}},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/ic/src/ic.erl b/lib/ic/src/ic.erl
index c0742cf7bd..4f3e8d180c 100644
--- a/lib/ic/src/ic.erl
+++ b/lib/ic/src/ic.erl
@@ -250,7 +250,7 @@ make_erl_options(Opts) ->
Optimize = Opts#options.optimize,
PreProc =
lists:flatten(
- lists:map(fun(D) -> io_lib:format("-I\"~s\" ", [ic_util:to_list(D)]) end,
+ lists:map(fun(D) -> io_lib:format("-I\"~ts\" ", [ic_util:to_list(D)]) end,
Includes1)++
lists:map(
fun ({Name, Value}) ->
diff --git a/lib/ic/src/ic_codegen.erl b/lib/ic/src/ic_codegen.erl
index f611c69bea..82431b79a2 100644
--- a/lib/ic/src/ic_codegen.erl
+++ b/lib/ic/src/ic_codegen.erl
@@ -216,12 +216,14 @@ exp_to_string({F,N}) -> io_lib:format("~p/~p", [ic_util:to_atom(F), N]).
%%--------------------------------------------------------------------
emit_stub_head(_G, ignore, _Name, _) -> ignore;
emit_stub_head(G, F1, Name, erlang) ->
+ comment(F1, " coding: latin-1", []),
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) ->
+ comment(F1, " coding: latin-1", []),
ic_erl_template:emit_header(G, F1, Name),
F1;
emit_stub_head(_G, F1, _Name, erlang_template_no_gen) ->
@@ -259,6 +261,7 @@ compiler_vsn(Vsn) ->
%% Name is Fully scoped (undescore) name of interface or module
emit_hrl_head(_G, ignore, _Name, _) -> ignore;
emit_hrl_head(G, Fd, Name, erlang) ->
+ comment(Fd, " coding: latin-1", []),
mcomment(Fd, ["Erlang header file" |
hrl_header(G, Name)]),
nl(Fd),
diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl
index 54701f7438..e54304ebaa 100644
--- a/lib/ic/src/ic_pp.erl
+++ b/lib/ic/src/ic_pp.erl
@@ -257,15 +257,15 @@ run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir,
tokenise(File, FileName) ->
{Result, _L} = token(File, 2, [], not_set, 0),
- FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 ~p~n",[FileName]))),
+ FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\"~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]))),
+ FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\" 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]))),
+ FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p \"~ts\" 2~n~n",[IncLine-1,PrevFile]))),
FileInfoEnd = [{file_info, FI_end}],
{Result, FileInfoStart, FileInfoEnd}.
% [FileInfoStart | Result] ++ FileInfoEnd.
@@ -1942,7 +1942,7 @@ read_inc_file(FileName, IncDir, Mio) ->
FileList = binary_to_list(Bin),
{ok, AbsFile, FileList};
{error, Text} ->
- {error, Text}
+ {error, Text}
end;
true ->
skip
diff --git a/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl
index 9e49305c3c..cbcf32515e 100644
--- a/lib/ic/test/java_client_erl_server_SUITE.erl
+++ b/lib/ic/test/java_client_erl_server_SUITE.erl
@@ -288,7 +288,7 @@ classpath(Dir) ->
cmd(Cmd) ->
PortOpts = [{line,80},eof,exit_status,stderr_to_stdout],
- io:format("<cmd> ~s~n", [Cmd]),
+ io:format("<cmd> ~ts~n", [Cmd]),
case catch open_port({spawn,Cmd}, PortOpts) of
Port when is_port(Port) ->
Result = cmd_loop(Port, []),
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index fe27d095d3..2ffbbad444 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.3.4
+IC_VSN = 4.3.5
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index 520db1b457..5674599ac5 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -192,7 +192,12 @@ do_open(Pid, OpenOptions, TLSOpts) ->
'ok' | {'error', Reason :: 'euser' | common_reason()}.
user(Pid, User, Pass) ->
- call(Pid, {user, User, Pass}, atom).
+ case {is_name_sane(User), is_name_sane(Pass)} of
+ {true, true} ->
+ call(Pid, {user, User, Pass}, atom);
+ _ ->
+ {error, euser}
+ end.
-spec user(Pid :: pid(),
User :: string(),
@@ -201,7 +206,12 @@ user(Pid, User, Pass) ->
'ok' | {'error', Reason :: 'euser' | common_reason()}.
user(Pid, User, Pass, Acc) ->
- call(Pid, {user, User, Pass, Acc}, atom).
+ case {is_name_sane(User), is_name_sane(Pass), is_name_sane(Acc)} of
+ {true, true, true} ->
+ call(Pid, {user, User, Pass, Acc}, atom);
+ _ ->
+ {error, euser}
+ end.
%%--------------------------------------------------------------------------
@@ -216,7 +226,12 @@ user(Pid, User, Pass, Acc) ->
'ok' | {'error', Reason :: 'eacct' | common_reason()}.
account(Pid, Acc) ->
- call(Pid, {account, Acc}, atom).
+ case is_name_sane(Acc) of
+ true ->
+ call(Pid, {account, Acc}, atom);
+ _ ->
+ {error, eacct}
+ end.
%%--------------------------------------------------------------------------
@@ -262,7 +277,12 @@ lpwd(Pid) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
cd(Pid, Dir) ->
- call(Pid, {cd, Dir}, atom).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {cd, Dir}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -305,7 +325,12 @@ ls(Pid) ->
{'error', Reason :: restriction_reason() | common_reason()}.
ls(Pid, Dir) ->
- call(Pid, {dir, long, Dir}, string).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {dir, long, Dir}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -333,7 +358,12 @@ nlist(Pid) ->
{'error', Reason :: restriction_reason() | common_reason()}.
nlist(Pid, Dir) ->
- call(Pid, {dir, short, Dir}, string).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {dir, short, Dir}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -349,7 +379,12 @@ nlist(Pid, Dir) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
rename(Pid, Old, New) ->
- call(Pid, {rename, Old, New}, string).
+ case {is_name_sane(Old), is_name_sane(New)} of
+ {true, true} ->
+ call(Pid, {rename, Old, New}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -365,7 +400,12 @@ rename(Pid, Old, New) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
delete(Pid, File) ->
- call(Pid, {delete, File}, string).
+ case is_name_sane(File) of
+ true ->
+ call(Pid, {delete, File}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -380,7 +420,12 @@ delete(Pid, File) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
mkdir(Pid, Dir) ->
- call(Pid, {mkdir, Dir}, atom).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {mkdir, Dir}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -395,7 +440,12 @@ mkdir(Pid, Dir) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
rmdir(Pid, Dir) ->
- call(Pid, {rmdir, Dir}, atom).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {rmdir, Dir}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -437,7 +487,12 @@ recv(Pid, RemotFileName) ->
'ok' | {'error', Reason :: term()}.
recv(Pid, RemotFileName, LocalFileName) ->
- call(Pid, {recv, RemotFileName, LocalFileName}, atom).
+ case is_name_sane(RemotFileName) of
+ true ->
+ call(Pid, {recv, RemotFileName, LocalFileName}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -456,7 +511,12 @@ recv(Pid, RemotFileName, LocalFileName) ->
{'error', Reason :: restriction_reason() | common_reason()}.
recv_bin(Pid, RemoteFile) ->
- call(Pid, {recv_bin, RemoteFile}, bin).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {recv_bin, RemoteFile}, bin);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -473,7 +533,12 @@ recv_bin(Pid, RemoteFile) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
recv_chunk_start(Pid, RemoteFile) ->
- call(Pid, {recv_chunk_start, RemoteFile}, atom).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {recv_chunk_start, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -521,7 +586,12 @@ send(Pid, LocalFileName) ->
shortage_reason()}.
send(Pid, LocalFileName, RemotFileName) ->
- call(Pid, {send, LocalFileName, RemotFileName}, atom).
+ case is_name_sane(RemotFileName) of
+ true ->
+ call(Pid, {send, LocalFileName, RemotFileName}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -541,7 +611,12 @@ send(Pid, LocalFileName, RemotFileName) ->
shortage_reason()}.
send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) ->
- call(Pid, {send_bin, Bin, RemoteFile}, atom);
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {send_bin, Bin, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end;
send_bin(_Pid, _Bin, _RemoteFile) ->
{error, enotbinary}.
@@ -559,7 +634,12 @@ send_bin(_Pid, _Bin, _RemoteFile) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
send_chunk_start(Pid, RemoteFile) ->
- call(Pid, {send_chunk_start, RemoteFile}, atom).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {send_chunk_start, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -575,7 +655,12 @@ send_chunk_start(Pid, RemoteFile) ->
'ok' | {'error', Reason :: term()}.
append_chunk_start(Pid, RemoteFile) ->
- call(Pid, {append_chunk_start, RemoteFile}, atom).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {append_chunk_start, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -683,7 +768,12 @@ append(Pid, LocalFileName) ->
'ok' | {'error', Reason :: term()}.
append(Pid, LocalFileName, RemotFileName) ->
- call(Pid, {append, LocalFileName, RemotFileName}, atom).
+ case is_name_sane(RemotFileName) of
+ true ->
+ call(Pid, {append, LocalFileName, RemotFileName}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -705,7 +795,12 @@ append(Pid, LocalFileName, RemotFileName) ->
shortage_reason()}.
append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) ->
- call(Pid, {append_bin, Bin, RemoteFile}, atom);
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {append_bin, Bin, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end;
append_bin(_Pid, _Bin, _RemoteFile) ->
{error, enotbinary}.
@@ -2302,6 +2397,15 @@ send_bin(State, Bin) ->
mk_cmd(Fmt, Args) ->
[io_lib:format(Fmt, Args)| [?CR, ?LF]]. % Deep list ok.
+is_name_sane([]) ->
+ true;
+is_name_sane([?CR| _]) ->
+ false;
+is_name_sane([?LF| _]) ->
+ false;
+is_name_sane([_| Rest]) ->
+ is_name_sane(Rest).
+
pwd_result(Lines) ->
{_, [?DOUBLE_QUOTE | Rest]} =
lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Lines),
diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl
index 69900bae65..134115bdfa 100644
--- a/lib/inets/src/http_client/httpc_cookie.erl
+++ b/lib/inets/src/http_client/httpc_cookie.erl
@@ -335,7 +335,8 @@ add_domain(Str, #http_cookie{domain = Domain}) ->
Str ++ "; $Domain=" ++ Domain.
parse_set_cookies(CookieHeaders, DefaultPathDomain) ->
- SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders],
+ %% empty Set-Cookie header is invalid according to RFC but some sites violate it
+ SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders, Value /= ""],
Cookies = [parse_set_cookie(SetCookieHeader, DefaultPathDomain) ||
SetCookieHeader <- SetCookieHeaders],
%% print_cookies("Parsed Cookies", Cookies),
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 80c8b2439e..88e08be789 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -55,8 +55,8 @@
headers, % #http_response_h{}
body, % binary()
mfa, % {Module, Function, Args}
- pipeline = queue:new(), % queue()
- keep_alive = queue:new(), % queue()
+ pipeline = queue:new(), % queue:queue()
+ keep_alive = queue:new(), % queue:queue()
status, % undefined | new | pipeline | keep_alive | close | {ssl_tunnel, Request}
canceled = [], % [RequestId]
max_header_size = nolimit, % nolimit | integer()
@@ -1119,15 +1119,8 @@ handle_http_body(Body, #state{headers = Headers,
handle_response(State#state{headers = NewHeaders,
body = NewBody})
end;
- Encoding when is_list(Encoding) ->
- ?hcrt("handle_http_body - encoding", [{encoding, Encoding}]),
- NewState = answer_request(Request,
- httpc_response:error(Request,
- unknown_encoding),
- State),
- {stop, normal, NewState};
- _ ->
- ?hcrt("handle_http_body - other", []),
+ Enc when Enc =:= "identity"; Enc =:= undefined ->
+ ?hcrt("handle_http_body - identity", []),
Length =
list_to_integer(Headers#http_response_h.'content-length'),
case ((Length =< MaxBodySize) orelse (MaxBodySize =:= nolimit)) of
@@ -1149,12 +1142,19 @@ handle_http_body(Body, #state{headers = Headers,
body_too_big),
State),
{stop, normal, NewState}
- end
+ end;
+ Encoding when is_list(Encoding) ->
+ ?hcrt("handle_http_body - other", [{encoding, Encoding}]),
+ NewState = answer_request(Request,
+ httpc_response:error(Request,
+ unknown_encoding),
+ State),
+ {stop, normal, NewState}
end.
handle_response(#state{status = new} = State) ->
?hcrd("handle response - status = new", []),
- handle_response(check_persistent(State));
+ handle_response(try_to_enable_pipeline_or_keep_alive(State));
handle_response(#state{request = Request,
status = Status,
@@ -1429,22 +1429,39 @@ is_keep_alive_enabled_server(_,_) ->
is_keep_alive_connection(Headers, #session{client_close = ClientClose}) ->
(not ((ClientClose) orelse httpc_response:is_server_closing(Headers))).
-check_persistent(
- #state{session = #session{type = Type} = Session,
+try_to_enable_pipeline_or_keep_alive(
+ #state{session = Session,
+ request = #request{method = Method},
status_line = {Version, _, _},
headers = Headers,
- profile_name = ProfileName} = State) ->
+ profile_name = ProfileName} = State) ->
+ ?hcrd("try to enable pipeline or keep-alive",
+ [{version, Version},
+ {headers, Headers},
+ {session, Session}]),
case is_keep_alive_enabled_server(Version, Headers) andalso
- is_keep_alive_connection(Headers, Session) of
+ is_keep_alive_connection(Headers, Session) of
true ->
- mark_persistent(ProfileName, Session),
- State#state{status = Type};
+ case (is_pipeline_enabled_client(Session) andalso
+ httpc_request:is_idempotent(Method)) of
+ true ->
+ insert_session(Session, ProfileName),
+ State#state{status = pipeline};
+ false ->
+ insert_session(Session, ProfileName),
+ %% Make sure type is keep_alive in session
+ %% as it in this case might be pipeline
+ NewSession = Session#session{type = keep_alive},
+ State#state{status = keep_alive,
+ session = NewSession}
+ end;
false ->
State#state{status = close}
end.
answer_request(#request{id = RequestId, from = From} = Request, Msg,
- #state{timers = Timers,
+ #state{session = Session,
+ timers = Timers,
profile_name = ProfileName} = State) ->
?hcrt("answer request", [{request, Request}, {msg, Msg}]),
httpc_response:send(From, Msg),
@@ -1454,14 +1471,19 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg,
Timer = {RequestId, TimerRef},
cancel_timer(TimerRef, {timeout, Request#request.id}),
httpc_manager:request_done(RequestId, ProfileName),
+ NewSession = maybe_make_session_available(ProfileName, Session),
Timers2 = Timers#timers{request_timers = lists:delete(Timer,
RequestTimers)},
State#state{request = Request#request{from = answer_sent},
+ session = NewSession,
timers = Timers2}.
-mark_persistent(ProfileName, Session) ->
- update_session(ProfileName, Session, #session.persistent, true),
- Session#session{persistent = true}.
+maybe_make_session_available(ProfileName,
+ #session{available = false} = Session) ->
+ update_session(ProfileName, Session, #session.available, true),
+ Session#session{available = true};
+maybe_make_session_available(_ProfileName, Session) ->
+ Session.
cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) ->
cancel_timer(QTmr, timeout_queue),
@@ -1829,7 +1851,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
[ProfileName, SessionId, Pos, Value,
(catch httpc_manager:which_session_info(ProfileName)),
Session,
- (catch httpc_manager:lookup_session(ProfileName, SessionId)),
+ (catch httpc_manager:lookup_session(SessionId, ProfileName)),
T, E]),
exit({failed_updating_session,
[{profile, ProfileName},
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index d5b3dd2a2a..add5d11dfa 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
@@ -143,8 +143,8 @@
%% true | false
%% This will be true, when a response has been received for
- %% the first request and the server has not closed the connection
- persistent = false
+ %% the first request. See type above.
+ available = false
}).
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index a3ed371e61..48a9c32454 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -451,7 +451,7 @@ do_init(ProfileName, CookiesDir) ->
%%--------------------------------------------------------------------
handle_call({request, Request}, _, State) ->
?hcri("request", [{request, Request}]),
- case (catch handle_request(Request, State, false)) of
+ case (catch handle_request(Request, State)) of
{reply, Msg, NewState} ->
{reply, Msg, NewState};
Error ->
@@ -511,7 +511,7 @@ handle_cast({retry_or_redirect_request, {Time, Request}},
{noreply, State};
handle_cast({retry_or_redirect_request, Request}, State) ->
- case (catch handle_request(Request, State, true)) of
+ case (catch handle_request(Request, State)) of
{reply, {ok, _}, NewState} ->
{noreply, NewState};
Error ->
@@ -724,7 +724,7 @@ get_handler_info(Tab) ->
handle_request(#request{settings =
#http_options{version = "HTTP/0.9"}} = Request,
- State, _) ->
+ State) ->
%% Act as an HTTP/0.9 client that does not know anything
%% about persistent connections
@@ -737,7 +737,7 @@ handle_request(#request{settings =
handle_request(#request{settings =
#http_options{version = "HTTP/1.0"}} = Request,
- State, _) ->
+ State) ->
%% Act as an HTTP/1.0 client that does not
%% use persistent connections
@@ -748,13 +748,13 @@ handle_request(#request{settings =
start_handler(NewRequest#request{headers = NewHeaders}, State),
{reply, {ok, NewRequest#request.id}, State};
-handle_request(Request, State = #state{options = Options}, Retry) ->
+handle_request(Request, State = #state{options = Options}) ->
NewRequest = handle_cookies(generate_request_id(Request), State),
SessionType = session_type(Options),
case select_session(Request#request.method,
Request#request.address,
- Request#request.scheme, SessionType, State, Retry) of
+ Request#request.scheme, SessionType, State) of
{ok, HandlerPid} ->
pipeline_or_keep_alive(NewRequest, HandlerPid, State);
no_connection ->
@@ -778,7 +778,6 @@ start_handler(#request{id = Id,
#state{profile_name = ProfileName,
handler_db = HandlerDb,
options = Options}) ->
- ClientClose = httpc_request:is_client_closing(Request#request.headers),
{ok, Pid} =
case is_inets_manager() of
true ->
@@ -789,18 +788,13 @@ start_handler(#request{id = Id,
end,
HandlerInfo = {Id, Pid, From},
ets:insert(HandlerDb, HandlerInfo),
- insert_session(#session{id = {Request#request.address, Pid},
- scheme = Request#request.scheme,
- client_close = ClientClose,
- type = session_type(Options)
- }, ProfileName),
erlang:monitor(process, Pid).
select_session(Method, HostPort, Scheme, SessionType,
#state{options = #options{max_pipeline_length = MaxPipe,
max_keep_alive_length = MaxKeepAlive},
- session_db = SessionDb}, Retry) ->
+ session_db = SessionDb}) ->
?hcrd("select session", [{session_type, SessionType},
{max_pipeline_length, MaxPipe},
{max_keep_alive_length, MaxKeepAlive}]),
@@ -813,23 +807,13 @@ select_session(Method, HostPort, Scheme, SessionType,
%% client_close, scheme and type specified.
%% The fields id (part of: HandlerPid) and queue_length
%% specified.
- Pattern = case (Retry andalso SessionType == pipeline) of
- true ->
- #session{id = {HostPort, '$1'},
- client_close = false,
- scheme = Scheme,
- queue_length = '$2',
- type = SessionType,
- persistent = true,
- _ = '_'};
- false ->
- #session{id = {HostPort, '$1'},
- client_close = false,
- scheme = Scheme,
- queue_length = '$2',
- type = SessionType,
- _ = '_'}
- end,
+ Pattern = #session{id = {HostPort, '$1'},
+ client_close = false,
+ scheme = Scheme,
+ queue_length = '$2',
+ type = SessionType,
+ available = true,
+ _ = '_'},
%% {'_', {HostPort, '$1'}, false, Scheme, '_', '$2', SessionTyp},
Candidates = ets:match(SessionDb, Pattern),
?hcrd("select session", [{host_port, HostPort},
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 16a080f8e2..6fc07f033c 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -66,7 +66,7 @@ get_bin(_Env,_Input) ->
<INPUT TYPE=\"text\" NAME=\"input2\">
<INPUT TYPE=\"submit\"><BR>
</FORM>" ++ "\n"),
- footer()].
+ list_to_binary(footer())].
post(_Env,[]) ->
[header(),
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index a6dd364c2d..9eae962d03 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -110,4 +110,6 @@
{registered,[inets_sup, httpc_manager]},
%% If the "new" ssl is used then 'crypto' must be started before inets.
{applications,[kernel,stdlib]},
- {mod,{inets_app,[]}}]}.
+ {mod,{inets_app,[]}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","runtime_tools-1.8.14",
+ "mnesia-4.12","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index c63dcafa6c..dd081962cc 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,7 +1,7 @@
-%% This is an -*- erlang -*- file.
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -15,11 +15,13 @@
%% under the License.
%%
%% %CopyrightEnd%
-
{"%VSN%",
[
- {<<"5\\.*">>, [{restart_application, inets}]}
- ],
+ {"5.9.8", [{load_module, ftp, soft_purge, soft_purge, []}]},
+ {<<"5\\..*">>,[{restart_application, inets}]}
+ ],
[
- {<<"5\\.*">>, [{restart_application, inets}]}
-]}.
+ {"5.9.8", [{load_module, ftp, soft_purge, soft_purge, []}]},
+ {<<"5\\..*">>,[{restart_application, inets}]}
+ ]
+}.
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index c156b34406..609396273d 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2014. 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
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
index 35f21cc74d..daee1bdcdc 100644
--- a/lib/inets/test/ftp_suite_lib.erl
+++ b/lib/inets/test/ftp_suite_lib.erl
@@ -1266,6 +1266,8 @@ read_log_6035([]) ->
%%--------------------------------------------------------------------
do_user(Pid) ->
{error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS),
+ {error, euser} = ftp:user(Pid, ?FTP_USER++"\r\nPASS "++?FTP_PASS, ?FTP_PASS),
+ {error, euser} = ftp:user(Pid, ?FTP_USER, ?FTP_PASS++"\r\nCWD ."),
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
ok.
@@ -1278,6 +1280,7 @@ do_pwd(Pid) ->
do_cd(Pid) ->
ok = ftp:cd(Pid, "/pub"),
{error, epath} = ftp:cd(Pid, ?BAD_DIR),
+ {error, efnamena} = ftp:cd(Pid, "/pub\r\nCWD ."),
ok.
do_lcd(Pid, Dir) ->
@@ -1294,11 +1297,14 @@ do_ls(Pid) ->
%% directory, but can also be a filename or a group
%% of files (including wildcards).
{ok, _} = ftp:ls(Pid, "incom*"),
+ %% but \r\n can't be in the wildcard
+ {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."),
ok.
do_nlist(Pid, WildcardSupport) ->
{ok, _} = ftp:nlist(Pid),
{ok, _} = ftp:nlist(Pid, "incoming"),
+ {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."),
%% neither nlist nor ls operates on a directory
%% they operate on a pathname, which *can* be a
%% directory, but can also be a filename or a group
@@ -1324,6 +1330,8 @@ do_rename(Pid, Config) ->
ftp:delete(Pid, NewLFile), % reset
ok = ftp:send(Pid, LFile),
{error, epath} = ftp:rename(Pid, NewLFile, LFile),
+ {error, efnamena} = ftp:rename(Pid, NewLFile++"\r\nRNTO "++LFile++"\r\nRNFR "++NewLFile, LFile),
+ {error, efnamena} = ftp:rename(Pid, NewLFile, LFile++"\r\nCWD ."),
ok = ftp:rename(Pid, LFile, NewLFile),
ftp:delete(Pid, LFile), % cleanup
ftp:delete(Pid, NewLFile), % cleanup
@@ -1338,6 +1346,7 @@ do_delete(Pid, Config) ->
ok = ftp:cd(Pid, "incoming"),
ok = ftp:lcd(Pid, PrivDir),
ftp:delete(Pid,LFile), % reset
+ {error, efnamena} = ftp:delete(Pid,LFile++"\r\nCWD ."),
ok = ftp:send(Pid, LFile),
ok = ftp:delete(Pid,LFile),
ok.
@@ -1348,6 +1357,8 @@ do_mkdir(Pid) ->
integer_to_list(B) ++ "_" ++ integer_to_list(C),
ok = ftp:cd(Pid, "incoming"),
{ok, CurrDir} = ftp:pwd(Pid),
+ {error, efnamena} = ftp:mkdir(Pid, NewDir++"\r\nCWD ."),
+ {error, efnamena} = ftp:rmdir(Pid, NewDir++"\r\nCWD ."),
ok = ftp:mkdir(Pid, NewDir),
ok = ftp:cd(Pid, NewDir),
ok = ftp:cd(Pid, CurrDir),
@@ -1363,6 +1374,7 @@ do_send(Pid, Config) ->
ok = file:write_file(AbsLFile, list_to_binary(Contents)),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:lcd(Pid, PrivDir),
+ {error, efnamena} = ftp:send(Pid, LFile, RFile++"1\r\nCWD ."),
ok = ftp:send(Pid, LFile, RFile),
{ok, RFilesString} = ftp:nlist(Pid),
RFiles = split(RFilesString),
@@ -1392,6 +1404,7 @@ do_append(Pid, Config) ->
ftp:delete(Pid, RFile),
ftp:delete(Pid, LFile),
+ {error, efnamena} = ftp:append(Pid, LFile, RFile++"1\r\nCWD ."),
ok = ftp:append(Pid, LFile, RFile),
ok = ftp:append(Pid, LFile, RFile),
ok = ftp:append(Pid, LFile),
@@ -1413,6 +1426,7 @@ do_send_bin(Pid, Config) ->
Bin = list_to_binary(Contents),
ok = ftp:cd(Pid, "incoming"),
{error, enotbinary} = ftp:send_bin(Pid, Contents, File),
+ {error, efnamena} = ftp:send_bin(Pid, Bin, File++"1\r\nCWD ."),
ok = ftp:send_bin(Pid, Bin, File),
{ok, RFilesString} = ftp:nlist(Pid),
RFiles = split(RFilesString),
@@ -1426,6 +1440,7 @@ do_append_bin(Pid, Config) ->
Bin = list_to_binary(Contents),
ok = ftp:cd(Pid, "incoming"),
{error, enotbinary} = ftp:append_bin(Pid, Contents, File),
+ {error, efnamena} = ftp:append_bin(Pid, Bin, File++"1\r\nCWD ."),
ok = ftp:append_bin(Pid, Bin, File),
ok = ftp:append_bin(Pid, Bin, File),
%% Control the contents of the file
@@ -1438,6 +1453,7 @@ do_send_chunk(Pid, Config) ->
Contents = "ftp_SUITE test ...",
Bin = list_to_binary(Contents),
ok = ftp:cd(Pid, "incoming"),
+ {error, efnamena} = ftp:send_chunk_start(Pid, File++"1\r\nCWD ."),
ok = ftp:send_chunk_start(Pid, File),
{error, echunk} = ftp:cd(Pid, "incoming"),
{error, enotbinary} = ftp:send_chunk(Pid, Contents),
@@ -1454,6 +1470,7 @@ do_append_chunk(Pid, Config) ->
File = ?config(file, Config),
Contents = ["ER","LE","RL"],
ok = ftp:cd(Pid, "incoming"),
+ {error, efnamena} = ftp:append_chunk_start(Pid, File++"1\r\nCWD ."),
ok = ftp:append_chunk_start(Pid, File),
{error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)),
ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))),
@@ -1480,6 +1497,7 @@ do_recv(Pid, Config) ->
ok = file:delete(AbsFile), % cleanup
test_server:sleep(100),
ok = ftp:lcd(Pid, PrivDir),
+ {error, efnamena} = ftp:recv(Pid, File++"\r\nCWD ."),
ok = ftp:recv(Pid, File),
{ok, Files} = file:list_dir(PrivDir),
true = lists:member(File, Files),
@@ -1495,6 +1513,7 @@ do_recv_bin(Pid, Config) ->
ok = ftp:cd(Pid, "incoming"),
ok = ftp:send_bin(Pid, Bin1, File),
test_server:sleep(100),
+ {error, efnamena} = ftp:recv_bin(Pid, File++"\r\nCWD ."),
{ok, Bin2} = ftp:recv_bin(Pid, File),
ok = ftp:delete(Pid, File), % cleanup
Contents2 = binary_to_list(Bin2),
@@ -1520,6 +1539,7 @@ do_recv_chunk(Pid, Config) ->
ok = ftp:send_bin(Pid, Bin1, File),
test_server:sleep(100),
{error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>),
+ {error, efnamena} = ftp:recv_chunk_start(Pid, File++"\r\nCWD ."),
ok = ftp:recv_chunk_start(Pid, File),
{ok, Contents2} = recv_chunk(Pid, <<>>),
ok = ftp:delete(Pid, File), % cleanup
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index fe6edd504e..b1b799c953 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -91,6 +91,7 @@ only_simulated() ->
[
cookie,
cookie_profile,
+ empty_set_cookie,
trace,
stream_once,
no_content_204,
@@ -104,6 +105,7 @@ only_simulated() ->
remote_socket_close,
remote_socket_close_async,
transfer_encoding,
+ transfer_encoding_identity,
redirect_loop,
redirect_moved_permanently,
redirect_multiple_choises,
@@ -296,6 +298,9 @@ trace(Config) when is_list(Config) ->
pipeline(Config) when is_list(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), []},
{ok, _} = httpc:request(get, Request, [], [], pipeline),
+
+ %% Make sure pipeline session is registerd
+ test_server:sleep(4000),
keep_alive_requests(Request, pipeline).
%%--------------------------------------------------------------------
@@ -303,6 +308,9 @@ pipeline(Config) when is_list(Config) ->
persistent_connection(Config) when is_list(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), []},
{ok, _} = httpc:request(get, Request, [], [], persistent),
+
+ %% Make sure pipeline session is registerd
+ test_server:sleep(4000),
keep_alive_requests(Request, persistent).
%%-------------------------------------------------------------------------
@@ -530,6 +538,19 @@ cookie_profile(Config) when is_list(Config) ->
inets:stop(httpc, cookie_test).
%%-------------------------------------------------------------------------
+empty_set_cookie() ->
+ [{doc, "Test empty Set-Cookie header."}].
+empty_set_cookie(Config) when is_list(Config) ->
+ ok = httpc:set_options([{cookies, enabled}]),
+
+ Request0 = {url(group_name(Config), "/empty_set_cookie.html", Config), []},
+
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, Request0, [], []),
+
+ ok = httpc:set_options([{cookies, disabled}]).
+
+%%-------------------------------------------------------------------------
headers_as_is(doc) ->
["Test the option headers_as_is"];
headers_as_is(Config) when is_list(Config) ->
@@ -624,6 +645,12 @@ transfer_encoding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+transfer_encoding_identity(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/identity_transfer_encoding.html", Config),
+ {ok, {{_,200,_}, [_|_], "IDENTITY"}} = httpc:request(URL).
+
+%%-------------------------------------------------------------------------
+
empty_response_header() ->
[{doc, "Test the case that the HTTP server does not send any headers. Solves OTP-6830"}].
empty_response_header(Config) when is_list(Config) ->
@@ -1609,6 +1636,13 @@ handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/identity_transfer_encoding.html",_,_,_,_) ->
+ "HTTP/1.0 200 OK\r\n"
+ "Transfer-Encoding:identity\r\n"
+ "Content-Length:8\r\n"
+ "\r\n"
+ "IDENTITY";
+
handle_uri(_,"/cookie.html",_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"set-cookie:" ++ "test_cookie=true; path=/;" ++
@@ -1616,6 +1650,12 @@ handle_uri(_,"/cookie.html",_,_,_,_) ->
"Content-Length:32\r\n\r\n"++
"<HTML><BODY>foobar</BODY></HTML>";
+handle_uri(_,"/empty_set_cookie.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "set-cookie: \r\n" ++
+ "Content-Length:32\r\n\r\n"++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
handle_uri(_,"/missing_crlf.html",_,_,_,_) ->
"HTTP/1.1 200 ok" ++
"Content-Length:32\r\n" ++
diff --git a/lib/inets/test/httpd_1_0.erl b/lib/inets/test/httpd_1_0.erl
index 53f23b12e0..0836c9e881 100644
--- a/lib/inets/test/httpd_1_0.erl
+++ b/lib/inets/test/httpd_1_0.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -20,7 +20,7 @@
-module(httpd_1_0).
--export([host/4]).
+-export([host/4, trace/4]).
%%-------------------------------------------------------------------------
%% Test cases
@@ -31,3 +31,8 @@ host(Type, Port, Host, Node) ->
"GET / HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]).
+trace(Type, Port, Host, Node)->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "TRACE / HTTP/1.0\r\n\r\n",
+ [{statuscode, 501},
+ {version, "HTTP/1.0"}]).
diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl
index 4b2a5f619d..6a5fc4a18f 100644
--- a/lib/inets/test/httpd_1_1.erl
+++ b/lib/inets/test/httpd_1_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
@@ -22,7 +22,7 @@
-include_lib("kernel/include/file.hrl").
--export([host/4, chunked/4, expect/4, range/4, if_test/5, http_trace/4,
+-export([host/4, chunked/4, expect/4, range/4, if_test/5, trace/4,
head/4, mod_cgi_chunked_encoding_test/5]).
%% -define(all_keys_lower_case,true).
@@ -152,13 +152,13 @@ if_test(Type, Port, Host, Node, DocRoot)->
calendar:datetime_to_gregorian_seconds(FileInfo#file_info.mtime),
Mod = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
- CreatedSec-1)),
+ CreatedSec-1)),
%% Test that we get the data when the file is modified
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\nHost:" ++ Host ++
- "\r\nIf-Modified-Since:" ++
- Mod ++ "\r\n\r\n",
+ "\r\nIf-Modified-Since:" ++
+ Mod ++ "\r\n\r\n",
[{statuscode, 200}]),
Mod1 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec+100)),
@@ -168,74 +168,69 @@ if_test(Type, Port, Host, Node, DocRoot)->
++ Mod1 ++"\r\n\r\n",
[{statuscode, 304}]),
-
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\nHost:" ++ Host ++
- "\r\nIf-Modified-Since:" ++
- "AAA[...]AAAA" ++ "\r\n\r\n",
+ "\r\nIf-Modified-Since:" ++
+ "AAA[...]AAAA" ++ "\r\n\r\n",
[{statuscode, 400}]),
-
-
- Mod2 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
+
+ Mod2 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec+1)),
- %% Control that the If-Unmodified-Header lmits the response
- ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
- "GET / HTTP/1.1\r\nHost:"
- ++ Host ++
- "\r\nIf-Unmodified-Since:" ++ Mod2
- ++ "\r\n\r\n",
- [{statuscode, 200}]),
- Mod3 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
+ %% Control that the If-Unmodified-Header lmits the response
+ ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++
+ "\r\nIf-Unmodified-Since:" ++ Mod2
+ ++ "\r\n\r\n",
+ [{statuscode, 200}]),
+ Mod3 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec-1)),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:"
- ++ Host ++
- "\r\nIf-Unmodified-Since:"++ Mod3
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++
+ "\r\nIf-Unmodified-Since:"++ Mod3
++"\r\n\r\n",
- [{statuscode, 412}]),
+ [{statuscode, 412}]),
- %% Control that we get the body when the etag match
+ %% Control that we get the body when the etag match
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:" ++ Host
- ++"\r\n"++
- "If-Match:"++
- httpd_util:create_etag(FileInfo)++
- "\r\n\r\n",
- [{statuscode, 200}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:" ++
- Host ++ "\r\n"++
- "If-Match:NotEtag\r\n\r\n",
- [{statuscode, 412}]),
+ "GET / HTTP/1.1\r\nHost:" ++ Host
+ ++"\r\n"++
+ "If-Match:"++
+ httpd_util:create_etag(FileInfo)++
+ "\r\n\r\n",
+ [{statuscode, 200}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET / HTTP/1.1\r\nHost:" ++
+ Host ++ "\r\n"++
+ "If-Match:NotEtag\r\n\r\n",
+ [{statuscode, 412}]),
- %% Control the response when the if-none-match header is there
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:"
- ++ Host ++"\r\n"++
- "If-None-Match:NoTaag," ++
- httpd_util:create_etag(FileInfo) ++
- "\r\n\r\n",
- [{statuscode, 304}]),
+ %% Control the response when the if-none-match header is there
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++"\r\n"++
+ "If-None-Match:NoTaag," ++
+ httpd_util:create_etag(FileInfo) ++
+ "\r\n\r\n",
+ [{statuscode, 304}]),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\nHost:"
- ++ Host ++ "\r\n"++
- "If-None-Match:NotEtag,"
- "NeihterEtag\r\n\r\n",
+ ++ Host ++ "\r\n"++
+ "If-None-Match:NotEtag,"
+ "NeihterEtag\r\n\r\n",
[{statuscode,200}]),
ok.
-
-http_trace(Type, Port, Host, Node)->
+
+trace(Type, Port, Host, Node)->
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"TRACE / HTTP/1.1\r\n" ++
"Host:" ++ Host ++ "\r\n" ++
"Max-Forwards:2\r\n\r\n",
- [{statuscode, 200}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "TRACE / HTTP/1.0\r\n\r\n",
- [{statuscode, 501},
- {version, "HTTP/1.0"}]).
+ [{statuscode, 200}]).
head(Type, Port, Host, Node)->
%% mod_include
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
@@ -283,7 +278,7 @@ mod_cgi_chunked_encoding_test(Type, Port, Host, Node, [Request| Rest])->
%%--------------------------------------------------------------------
validateRangeRequest(Socket,Response,ValidBody,C,O,DE)->
receive
- {tcp,Socket,Data} ->
+ {_,Socket,Data} ->
case string:str(Data,"\r\n") of
0->
validateRangeRequest(Socket,
@@ -312,7 +307,7 @@ validateRangeRequest1(Socket, Response, ValidBody) ->
case end_of_header(Response) of
false ->
receive
- {tcp,Socket,Data} ->
+ {_,Socket,Data} ->
validateRangeRequest1(Socket, Response ++ Data,
ValidBody);
_->
@@ -331,10 +326,10 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, {multiPart,Boundary})->
validateMultiPartRangeRequest(Body, ValidBody, Boundary);
false->
receive
- {tcp, Socket, Data} ->
+ {_, Socket, Data} ->
validateRangeRequest2(Socket, Head, Body ++ Data,
ValidBody, {multiPart, Boundary});
- {tcp_closed, Socket} ->
+ {_, Socket} ->
error;
_ ->
error
@@ -353,7 +348,7 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, BodySize)
end;
Size when Size < BodySize ->
receive
- {tcp, Socket, Data} ->
+ {_, Socket, Data} ->
validateRangeRequest2(Socket, Head,
Body ++ Data, ValidBody, BodySize);
_ ->
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index c0d73663d3..3eb8a0818f 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -26,6 +26,7 @@
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
-include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
@@ -33,6 +34,11 @@
-record(httpd_user, {user_name, password, user_data}).
-record(httpd_group, {group_name, userlist}).
+-define(MAX_HEADER_SIZE, 256).
+%% Minutes before failed auths timeout.
+-define(FAIL_EXPIRE_TIME,1).
+%% Seconds before successful auths timeout.
+-define(AUTH_TIMEOUT,5).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -42,22 +48,59 @@ suite() ->
all() ->
[
- {group, http},
- {group, http_limit}
- %%{group, https}
+ {group, http_basic},
+ {group, https_basic},
+ {group, http_limit},
+ {group, https_limit},
+ {group, http_basic_auth},
+ {group, https_basic_auth},
+ {group, http_auth_api},
+ {group, https_auth_api},
+ {group, http_auth_api_dets},
+ {group, https_auth_api_dets},
+ {group, http_auth_api_mnesia},
+ {group, https_auth_api_mnesia},
+ {group, http_htaccess},
+ {group, https_htaccess},
+ {group, http_security},
+ {group, https_security}
].
groups() ->
[
- {http, [], all_groups()},
- %%{https, [], all_groups()},
- {http_limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
- {http_1_1, [], [host, chunked, expect, cgi] ++ http_head() ++ http_get()},
- {http_1_0, [], [host, cgi] ++ http_head() ++ http_get()},
- {http_0_9, [], http_head() ++ http_get()}
+ {http_basic, [], basic_groups()},
+ {https_basic, [], basic_groups()},
+ {http_limit, [], [{group, limit}]},
+ {https_limit, [], [{group, limit}]},
+ {http_basic_auth, [], [{group, basic_auth}]},
+ {https_basic_auth, [], [{group, basic_auth}]},
+ {http_auth_api, [], [{group, auth_api}]},
+ {https_auth_api, [], [{group, auth_api}]},
+ {http_auth_api_dets, [], [{group, auth_api_dets}]},
+ {https_auth_api_dets, [], [{group, auth_api_dets}]},
+ {http_auth_api_mnesia, [], [{group, auth_api_mnesia}]},
+ {https_auth_api_mnesia, [], [{group, auth_api_mnesia}]},
+ {http_htaccess, [], [{group, htaccess}]},
+ {https_htaccess, [], [{group, htaccess}]},
+ {http_security, [], [{group, security}]},
+ {https_security, [], [{group, security}]},
+ {limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {basic_auth, [], [basic_auth_1_1, basic_auth_1_0, basic_auth_0_9]},
+ {auth_api, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {auth_api_dets, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {auth_api_mnesia, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
+ {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
+ {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since] ++ http_head() ++ http_get() ++ load()},
+ {http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
+ {http_0_9, [], http_head() ++ http_get() ++ load()}
].
-all_groups ()->
+basic_groups ()->
[{group, http_1_1},
{group, http_1_0},
{group, http_0_9}
@@ -66,15 +109,27 @@ all_groups ()->
http_head() ->
[head].
http_get() ->
- [alias, get,
- basic_auth,
- esi, ssi].
+ [alias,
+ get,
+ %%actions, Add configuration so that this test mod_action
+ esi,
+ ssi,
+ content_length,
+ bad_hex,
+ missing_CR,
+ max_header,
+ ipv6
+ ].
+load() ->
+ [light, medium
+ %%,heavy
+ ].
+
init_per_suite(Config) ->
PrivDir = ?config(priv_dir, Config),
DataDir = ?config(data_dir, Config),
inets_test_lib:stop_apps([inets]),
- inets_test_lib:start_apps([inets]),
ServerRoot = filename:join(PrivDir, "server_root"),
inets_test_lib:del_dirs(ServerRoot),
DocRoot = filename:join(ServerRoot, "htdocs"),
@@ -82,21 +137,31 @@ init_per_suite(Config) ->
[{server_root, ServerRoot},
{doc_root, DocRoot},
{node, node()},
- {host, inets_test_lib:hostname()} | Config].
+ {host, inets_test_lib:hostname()},
+ {address, getaddr()} | Config].
end_per_suite(_Config) ->
ok.
%%--------------------------------------------------------------------
-init_per_group(https = Group, Config0) ->
- case start_apps(Group) of
- ok ->
- init_httpd(Group, [{type, ssl} | Config0]);
- _ ->
- {skip, "Could not start https apps"}
- end;
-
-init_per_group(Group, Config0) when Group == http; Group == http_limit ->
+init_per_group(Group, Config0) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == https_security
+ ->
+ init_ssl(Group, Config0);
+init_per_group(Group, Config0) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_security
+ ->
+ ok = start_apps(Group),
init_httpd(Group, [{type, ip_comm} | Config0]);
init_per_group(http_1_1, Config) ->
[{http_version, "HTTP/1.1"} | Config];
@@ -104,22 +169,57 @@ init_per_group(http_1_0, Config) ->
[{http_version, "HTTP/1.0"} | Config];
init_per_group(http_0_9, Config) ->
[{http_version, "HTTP/0.9"} | Config];
+init_per_group(http_htaccess = Group, Config) ->
+ Path = ?config(doc_root, Config),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, ?config(address, Config)),
+ ok = start_apps(Group),
+ init_httpd(Group, [{type, ip_comm} | Config]);
+init_per_group(https_htaccess = Group, Config) ->
+ Path = ?config(doc_root, Config),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, ?config(address, Config)),
+ init_ssl(Group, Config);
+init_per_group(auth_api, Config) ->
+ [{auth_prefix, ""} | Config];
+init_per_group(auth_api_dets, Config) ->
+ [{auth_prefix, "dets_"} | Config];
+init_per_group(auth_api_mnesia, Config) ->
+ start_mnesia(?config(node, Config)),
+ [{auth_prefix, "mnesia_"} | Config];
init_per_group(_, Config) ->
Config.
-end_per_group(http, _Config) ->
- ok;
-end_per_group(https, _Config) ->
- ssl:stop();
+
+end_per_group(Group, _Config) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_htaccess;
+ Group == http_security
+ ->
+ inets:stop();
+end_per_group(Group, _Config) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == https_htaccess;
+ Group == http_security
+ ->
+ ssl:stop(),
+ inets:stop();
+
+end_per_group(auth_api_mnesia, _) ->
+ cleanup_mnesia();
+
end_per_group(_, _) ->
ok.
-init_httpd(Group, Config0) ->
- Config1 = proplists:delete(port, Config0),
- Config = proplists:delete(server_pid, Config1),
- {Pid, Port} = server_start(Group, server_config(Group, Config)),
- [{server_pid, Pid}, {port, Port} | Config].
%%--------------------------------------------------------------------
-init_per_testcase(host, Config) ->
+init_per_testcase(Case, Config) when Case == host; Case == trace ->
Prop = ?config(tc_group_properties, Config),
Name = proplists:get_value(name, Prop),
Cb = case Name of
@@ -129,15 +229,15 @@ init_per_testcase(host, Config) ->
httpd_1_1
end,
[{version_cb, Cb} | proplists:delete(version_cb, Config)];
+
+init_per_testcase(range, Config) ->
+ DocRoot = ?config(doc_root, Config),
+ create_range_data(DocRoot),
+ Config;
+
init_per_testcase(_, Config) ->
Config.
-%% init_per_testcase(basic_auth = Case, Config) ->
-%% start_mnesia(?config(node, Config)),
-%% common_init_per_test_case(Case, Config);
-
-%% end_per_testcase(basic_auth, Config) ->
-%% cleanup_mnesia();
end_per_testcase(_Case, _Config) ->
ok.
@@ -163,8 +263,11 @@ get() ->
get(Config) when is_list(Config) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
http_request("GET /index.html ", Version, Host),
[{statuscode, 200},
{header, "Content-Type", "text/html"},
@@ -172,6 +275,15 @@ get(Config) when is_list(Config) ->
{header, "Server"},
{version, Version}]).
+basic_auth_1_1(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/1.1"} | Config]).
+
+basic_auth_1_0(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/1.0"} | Config]).
+
+basic_auth_0_9(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/0.9"} | Config]).
+
basic_auth() ->
[{doc, "Test Basic authentication with WWW-Authenticate header"}].
@@ -203,13 +315,211 @@ basic_auth(Config) ->
Config, [{statuscode, 200}]),
%% Authentication still required!
basic_auth_requiered(Config).
-
+
+auth_api_1_1(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/1.1"} | Config]).
+
+auth_api_1_0(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/1.0"} | Config]).
+
+auth_api_0_9(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/0.9"} | Config]).
+
+auth_api() ->
+ [{doc, "Test mod_auth API"}].
+
+auth_api(Config) when is_list(Config) ->
+ Prefix = ?config(auth_prefix, Config),
+ do_auth_api(Prefix, Config).
+
+do_auth_api(AuthPrefix, Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Node = ?config(node, Config),
+ ServerRoot = ?config(server_root, Config),
+ ok = http_status("GET / ", Config,
+ [{statuscode, 200}]),
+ ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/", "dummy", "WrongPassword",
+ Version, Host), Config, [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+
+ %% Change the password to DummyPassword then try to add a user
+ %% Get an error and set it to NoPassword
+ ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix,
+ "open", "NoPassword", "DummyPassword"),
+ {error,bad_password} =
+ add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
+ "onePassword", []),
+ ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix, "open",
+ "DummyPassword", "NoPassword"),
+
+ %% Test /*open, require user one Aladdin
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "onePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
+ "onePassword", []),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "two",
+ "twoPassword", []),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "Aladdin",
+ "AladdinPassword", []),
+ {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "open"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "WrongPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "two", "twoPassword", Version, Host),
+ Config,[{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "WrongPassword", Version, Host),
+ Config,[{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "open"),
+
+ %% Phase 2
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix,
+ "secret"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "one",
+ "onePassword",
+ []),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "two", "twoPassword", []),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "Aladdin",
+ "AladdinPassword",[]),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "one", "group1"),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "two", "group1"),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix,
+ "secret", "Aladdin", "group2"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "two", "twoPassword", Version, Host),
+ Config,[{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+
+ %% Phase 3
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_user(Node, ServerRoot, Port, AuthPrefix,
+ "secret/top_secret","three",
+ "threePassword",[]),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret",
+ "two","twoPassword", []),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "three", "group3"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "three", "threePassword",
+ Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "two", "group3"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/",
+ "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "secret/top_secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/","three", "threePassword", Version, Host),
+ Config, [{statuscde, 401}]).
+%%-------------------------------------------------------------------------
+ipv6() ->
+ [{require, ipv6_hosts},
+ {doc,"Test ipv6."}].
+ipv6(Config) when is_list(Config) ->
+ {ok, Hostname0} = inet:gethostname(),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ URI = http_request("GET /", Version, Host),
+ httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), [inet6],
+ ?config(code, Config),
+ URI,
+ [{statuscode, 200}, {version, Version}]);
+ false ->
+ {skip, "Host does not support IPv6"}
+ end.
+
+%%-------------------------------------------------------------------------
ssi() ->
[{doc, "HTTP GET server side include test"}].
ssi(Config) when is_list(Config) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
ok = httpd_test_lib:verify_request(?config(type, Config), Host, ?config(port, Config),
+ transport_opts(Type, Config),
?config(node, Config),
http_request("GET /fsize.shtml ", Version, Host),
[{statuscode, 200},
@@ -217,6 +527,131 @@ ssi(Config) when is_list(Config) ->
{header, "Date"},
{header, "Server"},
{version, Version}]).
+%%-------------------------------------------------------------------------
+htaccess_1_1(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/1.1"} | Config]).
+
+htaccess_1_0(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/1.0"} | Config]).
+
+htaccess_0_9(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/0.9"} | Config]).
+
+htaccess() ->
+ [{doc, "Test mod_auth API"}].
+
+htaccess(Config) when is_list(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Type = ?config(type, Config),
+ Port = ?config(port, Config),
+ Node = ?config(node, Config),
+ %% Control that authentication required!
+ %% Control that the pages that shall be
+ %% authenticated really need authenticatin
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/open/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/top_secret/ ",
+ Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/ht/open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = auth_status(auth_request("/ht/open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+
+ %% Control that not just the first user in the list is valid
+ %% Control the first user
+ %% Authennticating ["one:OnePassword" user first in user list]
+ ok = auth_status(auth_request("/ht/open/dummy.html", "one", "OnePassword",
+ Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Control the second user
+ %% Authentication OK and a directory listing is supplied!
+ %% ["Aladdin:open sesame" user second in user list]
+ ok = auth_status(auth_request("/ht/open/","Aladdin",
+ "AladdinPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Contro that bad passwords and userids get a good denial
+ %% User correct but wrong password! ["one:one" user first in user list]
+ ok = auth_status(auth_request("/ht/open/", "one", "one", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/ht/open/", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% Control that authetication still works, even if its a member in a group
+ %% Authentication OK! ["two:TwoPassword" user in first group]
+ ok = auth_status(auth_request("/ht/secret/dummy.html", "two",
+ "TwoPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Authentication OK and a directory listing is supplied!
+ %% ["three:ThreePassword" user in second group]
+ ok = auth_status(auth_request("/ht/secret/", "three",
+ "ThreePassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Deny users with bad passwords even if the user is a group member
+ %% User correct but wrong password! ["two:two" user in first group]
+ ok = auth_status(auth_request("/ht/secret/", "two", "two", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/ht/secret/", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% control that we deny the users that are in subnet above the allowed
+ ok = auth_status(auth_request("/ht/blocknet/dummy.html", "four",
+ "FourPassword", Version, Host), Config,
+ [{statuscode, 403}]),
+ %% Control that we only applies the rules to the right methods
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("HEAD /ht/blocknet/dummy.html ", Version, Host),
+ [{statuscode, head_status(Version)},
+ {version, Version}]),
+
+ %% Control that the rerquire directive can be overrideen
+ ok = auth_status(auth_request("/ht/secret/top_secret/ ", "Aladdin", "AladdinPassword",
+ Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% Authentication still required!
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/open/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/top_secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]).
+
+%%-------------------------------------------------------------------------
host() ->
[{doc, "Test host header"}].
@@ -224,21 +659,21 @@ host(Config) when is_list(Config) ->
Cb = ?config(version_cb, Config),
Cb:host(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-
+%%-------------------------------------------------------------------------
chunked() ->
[{doc, "Check that the server accepts chunked requests."}].
chunked(Config) when is_list(Config) ->
httpd_1_1:chunked(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-
+%%-------------------------------------------------------------------------
expect() ->
["Check that the server handles request with the expect header "
"field appropiate"].
expect(Config) when is_list(Config) ->
httpd_1_1:expect(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-
+%%-------------------------------------------------------------------------
max_clients_1_1() ->
[{doc, "Test max clients limit"}].
@@ -256,7 +691,7 @@ max_clients_0_9() ->
max_clients_0_9(Config) when is_list(Config) ->
do_max_clients([{http_version, "HTTP/0.9"} | Config]).
-
+%%-------------------------------------------------------------------------
esi() ->
[{doc, "Test mod_esi"}].
@@ -286,7 +721,7 @@ esi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
Config, [{statuscode, 200},
{no_header, "cache-control"}]).
-
+%%-------------------------------------------------------------------------
cgi() ->
[{doc, "Test mod_cgi"}].
@@ -361,7 +796,27 @@ cgi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/" ++ Script ++ " ", Config,
[{statuscode, 200},
{no_header, "cache-control"}]).
-
+%%-------------------------------------------------------------------------
+cgi_chunked_encoding_test() ->
+ [{doc, "Test chunked encoding together with mod_cgi "}].
+cgi_chunked_encoding_test(Config) when is_list(Config) ->
+ Host = ?config(host, Config),
+ Script =
+ case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/printenv.bat";
+ _ ->
+ "/cgi-bin/printenv.sh"
+ end,
+ Requests =
+ ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n",
+ "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:"
+ ++ Host ++"\r\n\r\n"],
+ httpd_1_1:mod_cgi_chunked_encoding_test(?config(type, Config), ?config(port, Config),
+ Host,
+ ?config(node, Config),
+ Requests).
+%%-------------------------------------------------------------------------
alias() ->
[{doc, "Test mod_alias"}].
@@ -389,160 +844,246 @@ alias(Config) when is_list(Config) ->
[{statuscode, 301},
{header, "Location"},
{header, "Content-Type","text/html"}]).
+%%-------------------------------------------------------------------------
+actions() ->
+ [{doc, "Test mod_actions"}].
+actions(Config) when is_list(Config) ->
+ ok = http_status("GET /", Config, [{statuscode, 200}]).
-%% auth_api() ->
-%% [{doc, "Test mod_auth API"}].
-
-%% auth_api(Config) when is_list(Config) ->
-%% Version = ?config(http_version, Config),
-%% Host = ?config(host, Config),
-%% ok = http_status("GET / ", Config,
-%% [{statuscode, 200}]),
-%% ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
-%% [{statuscode, 200}]),
-
-%% %% Make sure Authenticate header is received even the second time
-%% %% we try a incorrect password! Otherwise a browser client will hang!
-%% ok = auth_status(auth_request("/" ++ AuthStoreType ++ "open/",
-%% "dummy", "WrongPassword", Host), Config,
-%% [{statuscode, 401},
-%% {header, "WWW-Authenticate"}]),
-%% ok = auth_status(auth_request("/" ++ AuthStoreType ++ "open/", "dummy", "WrongPassword",
-%% Host), Config, [{statuscode, 401},
-%% {header, "WWW-Authenticate"}]),
-
-%% %% Change the password to DummyPassword then try to add a user
-%% %% Get an error and set it to NoPassword
-%% ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "open", "NoPassword", "DummyPassword"),
-%% {error,bad_password} =
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one",
-%% "onePassword", []),
-%% ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++"open",
-%% "DummyPassword", "NoPassword"),
-
-%% %% Test /*open, require user one Aladdin
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "open"),
+%%-------------------------------------------------------------------------
+range() ->
+ [{doc, "Test Range header"}].
+
+range(Config) when is_list(Config) ->
+ httpd_1_1:range(?config(type, Config), ?config(port, Config),
+ ?config(host, Config), ?config(node, Config)).
+
+%%-------------------------------------------------------------------------
+if_modified_since() ->
+ [{doc, "Test If-Modified-Since header"}].
+
+if_modified_since(Config) when is_list(Config) ->
+ httpd_1_1:if_test(?config(type, Config), ?config(port, Config),
+ ?config(host, Config), ?config(node, Config),
+ ?config(doc_root, Config)).
+%%-------------------------------------------------------------------------
+trace() ->
+ [{doc, "Test TRACE method"}].
+
+trace(Config) when is_list(Config) ->
+ Cb = ?config(version_cb, Config),
+ Cb:trace(?config(type, Config), ?config(port, Config),
+ ?config(host, Config), ?config(node, Config)).
+
+%%-------------------------------------------------------------------------
+light() ->
+ ["Test light load"].
+light(Config) when is_list(Config) ->
+ httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
+ ?config(node, Config), 10).
+%%-------------------------------------------------------------------------
+medium() ->
+ ["Test medium load"].
+medium(Config) when is_list(Config) ->
+ httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
+ ?config(node, Config), 100).
+%%-------------------------------------------------------------------------
+heavy() ->
+ ["Test heavy load"].
+heavy(Config) when is_list(Config) ->
+ httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
+ ?config(node, Config),
+ 1000).
+%%-------------------------------------------------------------------------
+content_length() ->
+ ["Tests that content-length is correct OTP-5775"].
+content_length(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET /cgi-bin/erl/httpd_example:get_bin ",
+ Version, Host),
+ [{statuscode, 200},
+ {content_length, 274},
+ {version, Version}]).
+%%-------------------------------------------------------------------------
+bad_hex() ->
+ ["Tests that a URI with a bad hexadecimal code is handled OTP-6003"].
+bad_hex(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET http://www.erlang.org/%skalle ",
+ Version, Host),
+ [{statuscode, 400},
+ {version, Version}]).
+%%-------------------------------------------------------------------------
+missing_CR() ->
+ ["Tests missing CR in delimiter OTP-7304"].
+missing_CR(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request_missing_CR("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {version, Version}]).
+
+%%-------------------------------------------------------------------------
+max_header() ->
+ ["Denial Of Service (DOS) attack, prevented by max_header"].
+max_header(Config) when is_list(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ case Version of
+ "HTTP/0.9" ->
+ {skip, no_implemented};
+ _ ->
+ dos_hostname(?config(type, Config), ?config(port, Config), Host,
+ ?config(node, Config), Version, ?MAX_HEADER_SIZE)
+ end.
+
+%%-------------------------------------------------------------------------
+security_1_1(Config) when is_list(Config) ->
+ security([{http_version, "HTTP/1.1"} | Config]).
+
+security_1_0(Config) when is_list(Config) ->
+ security([{http_version, "HTTP/1.0"} | Config]).
+
+security() ->
+ ["Test mod_security"].
+security(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Node = ?config(node, Config),
+ ServerRoot = ?config(server_root, Config),
+
+ global:register_name(mod_security_test, self()), % Receive events
+
+ test_server:sleep(5000),
+
+ OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
+
+ %% Test blocking / unblocking of users.
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "one", "onePassword", [{statuscode, 401}]),
+ %% /open, require user one Aladdin
+ remove_users(Node, ServerRoot, Host, Port, "", "open"),
+
+ ok = auth_status(auth_request("/open/",
+ "one", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "onePassword"}]},
+ Node, Port),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "two", "twoPassword", [{statuscode, 401}]),
+ ok = auth_status(auth_request("/open/",
+ "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "Aladdin", "onePassword", [{statuscode, 401}]),
-
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one",
-%% "onePassword", []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "two",
-%% "twoPassword", []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "Aladdin",
-%% "AladdinPassword", []),
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "two"}, {password, "twoPassword"}]},
+ Node, Port),
+
+ ok = auth_status(auth_request("/open/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "Aladdin"},
+ {password, "AladdinPassword"}]},
+ Node, Port),
+
+ add_user(Node, ServerRoot, Port, "", "open", "one", "onePassword", []),
+ add_user(Node, ServerRoot, Port, "", "open", "two", "twoPassword", []),
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "WrongPassword"}]},
+ Node, Port),
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "WrongPassword"}]},
+ Node, Port),
+ receive_security_event({event, user_block, Port, OpenDir,
+ [{user, "one"}]}, Node, Port),
+
+ global:unregister_name(mod_security_test), % No more events.
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% User "one" should be blocked now..
+ case list_blocked_users(Node, Port) of
+ [{"one",_, Port, OpenDir,_}] ->
+ ok;
+ Blocked ->
+ ct:fail({unexpected_blocked, Blocked})
+ end,
+
+ [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
+
+ true = unblock_user(Node, "one", Port, OpenDir),
+ %% User "one" should not be blocked any more.
+
+ [] = list_blocked_users(Node, Port),
+
+ ok = auth_status(auth_request("/open/", "one", "onePassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Test list_auth_users & auth_timeout
+
+ ["one"] = list_auth_users(Node, Port),
+
+ ok = auth_status(auth_request("/open/", "two", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ["one"] = list_auth_users(Node, Port),
+
-%% {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType++"open"),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
-%% "one", "WrongPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
-%% "one", "onePassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "two", "twoPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
-%% "Aladdin", "WrongPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "Aladdin", "AladdinPassword", [{statuscode, 200}]),
+ ["one"] = list_auth_users(Node, Port, OpenDir),
+
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"open"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType++"open"),
-
-%% %% Phase 2
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"secret"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "one", "onePassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "two", "twoPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "secret/",
-%% "three", "threePassword", [{statuscode, 401}]),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret", "one",
-%% "onePassword",
-%% []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret",
-%% "two", "twoPassword", []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType++"secret", "Aladdin",
-%% "AladdinPassword",[]),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret",
-%% "one", "group1"),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret",
-%% "two", "group1"),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret", "Aladdin", "group2"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "one", "onePassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "two", "twoPassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "Aladdin", "AladdinPassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "three", "threePassword", [{statuscode, 401}]),
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType ++ "secret"),
-%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"),
-%% Directory = filename:join([ServerRoot, "htdocs", AuthStoreType ++
-%% "secret"]),
-%% {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory),
-
-%% %% Phase 3
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/",
-%% "three", "threePassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/", "two", "twoPassword",
-%% [{statuscode, 401}]),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret/top_secret","three",
-%% "threePassword",[]),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret/top_secret",
-%% "two","twoPassword", []),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret/top_secret",
-%% "three", "group3"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/", "three", "threePassword",
-%% [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/", "two", "twoPassword",
-%% [{statuscode, 401}]),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret/top_secret",
-%% "two", "group3"),
-%% auth_request(Type,Host,Port,Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/",
-%% "two", "twoPassword", [{statuscode, 200}]),
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType ++ "secret/top_secret"),
-%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% Directory2 = filename:join([ServerRoot, "htdocs",
-%% AuthStoreType ++ "secret/top_secret"]),
-%% {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory2),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++
-%% "secret/top_secret/", "two", "twoPassword",
-%% [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++
-%% "secret/top_secret/","three", "threePassword",
-%% [{statuscode, 401}]).
+ ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ["one"] = list_auth_users(Node, Port),
+
+
+ ["one"] = list_auth_users(Node, Port, OpenDir),
+
+ %% Wait for successful auth to timeout.
+ test_server:sleep(?AUTH_TIMEOUT*1001),
+
+ [] = list_auth_users(Node, Port),
+
+ [] = list_auth_users(Node, Port, OpenDir),
+
+ %% "two" is blocked.
+
+ true = unblock_user(Node, "two", Port, OpenDir),
+
+
+ %% Test explicit blocking. Block user 'two'.
+
+ [] = list_blocked_users(Node,Port,OpenDir),
+
+ true = block_user(Node, "two", Port, OpenDir, 10),
+
+ ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ true = unblock_user(Node, "two", Port, OpenDir).
+
%%--------------------------------------------------------------------
@@ -550,21 +1091,34 @@ alias(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
do_max_clients(Config) ->
Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- start_blocker(Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Type = ?config(type, Config),
+
+ Request = http_request("GET /index.html ", Version, Host),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100),
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ transport_opts(Type, Config),
+ ?config(node, Config),
+ Request,
[{statuscode, 503},
{version, Version}]),
receive
- after 2000 ->
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
- [{statuscode, 200},
- {version, Version}])
- end.
+ {_, Socket, _Msg} ->
+ ok
+ end,
+ inets_test_lib:close(Type, Socket),
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ transport_opts(Type, Config),
+ ?config(node, Config),
+ Request,
+ [{statuscode, 200},
+ {version, Version}]).
setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
CgiDir = filename:join(ServerRoot, "cgi-bin"),
@@ -604,10 +1158,24 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
ok = file:write_file_info(EnvCGI,
FileInfo1#file_info{mode = 8#00755}).
-start_apps(https) ->
- inets_test_lib:start_apps([crypto, public_key, ssl]);
-start_apps(_) ->
- ok.
+start_apps(Group) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == http_htaccess;
+ Group == http_security ->
+ inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
+start_apps(Group) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == https_htaccess;
+ Group == https_security ->
+ inets_test_lib:start_apps([inets]).
server_start(_, HttpdConfig) ->
{ok, Pid} = inets:start(httpd, HttpdConfig),
@@ -615,6 +1183,80 @@ server_start(_, HttpdConfig) ->
{value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv),
{Pid, proplists:get_value(port, Info)}.
+init_ssl(Group, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ CaKey = {_Trusted,_} =
+ erl_make_certs:make_cert([{key, dsa},
+ {subject,
+ [{name, "Public Key"},
+ {?'id-at-name',
+ {printableString, "public_key"}},
+ {?'id-at-pseudonym',
+ {printableString, "pubkey"}},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}
+ ]}
+ ]),
+ ok = erl_make_certs:write_pem(PrivDir, "public_key_cacert", CaKey),
+
+ CertK1 = {_Cert1, _} = erl_make_certs:make_cert([{issuer, CaKey}]),
+ CertK2 = {_Cert2,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {digest, md5},
+ {extensions, false}]),
+ ok = erl_make_certs:write_pem(PrivDir, "public_key_cert", CertK2),
+
+ case start_apps(Group) of
+ ok ->
+ init_httpd(Group, [{type, ssl} | Config]);
+ _ ->
+ {skip, "Could not start https apps"}
+ end.
+
+server_config(http_basic, Config) ->
+ basic_conf() ++ server_config(http, Config);
+server_config(https_basic, Config) ->
+ basic_conf() ++ server_config(https, Config);
+server_config(http_limit, Config) ->
+ [{max_clients, 1}] ++ server_config(http, Config);
+server_config(https_limit, Config) ->
+ [{max_clients, 1}] ++ server_config(https, Config);
+server_config(http_basic_auth, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_conf(ServerRoot) ++ server_config(http, Config);
+server_config(https_basic_auth, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_conf(ServerRoot) ++ server_config(https, Config);
+server_config(http_auth_api, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, plain) ++ server_config(http, Config);
+server_config(https_auth_api, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, plain) ++ server_config(https, Config);
+server_config(http_auth_api_dets, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, dets) ++ server_config(http, Config);
+server_config(https_auth_api_dets, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, dets) ++ server_config(https, Config);
+server_config(http_auth_api_mnesia, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, mnesia) ++ server_config(http, Config);
+server_config(https_auth_api_mnesia, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, mnesia) ++ server_config(https, Config);
+server_config(http_htaccess, Config) ->
+ auth_access_conf() ++ server_config(http, Config);
+server_config(https_htaccess, Config) ->
+ auth_access_conf() ++ server_config(https, Config);
+server_config(http_security, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config);
+server_config(https_security, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(https, Config);
+
server_config(http, Config) ->
ServerRoot = ?config(server_root, Config),
[{port, 0},
@@ -625,6 +1267,7 @@ server_config(http, Config) ->
{ipfamily, inet},
{max_header_size, 256},
{max_header_action, close},
+ {directory_index, ["index.html", "welcome.html"]},
{mime_types, [{"html","text/html"},{"htm","text/html"}, {"shtml","text/html"},
{"gif", "image/gif"}]},
{alias, {"/icons/", filename:join(ServerRoot,"icons") ++ "/"}},
@@ -633,13 +1276,24 @@ server_config(http, Config) ->
{script_alias, {"/htbin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}},
{erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}},
{eval_script_alias, {"/eval", [httpd_example, io]}}
- ] ++ auth_conf(ServerRoot);
+ ];
-server_config(http_limit, Config) ->
- [{max_clients, 1}] ++ server_config(http, Config);
-
-server_config(_, _) ->
- [].
+server_config(https, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ [{socket_type, {essl,
+ [{cacertfile,
+ filename:join(PrivDir, "public_key_cacert.pem")},
+ {certfile,
+ filename:join(PrivDir, "public_key_cert.pem")},
+ {keyfile,
+ filename:join(PrivDir, "public_key_cert_key.pem")}
+ ]}}] ++ server_config(http, Config).
+
+init_httpd(Group, Config0) ->
+ Config1 = proplists:delete(port, Config0),
+ Config = proplists:delete(server_pid, Config1),
+ {Pid, Port} = server_start(Group, server_config(Group, Config)),
+ [{server_pid, Pid}, {port, Port} | Config].
http_request(Request, "HTTP/1.1" = Version, Host, {Headers, Body}) ->
Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n" ++ Headers ++ "\r\n" ++ Body;
@@ -662,19 +1316,33 @@ auth_request(Path, User, Passwd, Version, _Host) ->
base64:encode_to_string(User++":"++Passwd) ++
"\r\n\r\n".
+http_request_missing_CR(Request, "HTTP/1.1" = Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n\n";
+http_request_missing_CR(Request, Version, _) ->
+ Request ++ Version ++ "\r\n\n".
+
head_status("HTTP/0.9") ->
501; %% Not implemented in HTTP/0.9
head_status(_) ->
200.
+basic_conf() ->
+ [{modules, [mod_alias, mod_range, mod_responsecontrol,
+ mod_trace, mod_esi, mod_cgi, mod_dir, mod_get, mod_head]}].
+
+auth_access_conf() ->
+ [{modules, [mod_alias, mod_htaccess, mod_dir, mod_get, mod_head]},
+ {access_files, [".htaccess"]}].
+
auth_conf(Root) ->
- [{directory, {filename:join(Root, "htdocs/open"),
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/open"),
[{auth_type, plain},
{auth_name, "Open Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
{require_user, ["one", "Aladdin"]}]}},
- {directory, {filename:join(Root, "htdocs/secret"),
+ {directory, {filename:join(Root, "htdocs/secret"),
[{auth_type, plain},
{auth_name, "Secret Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
@@ -685,43 +1353,134 @@ auth_conf(Root) ->
{auth_name, "Top Secret Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
- {require_group, ["group3"]}]}},
+ {require_group, ["group3"]}]}}].
+
+auth_api_conf(Root, plain) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
{directory, {filename:join(Root, "htdocs/open"),
- [{auth_type, mnesia},
+ [{auth_type, plain},
{auth_name, "Open Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
{require_user, ["one", "Aladdin"]}]}},
{directory, {filename:join(Root, "htdocs/secret"),
- [{auth_type, mnesia},
+ [{auth_type, plain},
{auth_name, "Secret Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
- {require_group, ["group1", "group2"]}]}}
- ].
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/secret/top_secret"),
+ [{auth_type, plain},
+ {auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]}]}}];
+auth_api_conf(Root, dets) ->
+ [
+ {modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/dets_open"),
+ [{auth_type, dets},
+ {auth_name, "Dets Open Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/dets_secret"),
+ [{auth_type, dets},
+ {auth_name, "Dests Secret Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/dets_secret/top_secret"),
+ [{auth_type, dets},
+ {auth_name, "Dets Top Secret Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_group, ["group3"]}]}}
+ ];
+
+auth_api_conf(Root, mnesia) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/mnesia_open"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Open Area"},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/mnesia_secret"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Secret Area"},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/mnesia_secret/top_secret"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Top Secret Area"},
+ {require_group, ["group3"]}]}}].
+
+security_conf(Root) ->
+ SecFile = filename:join(Root, "security_data"),
+ Open = filename:join(Root, "htdocs/open"),
+ Secret = filename:join(Root, "htdocs/secret"),
+ TopSecret = filename:join(Root, "htdocs/secret/top_secret"),
+
+ [{modules, [mod_alias, mod_auth, mod_security, mod_dir, mod_get, mod_head]},
+ {security_directory, {Open,
+ [{auth_name, "Open Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_user, ["one", "Aladdin"]} |
+ mod_security_conf(SecFile, Open)]}},
+ {security_directory, {Secret,
+ [{auth_name, "Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group1", "group2"]} |
+ mod_security_conf(SecFile, Secret)]}},
+ {security_directory, {TopSecret,
+ [{auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]} |
+ mod_security_conf(SecFile, TopSecret)]}}].
+
+mod_security_conf(SecFile, Dir) ->
+ [{data_file, SecFile},
+ {max_retries, 3},
+ {fail_expire_time, ?FAIL_EXPIRE_TIME},
+ {block_time, 1},
+ {auth_timeout, ?AUTH_TIMEOUT},
+ {callback_module, ?MODULE},
+ {path, Dir} %% This is should not be needed, but is atm, awful design!
+ ].
+
http_status(Request, Config, Expected) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
http_request(Request, Version, Host),
Expected ++ [{version, Version}]).
http_status(Request, HeadersAndBody, Config, Expected) ->
Version = ?config(http_version, Config),
- Host = ?config(host, Config),
+ Host = ?config(host, Config),
+ Type = ?config(type, Config),
httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
http_request(Request, Version, Host, HeadersAndBody),
Expected ++ [{version, Version}]).
auth_status(AuthRequest, Config, Expected) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
AuthRequest,
Expected ++ [{version, Version}]).
@@ -772,23 +1531,258 @@ cleanup_mnesia() ->
mnesia:delete_schema([node()]),
ok.
-start_blocker(Config) ->
- spawn(httpd_SUITE, init_blocker, [self(), Config]),
- receive
- blocker_start ->
+transport_opts(ssl, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ [{cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}];
+transport_opts(_, _) ->
+ [].
+
+
+%%% mod_range
+create_range_data(Path) ->
+ PathAndFileName=filename:join([Path,"range.txt"]),
+ case file:read_file(PathAndFileName) of
+ {error, enoent} ->
+ file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890"]));
+ _ ->
ok
end.
+
+%%% mod_htaccess
+create_htaccess_data(Path, IpAddress)->
+ create_htaccess_dirs(Path),
+
+ create_html_file(filename:join([Path,"ht/open/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/secret/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
-init_blocker(From, Config) ->
- From ! blocker_start,
- block(Config).
+ create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]),
+ Path, "user one Aladdin"),
+ create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]),
+ Path, "group group1 group2"),
+ create_htaccess_file(filename:join([Path,
+ "ht/secret/top_secret/.htaccess"]),
+ Path, "user four"),
+ create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
+ Path, nouser, IpAddress),
+
+ create_user_group_file(filename:join([Path,"ht","users.file"]),
+ "one:OnePassword\ntwo:TwoPassword\nthree:"
+ "ThreePassword\nfour:FourPassword\nAladdin:"
+ "AladdinPassword"),
+ create_user_group_file(filename:join([Path,"ht","groups.file"]),
+ "group1: two one\ngroup2: two three").
+
+create_html_file(PathAndFileName)->
+ file:write_file(PathAndFileName,list_to_binary(
+ "<html><head><title>test</title></head>
+ <body>testar</body></html>")).
+
+create_htaccess_file(PathAndFileName, BaseDir, RequireData)->
+ file:write_file(PathAndFileName,
+ list_to_binary(
+ "AuthUserFile "++ BaseDir ++
+ "/ht/users.file\nAuthGroupFile "++ BaseDir
+ ++ "/ht/groups.file\nAuthName Test\nAuthType"
+ " Basic\n<Limit>\nrequire " ++ RequireData ++
+ "\n</Limit>")).
+
+create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
+ file:write_file(PathAndFileName,list_to_binary(
+ "AuthUserFile "++ BaseDir ++
+ "/ht/users.file\nAuthGroupFile " ++
+ BaseDir ++ "/ht/groups.file\nAuthName"
+ " Test\nAuthType"
+ " Basic\n<Limit GET>\n\tallow from " ++
+ format_ip(IpAddress,
+ string:rchr(IpAddress,$.)) ++
+ "\n</Limit>")).
+
+create_user_group_file(PathAndFileName, Data)->
+ file:write_file(PathAndFileName, list_to_binary(Data)).
+
+create_htaccess_dirs(Path)->
+ ok = file:make_dir(filename:join([Path,"ht"])),
+ ok = file:make_dir(filename:join([Path,"ht/open"])),
+ ok = file:make_dir(filename:join([Path,"ht/blocknet"])),
+ ok = file:make_dir(filename:join([Path,"ht/secret"])),
+ ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])).
+
+remove_htaccess_dirs(Path)->
+ file:del_dir(filename:join([Path,"ht/secret/top_secret"])),
+ file:del_dir(filename:join([Path,"ht/secret"])),
+ file:del_dir(filename:join([Path,"ht/blocknet"])),
+ file:del_dir(filename:join([Path,"ht/open"])),
+ file:del_dir(filename:join([Path,"ht"])).
+
+format_ip(IpAddress,Pos)when Pos > 0->
+ case lists:nth(Pos,IpAddress) of
+ $.->
+ case lists:nth(Pos-2,IpAddress) of
+ $.->
+ format_ip(IpAddress,Pos-3);
+ _->
+ lists:sublist(IpAddress,Pos-2) ++ "."
+ end;
+ _ ->
+ format_ip(IpAddress,Pos-1)
+ end;
+
+format_ip(IpAddress, _Pos)->
+ "1" ++ IpAddress.
+
+remove_htaccess(Path)->
+ file:delete(filename:join([Path,"ht/open/dummy.html"])),
+ file:delete(filename:join([Path,"ht/secret/dummy.html"])),
+ file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
+ file:delete(filename:join([Path,"ht/blocknet/dummy.html"])),
+ file:delete(filename:join([Path,"ht/blocknet/.htaccess"])),
+ file:delete(filename:join([Path,"ht/open/.htaccess"])),
+ file:delete(filename:join([Path,"ht/secret/.htaccess"])),
+ file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])),
+ file:delete(filename:join([Path,"ht","users.file"])),
+ file:delete(filename:join([Path,"ht","groups.file"])),
+ remove_htaccess_dirs(Path).
+
+dos_hostname(Type, Port, Host, Node, Version, Max) ->
+ TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request("", Version),
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request("dummy-host.ericsson.se", Version),
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request(TooLongHeader, Version),
+ [{statuscode, dos_code(Version)},
+ {version, Version}]).
+dos_hostname_request(Host, Version) ->
+ dos_http_request("GET / ", Version, Host).
+
+dos_http_request(Request, "HTTP/1.1" = Version, Host) ->
+ http_request(Request, Version, Host);
+dos_http_request(Request, Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n".
+
+dos_code("HTTP/1.0") ->
+ 403; %% 413 not defined in HTTP/1.0
+dos_code(_) ->
+ 413.
+
+update_password(Node, ServerRoot, _Address, Port, AuthPrefix, Dir, Old, New)->
+ Directory = filename:join([ServerRoot, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, update_password,
+ [undefined, Port, Directory, Old, New, New]).
+
+add_user(Node, Root, Port, AuthPrefix, Dir, User, Password, UserData) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, add_user,
+ [User, Password, UserData, Addr, Port, Directory]).
+
+
+delete_user(Node, Root, _Host, Port, AuthPrefix, Dir, User) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, delete_user, [User, Addr, Port, Directory]).
+remove_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir) ->
+ %% List users, delete them, and make sure they are gone.
+ case list_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir) of
+ {ok, Users} ->
+ lists:foreach(fun(User) ->
+ delete_user(Node, ServerRoot, Host,
+ Port, AuthPrefix, Dir, User)
+ end,
+ Users),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir);
+ _ ->
+ ok
+ end.
+
+list_users(Node, Root, _Host, Port, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]).
+
+remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir) ->
+ {ok, Groups} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir),
+ lists:foreach(fun(Group) ->
+ delete_group(Node, Group, Port, ServerRoot, AuthPrefix, Dir)
+ end,
+ Groups),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir).
+
+delete_group(Node, Group, Port, Root, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, delete_group, [Group, Addr, Port, Directory]).
+
+list_groups(Node, Root, _, Port, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_groups, [Addr, Port, Directory]).
+
+add_group_member(Node, Root, Port, AuthPrefix, Dir, User, Group) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, add_group_member, [Group, User, Addr, Port,
+ Directory]).
+getaddr() ->
+ {ok,HostName} = inet:gethostname(),
+ {ok,{A1,A2,A3,A4}} = inet:getaddr(HostName,inet),
+ lists:flatten(io_lib:format("~p.~p.~p.~p",[A1,A2,A3,A4])).
+
+receive_security_event(Event, Node, Port) ->
+ receive
+ Event ->
+ ok;
+ {'EXIT', _, _} ->
+ receive_security_event(Event, Node, Port)
+ after 5000 ->
+ %% Flush the message queue, to see if we got something...
+ inets_test_lib:flush()
+ end.
+
+list_blocked_users(Node,Port) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]).
+
+list_blocked_users(Node,Port,Dir) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_blocked_users, [Addr,Port,Dir]).
+
+block_user(Node,User,Port,Dir,Sec) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, block_user, [User, Addr, Port, Dir, Sec]).
+
+unblock_user(Node,User,Port,Dir) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, unblock_user, [User, Addr, Port, Dir]).
+
+list_auth_users(Node,Port) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_auth_users, [Addr,Port]).
+
+list_auth_users(Node,Port,Dir) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_auth_users, [Addr,Port,Dir]).
+
+event(What, Port, Dir, Data) ->
+ Msg = {event, What, Port, Dir, Data},
+ case global:whereis_name(mod_security_test) of
+ undefined ->
+ ok;
+ _Pid ->
+ global:send(mod_security_test, Msg)
+ end.
-block(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /eval?httpd_example:delay(1000) ",
- Version, Host),
- [{statuscode, 200},
- {version, Version}]).
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 2d06f3e70c..fbe65145dc 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -65,7 +65,8 @@ end_per_group(_GroupName, Config) ->
init_per_suite(Config) ->
tsp("init_per_suite -> entry with"
"~n Config: ~p", [Config]),
- ok = inets:start(),
+ inets_test_lib:stop_apps([inets]),
+ inets_test_lib:start_apps([inets]),
PrivDir = ?config(priv_dir, Config),
DataDir = ?config(data_dir, Config),
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 6406eeae79..ed466fd727 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -92,16 +92,6 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, TimeOut) ->
- tsp("verify_request -> entry with"
- "~n SocketType: ~p"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n TranspOpts: ~p"
- "~n Node: ~p"
- "~n Options: ~p"
- "~n TimeOut: ~p",
- [SocketType, Host, Port, TranspOpts0, Node, Options, TimeOut]),
-
%% For now, until we modernize the httpd tests
TranspOpts =
case lists:member(inet6, TranspOpts0) of
@@ -113,10 +103,7 @@ verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, T
try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of
{ok, Socket} ->
- tsp("verify_request -> connected - now send message"),
SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
- tsp("verify_request -> send result: "
- "~n ~p", [SendRes]),
State = case inets_regexp:match(RequestStr, "printenv") of
nomatch ->
#state{};
@@ -127,37 +114,24 @@ verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, T
case request(State#state{request = RequestStr,
socket = Socket}, TimeOut) of
{error, Reason} ->
- tsp("verify_request -> request failed: "
- "~n Reason: ~p", [Reason]),
{error, Reason};
NewState ->
- tsp("verify_request -> validate reply: "
- "~n NewState: ~p", [NewState]),
ValidateResult =
validate(RequestStr, NewState, Options, Node, Port),
- tsp("verify_request -> validation result: "
- "~n ~p", [ValidateResult]),
inets_test_lib:close(SocketType, Socket),
ValidateResult
end;
ConnectError ->
- tsp("verify_request -> connect error: "
- "~n ~p"
- "~n", [ConnectError]),
- tsf({connect_error, ConnectError,
- [SocketType, Host, Port, TranspOpts]})
+ ct:fail({connect_error, ConnectError,
+ [SocketType, Host, Port, TranspOpts]})
catch
T:E ->
- tsp("verify_request -> connect failed: "
- "~n E: ~p"
- "~n T: ~p"
- "~n", [E, T]),
- tsf({connect_failure,
- [{type, T},
- {error, E},
- {stacktrace, erlang:get_stacktrace()},
- {args, [SocketType, Host, Port, TranspOpts]}]})
+ ct:fail({connect_failure,
+ [{type, T},
+ {error, E},
+ {stacktrace, erlang:get_stacktrace()},
+ {args, [SocketType, Host, Port, TranspOpts]}]})
end.
request(#state{mfa = {Module, Function, Args},
@@ -166,10 +140,6 @@ request(#state{mfa = {Module, Function, Args},
HeadRequest = lists:sublist(RequestStr, 1, 4),
receive
{tcp, Socket, Data} ->
- io:format("~p ~w[~w]request -> received (tcp) data"
- "~n Data: ~p"
- "~n", [self(), ?MODULE, ?LINE, Data]),
- print(tcp, Data, State),
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
@@ -179,22 +149,12 @@ request(#state{mfa = {Module, Function, Args},
request(State#state{mfa = NewMFA}, TimeOut)
end;
{tcp_closed, Socket} when Function =:= whole_body ->
- io:format("~p ~w[~w]request -> "
- "received (tcp) closed when whole_body"
- "~n", [self(), ?MODULE, ?LINE]),
- print(tcp, "closed", State),
State#state{body = hd(Args)};
{tcp_closed, Socket} ->
- io:format("~p ~w[~w]request -> received (tcp) closed"
- "~n", [self(), ?MODULE, ?LINE]),
exit({test_failed, connection_closed});
{tcp_error, Socket, Reason} ->
- io:format("~p ~w[~w]request -> received (tcp) error"
- "~n Reason: ~p"
- "~n", [self(), ?MODULE, ?LINE, Reason]),
ct:fail({tcp_error, Reason});
{ssl, Socket, Data} ->
- print(ssl, Data, State),
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
@@ -204,28 +164,19 @@ request(#state{mfa = {Module, Function, Args},
request(State#state{mfa = NewMFA}, TimeOut)
end;
{ssl_closed, Socket} when Function =:= whole_body ->
- print(ssl, "closed", State),
State#state{body = hd(Args)};
{ssl_closed, Socket} ->
exit({test_failed, connection_closed});
{ssl_error, Socket, Reason} ->
ct:fail({ssl_error, Reason})
after TimeOut ->
- io:format("~p ~w[~w]request -> timeout"
- "~n", [self(), ?MODULE, ?LINE]),
+ ct:pal("~p ~w[~w]request -> timeout"
+ "~n", [self(), ?MODULE, ?LINE]),
ct:fail(connection_timed_out)
end.
handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
State = #state{request = RequestStr}) ->
- io:format("~p ~w[~w]handle_http_msg -> entry with"
- "~n Version: ~p"
- "~n StatusCode: ~p"
- "~n ReasonPharse: ~p"
- "~n Headers: ~p"
- "~n Body: ~p"
- "~n", [self(), ?MODULE, ?LINE,
- Version, StatusCode, ReasonPharse, Headers, Body]),
case is_expect(RequestStr) of
true ->
State#state{status_line = {Version,
@@ -285,11 +236,6 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
headers = Headers,
body = Body}, Options, N, P) ->
- tsp("validate -> entry with"
- "~n StatusCode: ~p"
- "~n Headers: ~p"
- "~n Body: ~p", [StatusCode, Headers, Body]),
-
check_version(Version, Options),
case lists:keysearch(statuscode, 1, Options) of
{value, _} ->
@@ -311,20 +257,20 @@ check_version(Version, Options) ->
{value, {version, Version}} ->
ok;
{value, {version, Ver}} ->
- tsf({wrong_version, [{got, Version},
- {expected, Ver}]});
+ ct:fail({wrong_version, [{got, Version},
+ {expected, Ver}]});
_ ->
- case Version of
- "HTTP/1.1" ->
- ok;
+ case Version of
+ "HTTP/1.1" ->
+ ok;
_ ->
- tsf({wrong_version, [{got, Version},
- {expected, "HTTP/1.1"}]})
- end
+ ct:fail({wrong_version, [{got, Version},
+ {expected, "HTTP/1.1"}]})
+ end
end.
check_status_code(StatusCode, [], Options) ->
- tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]});
+ ct:fail({wrong_status_code, [{got, StatusCode}, {expected, Options}]});
check_status_code(StatusCode, Current = [_ | Rest], Options) ->
case lists:keysearch(statuscode, 1, Current) of
{value, {statuscode, StatusCode}} ->
@@ -332,7 +278,7 @@ check_status_code(StatusCode, Current = [_ | Rest], Options) ->
{value, {statuscode, _OtherStatus}} ->
check_status_code(StatusCode, Rest, Options);
false ->
- tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]})
+ ct:fail({wrong_status_code, [{got, StatusCode}, {expected, Options}]})
end.
do_validate(_, [], _, _) ->
@@ -345,9 +291,9 @@ do_validate(Header, [{header, HeaderField}|Rest], N, P) ->
{value, {LowerHeaderField, _Value}} ->
ok;
false ->
- tsf({missing_header_field, LowerHeaderField, Header});
+ ct:fail({missing_header_field, LowerHeaderField, Header});
_ ->
- tsf({missing_header_field, LowerHeaderField, Header})
+ ct:fail({missing_header_field, LowerHeaderField, Header})
end,
do_validate(Header, Rest, N, P);
do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) ->
@@ -356,15 +302,15 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) ->
{value, {LowerHeaderField, Value}} ->
ok;
false ->
- tsf({wrong_header_field_value, LowerHeaderField, Header});
+ ct:fail({wrong_header_field_value, LowerHeaderField, Header});
_ ->
- tsf({wrong_header_field_value, LowerHeaderField, Header})
+ ct:fail({wrong_header_field_value, LowerHeaderField, Header})
end,
do_validate(Header, Rest, N, P);
do_validate(Header,[{no_header, HeaderField}|Rest],N,P) ->
case lists:keysearch(HeaderField,1,Header) of
{value,_} ->
- tsf({wrong_header_field_value, HeaderField, Header});
+ ct:fail({wrong_header_field_value, HeaderField, Header});
_ ->
ok
end,
@@ -382,14 +328,14 @@ is_expect(RequestStr) ->
%% OTP-5775, content-length
check_body("GET /cgi-bin/erl/httpd_example:get_bin HTTP/1.0\r\n\r\n", 200, "text/html", Length, _Body) when (Length =/= 274) ->
- tsf(content_length_error);
+ ct:fail(content_length_error);
check_body("GET /cgi-bin/cgi_echo HTTP/1.0\r\n\r\n", 200, "text/plain",
_, Body) ->
case size(Body) of
100 ->
ok;
_ ->
- tsf(content_length_error)
+ ct:fail(content_length_error)
end;
check_body(RequestStr, 200, "text/html", _, Body) ->
@@ -404,16 +350,3 @@ check_body(RequestStr, 200, "text/html", _, Body) ->
check_body(_, _, _, _,_) ->
ok.
-print(Proto, Data, #state{print = true}) ->
- ct:pal("Received ~p: ~p~n", [Proto, Data]);
-print(_, _, #state{print = false}) ->
- ok.
-
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl
index d563b52ae7..a8051c6c85 100644
--- a/lib/inets/test/inets_appup_test.erl
+++ b/lib/inets/test/inets_appup_test.erl
@@ -23,13 +23,7 @@
-module(inets_appup_test).
-compile(export_all).
--compile({no_auto_import,[error/1]}).
-
--include("inets_test_lib.hrl").
-
-
- % t() -> megaco_test_lib:t(?MODULE).
- % t(Case) -> megaco_test_lib:t({?MODULE, Case}).
+-include_lib("common_test/include/ct.hrl").
%% Test server callbacks
@@ -59,16 +53,9 @@ end_per_group(_GroupName, Config) ->
init_per_suite(suite) -> [];
init_per_suite(doc) -> [];
init_per_suite(Config) when is_list(Config) ->
- AppFile = file_name(inets, ".app"),
- AppupFile = file_name(inets, ".appup"),
- [{app_file, AppFile}, {appup_file, AppupFile}|Config].
+ Config.
-file_name(App, Ext) ->
- LibDir = code:lib_dir(App),
- filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-
-
end_per_suite(suite) -> [];
end_per_suite(doc) -> [];
end_per_suite(Config) when is_list(Config) ->
@@ -77,282 +64,7 @@ end_per_suite(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup(suite) ->
- [];
-appup(doc) ->
- "perform a simple check of the appup file";
+appup() ->
+ [{doc, "Perform a simple check of the inets appup file"}].
appup(Config) when is_list(Config) ->
- AppupFile = key1search(appup_file, Config),
- AppFile = key1search(app_file, Config),
- Modules = modules(AppFile),
- check_appup(AppupFile, Modules).
-
-modules(File) ->
- case file:consult(File) of
- {ok, [{application,inets,Info}]} ->
- case lists:keysearch(modules,1,Info) of
- {value, {modules, Modules}} ->
- Modules;
- false ->
- fail({bad_appinfo, Info})
- end;
- Error ->
- fail({bad_appfile, Error})
- end.
-
-
-check_appup(AppupFile, Modules) ->
- case file:consult(AppupFile) of
- {ok, [{V, UpFrom, DownTo}]} ->
-% io:format("~p => "
-% "~n ~p"
-% "~n ~p"
-% "~n", [V, UpFrom, DownTo]),
- check_appup(V, UpFrom, DownTo, Modules);
- Else ->
- fail({bad_appupfile, Else})
- end.
-
-
-check_appup(V, UpFrom, DownTo, Modules) ->
- check_version(V),
- check_depends(up, UpFrom, Modules),
- check_depends(down, DownTo, Modules),
- ok.
-
-
-check_depends(_, [], _) ->
- ok;
-check_depends(UpDown, [Dep|Deps], Modules) ->
- check_depend(UpDown, Dep, Modules),
- check_depends(UpDown, Deps, Modules).
-
-
-check_depend(UpDown, {V, Instructions}, Modules) ->
- check_version(V),
- case check_instructions(UpDown,
- Instructions, Instructions, [], [], Modules) of
- {_Good, []} ->
- ok;
- {_, Bad} ->
- fail({bad_instructions, Bad, UpDown})
- end.
-
-
-check_instructions(_, [], _, Good, Bad, _) ->
- {lists:reverse(Good), lists:reverse(Bad)};
-check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) ->
- case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of
- ok ->
- check_instructions(UpDown, Instrs, AllInstr,
- [Instr|Good], Bad, Modules);
- {error, Reason} ->
- check_instructions(UpDown, Instrs, AllInstr, Good,
- [{Instr, Reason}|Bad], Modules)
- end;
-check_instructions(UpDown, Instructions, _, _, _, _) ->
- fail({bad_instructions, {UpDown, Instructions}}).
-
-%% A new module is added
-check_instruction(up, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- check_module(Module, Modules);
-
-%% An old module is re-added
-check_instruction(down, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- error({existing_readded_module, Module})
- end;
-
-%% Removing a module on upgrade:
-%% - the module has been removed from the app-file.
-%% - check that no module depends on this (removed) module
-check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post) ->
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- check_purge(Pre),
- check_purge(Post);
- ok ->
- error({existing_removed_module, Module})
- end;
-
-%% Removing a module on downgrade: the module exist
-%% in the app-file.
-check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post) ->
- case (catch check_module(Module, Modules)) of
- ok ->
- check_purge(Pre),
- check_purge(Post),
- check_no_remove_depends(Module, AllInstr);
- {error, {unknown_module, Module, Modules}} ->
- error({nonexisting_removed_module, Module})
- end;
-
-check_instruction(up, {load_module, Module, Pre, Post, Depend}, _, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(down, {load_module, Module, Pre, Post, Depend}, _, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- check_module(Module, Modules),
- % Can not be sure that the the dependent module exists in the new appfile
- %%check_module_depend(Module, Depend, Modules),
- check_purge(Pre),
- check_purge(Post);
-
-
-
-check_instruction(up, {delete_module, Module}, _, Modules)
- when is_atom(Module) ->
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- error({existing_module_deleted, Module})
- end;
-
-check_instruction(down, {delete_module, Module}, _, Modules)
- when is_atom(Module) ->
- check_module(Module, Modules);
-
-
-check_instruction(_, {apply, {Module, Function, Args}}, _, _) when is_atom(Module), is_atom(Function), is_list(Args) ->
- ok;
-
-check_instruction(_, {update, Module, supervisor}, _, Modules) when is_atom(Module) ->
- check_module(Module, Modules);
-
-check_instruction(_, {update, Module, {advanced, _}, DepMods}, _, Modules) when is_atom(Module), is_list(DepMods) ->
- check_module(Module, Modules),
- check_module_depend(Module, DepMods, Modules);
-
-check_instruction(_, {update, Module, Change, Pre, Post, Depend}, _, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_change(Change),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {restart_application, inets}, _AllInstr, _Modules) ->
- ok;
-
-check_instruction(_, {update, Module, {advanced, _}}, _, Modules) ->
- check_module(Module, Modules);
-
-check_instruction(_, Instr, _AllInstr, _Modules) ->
- error({error, {unknown_instruction, Instr}}).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-check_version(V) when is_list(V) ->
- ok;
-check_version(REBin) when is_binary(REBin) ->
- try
- begin
- RE = binary_to_list(REBin),
- case re:compile(RE) of
- {ok, _} ->
- ok;
- {error, _} ->
- error({bad_version, REBin})
- end
- end
- catch
- _T:_E ->
- error({bad_version, REBin})
- end;
-check_version(V) ->
- error({bad_version, V}).
-
-
-check_module(M, Modules) when is_atom(M) ->
- case lists:member(M,Modules) of
- true ->
- ok;
- false ->
- error({unknown_module, M, Modules})
- end;
-check_module(M, _) ->
- error({bad_module, M}).
-
-
-check_module_depend(M, [], _) when is_atom(M) ->
- ok;
-check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) ->
- case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of
- [] ->
- ok;
- Unknown ->
- error({unknown_depend_modules, Unknown})
- end;
-check_module_depend(_M, D, _Modules) ->
- error({bad_depend, D}).
-
-
-check_no_remove_depends(_Module, []) ->
- ok;
-check_no_remove_depends(Module, [Instr|Instrs]) ->
- check_no_remove_depend(Module, Instr),
- check_no_remove_depends(Module, Instrs).
-
-check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, load_module, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, update, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(_, _) ->
- ok.
-
-
-check_change(soft) ->
- ok;
-check_change({advanced, _Something}) ->
- ok;
-check_change(Change) ->
- error({bad_change, Change}).
-
-
-check_purge(soft_purge) ->
- ok;
-check_purge(brutal_purge) ->
- ok;
-check_purge(Purge) ->
- error({bad_purge, Purge}).
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-error(Reason) ->
- throw({error, Reason}).
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
+ ok = ?t:appup_test(inets).
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 12b85a816f..cf28f5a245 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -260,10 +260,10 @@ tftpd_worker(suite) ->
[];
tftpd_worker(Config) when is_list(Config) ->
[] = supervisor:which_children(tftp_sup),
- {ok, Pid0} = inets:start(tftpd, [{host, "localhost"},
- {port, inet_port()}]),
- {ok, _Pid1} = inets:start(tftpd, [{host, "localhost"},
- {port, inet_port()}], stand_alone),
+ {ok, Pid0} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
+ {port, 0}]),
+ {ok, _Pid1} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
+ {port, 0}], stand_alone),
[{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup),
inets:stop(tftpd, Pid0),
@@ -397,13 +397,6 @@ httpc_subtree(Config) when is_list(Config) ->
tsp("httpc_subtree -> done"),
ok.
-inet_port() ->
- {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]),
- {ok, Port} = inet:port(Socket),
- gen_tcp:close(Socket),
- Port.
-
-
tsp(F) ->
tsp(F, []).
tsp(F, A) ->
diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl
index de9aa4562e..3e1a1a3845 100644
--- a/lib/inets/test/old_httpd_SUITE.erl
+++ b/lib/inets/test/old_httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
@@ -155,65 +155,103 @@ all() ->
[
{group, ip},
{group, ssl},
- {group, http_1_1_ip},
- {group, http_1_0_ip},
- {group, http_0_9_ip},
- {group, ipv6},
+ %%{group, http_1_1_ip},
+ %%{group, http_1_0_ip},
+ %%{group, http_0_9_ip},
+ %%{group, ipv6},
{group, tickets}
].
groups() ->
[
{ip, [],
- [ip_mod_alias, ip_mod_actions, ip_mod_security,
- ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api,
- ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get,
- ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium,
- ip_load_heavy, ip_dos_hostname, ip_time_test,
- ip_restart_no_block, ip_restart_disturbing_block,
- ip_restart_non_disturbing_block,
- ip_block_disturbing_idle, ip_block_non_disturbing_idle,
- ip_block_503, ip_block_disturbing_active,
- ip_block_non_disturbing_active,
- ip_block_disturbing_active_timeout_not_released,
- ip_block_disturbing_active_timeout_released,
- ip_block_non_disturbing_active_timeout_not_released,
- ip_block_non_disturbing_active_timeout_released,
- ip_block_disturbing_blocker_dies,
- ip_block_non_disturbing_blocker_dies]},
+ [
+ %%ip_mod_alias,
+ ip_mod_actions,
+ %%ip_mod_security,
+ %% ip_mod_auth,
+ %% ip_mod_auth_api,
+ ip_mod_auth_mnesia_api,
+ %%ip_mod_htaccess,
+ %%ip_mod_cgi,
+ %%ip_mod_esi,
+ %%ip_mod_get,
+ %%ip_mod_head,
+ %%ip_mod_all,
+ %% ip_load_light,
+ %% ip_load_medium,
+ %% ip_load_heavy,
+ %%ip_dos_hostname,
+ ip_time_test
+ %% Replaced by load_config
+ %% ip_restart_no_block,
+ %% ip_restart_disturbing_block,
+ %% ip_restart_non_disturbing_block,
+ %% ip_block_disturbing_idle,
+ %% ip_block_non_disturbing_idle,
+ %% ip_block_503,
+ %% ip_block_disturbing_active,
+ %% ip_block_non_disturbing_active,
+ %% ip_block_disturbing_active_timeout_not_released,
+ %% ip_block_disturbing_active_timeout_released,
+ %% ip_block_non_disturbing_active_timeout_not_released,
+ %% ip_block_non_disturbing_active_timeout_released,
+ %% ip_block_disturbing_blocker_dies,
+ %% ip_block_non_disturbing_blocker_dies
+ ]},
{ssl, [], [{group, essl}]},
{essl, [],
- [essl_mod_alias, essl_mod_actions, essl_mod_security,
- essl_mod_auth, essl_mod_auth_api,
- essl_mod_auth_mnesia_api, essl_mod_htaccess,
- essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head,
- essl_mod_all, essl_load_light, essl_load_medium,
- essl_load_heavy, essl_dos_hostname, essl_time_test,
- essl_restart_no_block, essl_restart_disturbing_block,
- essl_restart_non_disturbing_block,
- essl_block_disturbing_idle,
- essl_block_non_disturbing_idle, essl_block_503,
- essl_block_disturbing_active,
- essl_block_non_disturbing_active,
- essl_block_disturbing_active_timeout_not_released,
- essl_block_disturbing_active_timeout_released,
- essl_block_non_disturbing_active_timeout_not_released,
- essl_block_non_disturbing_active_timeout_released,
- essl_block_disturbing_blocker_dies,
- essl_block_non_disturbing_blocker_dies]},
- {http_1_1_ip, [],
- [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test,
- ip_http_trace, ip_http1_1_head,
- ip_mod_cgi_chunked_encoding_test]},
- {http_1_0_ip, [],
- [ip_head_1_0, ip_get_1_0, ip_post_1_0]},
- {http_0_9_ip, [], [ip_get_0_9]},
- {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
- ipv6_hostname_essl, ipv6_address_essl]},
+ [
+ %%essl_mod_alias,
+ essl_mod_actions,
+ %% essl_mod_security,
+ %% essl_mod_auth,
+ %% essl_mod_auth_api,
+ essl_mod_auth_mnesia_api,
+ %%essl_mod_htaccess,
+ %%essl_mod_cgi,
+ %%essl_mod_esi,
+ %%essl_mod_get,
+ %%essl_mod_head,
+ %% essl_mod_all,
+ %% essl_load_light,
+ %% essl_load_medium,
+ %% essl_load_heavy,
+ %%essl_dos_hostname,
+ essl_time_test
+ %% Replaced by load_config
+ %% essl_restart_no_block,
+ %% essl_restart_disturbing_block,
+ %% essl_restart_non_disturbing_block,
+ %% essl_block_disturbing_idle,
+ %% essl_block_non_disturbing_idle, essl_block_503,
+ %% essl_block_disturbing_active,
+ %% essl_block_non_disturbing_active,
+ %% essl_block_disturbing_active_timeout_not_released,
+ %% essl_block_disturbing_active_timeout_released,
+ %% essl_block_non_disturbing_active_timeout_not_released,
+ %% essl_block_non_disturbing_active_timeout_released,
+ %% essl_block_disturbing_blocker_dies,
+ %% essl_block_non_disturbing_blocker_dies
+ ]},
+ %% {http_1_1_ip, [],
+ %% [
+ %% %%ip_host, ip_chunked, ip_expect,
+ %% %%ip_range,
+ %% %%ip_if_test
+ %% %%ip_http_trace, ip_http1_1_head,
+ %% %%ip_mod_cgi_chunked_encoding_test
+ %% ]},
+ %%{http_1_0_ip, [],
+ %%[ip_head_1_0, ip_get_1_0, ip_post_1_0]},
+ %%{http_0_9_ip, [], [ip_get_0_9]},
+ %% {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
+ %% ipv6_hostname_essl, ipv6_address_essl]},
{tickets, [],
- [ticket_5775, ticket_5865, ticket_5913, ticket_6003,
- ticket_7304]}].
-
+ [%%ticket_5775, ticket_5865,
+ ticket_5913%%, ticket_6003,
+ %%ticket_7304
+ ]}].
init_per_group(ipv6 = _GroupName, Config) ->
case inets_test_lib:has_ipv6_support() of
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index cccfb7a44f..cbcf0362c9 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,7 +18,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.9.8
+INETS_VSN = 5.10
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
-
diff --git a/lib/jinterface/doc/src/jinterface_users_guide.xml b/lib/jinterface/doc/src/jinterface_users_guide.xml
index aaf84e2e3d..5dfe5c0c6d 100644
--- a/lib/jinterface/doc/src/jinterface_users_guide.xml
+++ b/lib/jinterface/doc/src/jinterface_users_guide.xml
@@ -112,6 +112,10 @@
<cell align="left" valign="middle"><seealso marker="java/com/ericsson/otp/erlang/OtpErlangTuple">OtpErlangTuple</seealso></cell>
</row>
<row>
+ <cell align="left" valign="middle">map</cell>
+ <cell align="left" valign="middle"><seealso marker="java/com/ericsson/otp/erlang/OtpErlangMap">OtpErlangMap</seealso></cell>
+ </row>
+ <row>
<cell align="left" valign="middle">term</cell>
<cell align="left" valign="middle"><seealso marker="java/com/ericsson/otp/erlang/OtpErlangObject">OtpErlangObject</seealso></cell>
</row>
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
index 968f284bff..3ef44b8851 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2014. 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
@@ -92,6 +92,7 @@ public class AbstractNode {
static final int dFlagNewFloats = 0x800;
static final int dFlagUnicodeIo = 0x1000;
static final int dFlagUtf8Atoms = 0x10000;
+ static final int dFlagMapTag = 0x20000;
int ntype = NTYPE_R6;
int proto = 0; // tcp/ip
@@ -100,7 +101,7 @@ public class AbstractNode {
int creation = 0;
int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
| dFlagBitBinaries | dFlagNewFloats | dFlagFunTags
- | dflagNewFunTags | dFlagUtf8Atoms;
+ | dflagNewFunTags | dFlagUtf8Atoms | dFlagMapTag;
/* initialize hostname and default cookie */
static {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
new file mode 100644
index 0000000000..03c18e55a2
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -0,0 +1,289 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2013. 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%
+ */
+package com.ericsson.otp.erlang;
+
+import java.io.Serializable;
+
+/**
+ * Provides a Java representation of Erlang maps. Maps are created from one or
+ * more arbitrary Erlang terms.
+ *
+ * <p>
+ * The arity of the map is the number of elements it contains. The keys and
+ * values can be retrieved as arrays and the value for a key can be queried.
+ *
+ */
+public class OtpErlangMap extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ private static final long serialVersionUID = -6410770117696198497L;
+
+ private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
+
+ private OtpErlangObject[] keys = NO_ELEMENTS;
+ private OtpErlangObject[] values = NO_ELEMENTS;
+
+ /**
+ * Create a map from an array of keys and an array of values.
+ *
+ * @param keys
+ * the array of terms to create the map keys from.
+ * @param values
+ * the array of terms to create the map values from.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if any array is empty (null) or contains null elements.
+ */
+ public OtpErlangMap(final OtpErlangObject[] keys,
+ final OtpErlangObject[] values) {
+ this(keys, 0, keys.length, values, 0, values.length);
+ }
+
+ /**
+ * Create a map from an array of terms.
+ *
+ * @param elems
+ * the array of terms to create the map from.
+ * @param start
+ * the offset of the first term to insert.
+ * @param vcount
+ * the number of terms to insert.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if any array is empty (null) or contains null elements.
+ */
+ public OtpErlangMap(final OtpErlangObject[] keys, final int kstart,
+ final int kcount, final OtpErlangObject[] values, final int vstart,
+ final int vcount) {
+ if (keys == null || values == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Map content can't be null");
+ } else if (kcount != vcount) {
+ throw new java.lang.IllegalArgumentException(
+ "Map keys and values must have same arity");
+ } else if (vcount < 1) {
+ this.keys = NO_ELEMENTS;
+ this.values = NO_ELEMENTS;
+ } else {
+ this.keys = new OtpErlangObject[vcount];
+ for (int i = 0; i < vcount; i++) {
+ if (keys[kstart + i] != null) {
+ this.keys[i] = keys[kstart + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Map key cannot be null (element" + (kstart + i)
+ + ")");
+ }
+ }
+ this.values = new OtpErlangObject[vcount];
+ for (int i = 0; i < vcount; i++) {
+ if (values[vstart + i] != null) {
+ this.values[i] = values[vstart + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Map value cannot be null (element" + (vstart + i)
+ + ")");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a map from a stream containing a map encoded in Erlang external
+ * format.
+ *
+ * @param buf
+ * the stream containing the encoded map.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang map.
+ */
+ public OtpErlangMap(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final int arity = buf.read_map_head();
+
+ if (arity > 0) {
+ keys = new OtpErlangObject[arity];
+ values = new OtpErlangObject[arity];
+
+ for (int i = 0; i < arity; i++) {
+ keys[i] = buf.read_any();
+ values[i] = buf.read_any();
+ }
+ } else {
+ keys = NO_ELEMENTS;
+ values = NO_ELEMENTS;
+ }
+ }
+
+ /**
+ * Get the arity of the map.
+ *
+ * @return the number of elements contained in the map.
+ */
+ public int arity() {
+ return keys.length;
+ }
+
+ /**
+ * Get the specified value from the map.
+ *
+ * @param key
+ * the key of the requested value.
+ *
+ * @return the requested value, of null if key is not a valid key.
+ */
+ public OtpErlangObject get(final OtpErlangObject key) {
+ if (key == null) {
+ return null;
+ }
+ for (int i = 0; i < keys.length; i++) {
+ if (key.equals(keys[i])) {
+ return values[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the keys from the map as an array.
+ *
+ * @return an array containing all of the map's keys.
+ */
+ public OtpErlangObject[] keys() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(keys, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get all the values from the map as an array.
+ *
+ * @return an array containing all of the map's values.
+ */
+ public OtpErlangObject[] values() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(values, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get the string representation of the map.
+ *
+ * @return the string representation of the map.
+ */
+ @Override
+ public String toString() {
+ int i;
+ final StringBuffer s = new StringBuffer();
+ final int arity = values.length;
+
+ s.append("#{");
+
+ for (i = 0; i < arity; i++) {
+ if (i > 0) {
+ s.append(",");
+ }
+ s.append(keys[i].toString());
+ s.append(" => ");
+ s.append(values[i].toString());
+ }
+
+ s.append("}");
+
+ return s.toString();
+ }
+
+ /**
+ * Convert this map to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded map should be written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ final int arity = values.length;
+
+ buf.write_map_head(arity);
+
+ for (int i = 0; i < arity; i++) {
+ buf.write_any(keys[i]);
+ buf.write_any(values[i]);
+ }
+ }
+
+ /**
+ * Determine if two maps are equal. Maps are equal if they have the same
+ * arity and all of the elements are equal.
+ *
+ * @param o
+ * the map to compare to.
+ *
+ * @return true if the maps have the same arity and all the elements are
+ * equal.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangMap)) {
+ return false;
+ }
+
+ final OtpErlangMap t = (OtpErlangMap) o;
+ final int a = arity();
+
+ if (a != t.arity()) {
+ return false;
+ }
+
+ for (int i = 0; i < a; i++) {
+ if (!keys[i].equals(t.keys[i])) {
+ return false; // early exit
+ }
+ }
+ for (int i = 0; i < a; i++) {
+ if (!values[i].equals(t.values[i])) {
+ return false; // early exit
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ protected int doHashCode() {
+ final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
+ final int a = arity();
+ hash.combine(a);
+ for (int i = 0; i < a; i++) {
+ hash.combine(keys[i].hashCode());
+ }
+ for (int i = 0; i < a; i++) {
+ hash.combine(values[i].hashCode());
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangMap newMap = (OtpErlangMap) super.clone();
+ newMap.values = values.clone();
+ return newMap;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
index 45a82d6c94..fa0fe18e95 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
@@ -85,6 +85,9 @@ public class OtpExternal {
/** The tag used for new style references */
public static final int newRefTag = 114;
+ /** The tag used for maps */
+ public static final int mapTag = 116;
+
/** The tag used for old Funs */
public static final int funTag = 117;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index 9dc1728346..0d1342d796 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -1202,6 +1202,9 @@ public class OtpInputStream extends ByteArrayInputStream {
case OtpExternal.newRefTag:
return new OtpErlangRef(this);
+ case OtpExternal.mapTag:
+ return new OtpErlangMap(this);
+
case OtpExternal.portTag:
return new OtpErlangPort(this);
@@ -1244,4 +1247,21 @@ public class OtpInputStream extends ByteArrayInputStream {
throw new OtpErlangDecodeException("Uknown data type: " + tag);
}
}
+
+ public int read_map_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ // decode the map header and get arity
+ switch (tag) {
+ case OtpExternal.mapTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid map tag: " + tag);
+ }
+
+ return arity;
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index 78f47aa32f..a78423db44 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -974,4 +974,9 @@ public class OtpOutputStream extends ByteArrayOutputStream {
write_atom(function);
write_long(arity);
}
+
+ public void write_map_head(final int arity) {
+ write1(OtpExternal.mapTag);
+ write4BE(arity);
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
index 1390542194..62fa7f990e 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
@@ -74,6 +74,7 @@ ERL = \
OtpErlangShort\
OtpErlangString\
OtpErlangTuple \
+ OtpErlangMap \
OtpErlangUInt \
OtpErlangUShort
diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl
index de8d611efc..cb725164cd 100644
--- a/lib/jinterface/test/jinterface_SUITE.erl
+++ b/lib/jinterface/test/jinterface_SUITE.erl
@@ -37,7 +37,9 @@
erl_exit_with_reason_any_term/1,
java_exit_with_reason_any_term/1,
status_handler_localStatus/1, status_handler_remoteStatus/1,
- status_handler_connAttempt/1]).
+ status_handler_connAttempt/1,
+ maps/1
+ ]).
-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
@@ -103,7 +105,8 @@ fundamental() ->
nodename, % Nodename.java
register_and_whereis, % RegisterAndWhereis.java
get_names, % GetNames.java
- boolean_atom % BooleanAtom.java
+ boolean_atom, % BooleanAtom.java
+ maps % Maps.java
].
ping() ->
@@ -675,6 +678,17 @@ status_handler_connAttempt(Config) when is_list(Config) ->
"NodeStatusHandler",
[erlang:get_cookie(),node(),?status_handler_connAttempt]).
+%%%-----------------------------------------------------------------
+maps(doc) ->
+ ["Maps.java: "
+ "Tests OtpErlangMap encoding, decoding, toString, get"];
+maps(suite) ->
+ [];
+maps(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "Maps",
+ []).
%%%-----------------------------------------------------------------
%%% INTERNAL FUNCTIONS
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
index 2a3dca463b..a15ed1aa63 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
+++ b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
@@ -46,7 +46,8 @@ JAVA_FILES = \
MboxPing.java \
MboxSendReceive.java \
MboxLinkUnlink.java \
- NodeStatusHandler.java
+ NodeStatusHandler.java \
+ Maps.java
CLASS_FILES = $(JAVA_FILES:.java=.class)
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Maps.java b/lib/jinterface/test/jinterface_SUITE_data/Maps.java
new file mode 100644
index 0000000000..653defc621
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/Maps.java
@@ -0,0 +1,108 @@
+import java.util.Arrays;
+
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangDecodeException;
+import com.ericsson.otp.erlang.OtpErlangList;
+import com.ericsson.otp.erlang.OtpErlangLong;
+import com.ericsson.otp.erlang.OtpErlangMap;
+import com.ericsson.otp.erlang.OtpInputStream;
+import com.ericsson.otp.erlang.OtpOutputStream;
+
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. 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%
+ */
+
+class Maps {
+
+ /*
+ * Implements test case jinterface_SUITE:maps/1
+ *
+ * Test the class OtpErlangMap
+ */
+
+ @SuppressWarnings("resource")
+ public static void main(final String argv[]) {
+
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 0 }, "#{}", 1);
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 1, 100, 0, 1, 97, 100,
+ 0, 1, 98 }, "#{a => b}", 2);
+ // make sure keys are sorted here, jinterface doesn't reorder them
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 106,
+ 100, 0, 1, 97, 97, 1 }, "#{2 => [],a => 1}", 3);
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 1, 104, 1, 97, 3, 108,
+ 0, 0, 0, 1, 100, 0, 1, 114, 106 }, "#{{3} => [r]}", 4);
+
+ try {
+ // #{2 => [],a => 1}
+ final OtpErlangMap map = new OtpErlangMap(new OtpInputStream(
+ new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 106,
+ 100, 0, 1, 97, 97, 1 }));
+
+ if (map.arity() != 2) {
+ fail(5);
+ }
+ if (!new OtpErlangLong(1).equals(map.get(new OtpErlangAtom("a")))) {
+ fail(6);
+ }
+ if (!new OtpErlangList().equals(map.get(new OtpErlangLong(2)))) {
+ fail(7);
+ }
+ if (map.get(new OtpErlangLong(1)) != null) {
+ fail(8);
+ }
+ } catch (final OtpErlangDecodeException e) {
+ fail(99);
+ }
+
+ }
+
+ @SuppressWarnings("resource")
+ private static void runTest(final byte[] in, final String out, final int err) {
+ try {
+ final OtpInputStream is = new OtpInputStream(in);
+
+ final OtpErlangMap map = new OtpErlangMap(is);
+ final String output = map.toString();
+ if (!output.equals(out)) {
+ fail("toString mismatch " + output + " <> " + out, err);
+ }
+
+ final OtpOutputStream os = new OtpOutputStream(map);
+ final byte[] outArray0 = os.toByteArray();
+ final byte[] outArray = new byte[outArray0.length + 1];
+ System.arraycopy(outArray0, 0, outArray, 1, outArray0.length);
+ outArray[0] = (byte) 131;
+ if (!Arrays.equals(in, outArray)) {
+ fail("encode error " + Arrays.toString(outArray), err);
+ }
+ } catch (final OtpErlangDecodeException e) {
+ fail("decode error " + e.getMessage(), err);
+ } catch (final Exception e) {
+ fail("error " + e.getMessage(), err);
+ }
+ }
+
+ private static void fail(final int reason) {
+ System.exit(reason);
+ }
+
+ private static void fail(final String str, final int reason) {
+ System.out.println(str);
+ System.exit(reason);
+ }
+}
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 1954040c3d..c50200fab6 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.5.8
+JINTERFACE_VSN = 1.5.9
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index de3ca1e176..ec5d1f09e4 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -76,8 +76,8 @@ BOOK_FILES = book.xml
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
- $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES) \
- $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES)\
+ $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
# ----------------------------------------------------
diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml
index 7c9d6eecec..8575d94048 100644
--- a/lib/kernel/doc/src/app.xml
+++ b/lib/kernel/doc/src/app.xml
@@ -61,7 +61,8 @@
{applications, Apps},
{env, Env},
{mod, Start},
- {start_phases, Phases}]}.
+ {start_phases, Phases},
+ {runtime_dependencies, RTDeps}]}.
Value Default
----- -------
@@ -77,8 +78,10 @@ Apps [App] []
Env [{Par,Val}] []
Start {Module,StartArgs} []
Phases [{Phase,PhaseArgs}] undefined
+RTDeps [ApplicationVersion] []
Module = Name = App = Par = Phase = atom()
- Val = StartArgs = PhaseArgs = term()</code>
+ Val = StartArgs = PhaseArgs = term()
+ ApplicationVersion = string()</code>
<p><c>Application</c> is the name of the application.</p>
<p>For the application controller, all keys are optional.
The respective default values are used for any omitted keys.</p>
@@ -87,6 +90,8 @@ Phases [{Phase,PhaseArgs}] undefined
<c>description</c>, <c>vsn</c>, <c>modules</c>, <c>registered</c>
and <c>applications</c>. The other keys are ignored by
<c>systools</c>.</p>
+ <warning><p>The <c>RTDeps</c> type was introduced in OTP 17.0 and
+ might be subject to changes during the OTP 17 release.</p></warning>
<taglist>
<tag><c>description</c></tag>
<item>
@@ -185,6 +190,33 @@ Phases [{Phase,PhaseArgs}] undefined
start phases must be a subset of the set of phases defined
for the primary application. Refer to <em>OTP Design Principles</em> for more information.</p>
</item>
+ <tag><marker id="runtime_dependencies"><c>runtime_dependencies</c></marker></tag>
+ <item><p>A list of application versions that the application
+ depends on. An example of such an application version is
+ <c>"kernel-3.0"</c>. Application versions specified as runtime
+ dependencies are minimum requirements. That is, a larger
+ application version than the one specified in the
+ dependency satisfies the requirement. For information on
+ how to compare application versions see
+ <seealso marker="doc/system_principles:versions">the
+ documentation of versions in the system principles
+ guide</seealso>. Note that that the application version
+ specifies a source code version. An additional indirect
+ requirement is that installed binary application of
+ the specified version has been built so that it is
+ compatible with the rest of the system.</p>
+ <p>Some dependencies might only be required in specific runtime
+ scenarios. In the case such optional dependencies exist, these are
+ specified and documented in the corresponding "App" documentation
+ of the specific application.</p>
+ <warning><p>The <c>runtime_dependencies</c> key was introduced in
+ OTP 17.0. The type of its value might be subject to changes during
+ the OTP 17 release.</p></warning>
+ <warning><p>All runtime dependencies specified in OTP applications
+ during the OTP 17 release may not be completely correct. This
+ is actively being worked on. Declared runtime dependencies in OTP
+ applications are expected to be correct in OTP 18.</p></warning>
+ </item>
</taglist>
</section>
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 29eaf348a9..7664fda4db 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -239,10 +239,19 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
<desc>
<p>Sets the value of the configuration parameter <c><anno>Par</anno></c> for
<c><anno>Application</anno></c>.</p>
- <p><c>set_env/3</c> uses the standard <c>gen_server</c> timeout
- value (5000 ms). A <c><anno>Timeout</anno></c> argument can be provided
+ <p><c>set_env/4</c> uses the standard <c>gen_server</c> timeout
+ value (5000 ms). The <c>timeout</c> option can be provided
if another timeout value is useful, for example, in situations
where the application controller is heavily loaded.</p>
+ <p>If <c>set_env/4</c> is called before the application is loaded,
+ the application environment values specified in the <c>Application.app</c>
+ file will override the ones previously set. This is also true for application
+ reloads.</p>
+ <p>The <c>persistent</c> option can be set to <c>true</c>
+ when there is a need to guarantee parameters set with <c>set_env/4</c>
+ will not be overridden by the ones defined in the application resource
+ file on load. This means persistent values will stick after the application
+ is loaded and also on application reload.</p>
<warning>
<p>Use this function only if you know what you are doing,
that is, on your own applications. It is very application
@@ -406,9 +415,11 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
<p>Removes the configuration parameter <c><anno>Par</anno></c> and its value
for <c><anno>Application</anno></c>.</p>
<p><c>unset_env/2</c> uses the standard <c>gen_server</c>
- timeout value (5000 ms). A <c><anno>Timeout</anno></c> argument can be
+ timeout value (5000 ms). The <c>timeout</c> option can be
provided if another timeout value is useful, for example, in
situations where the application controller is heavily loaded.</p>
+ <p><c>unset_env/3</c> also allows the persistent option to be passed
+ (see <c>set_env/4</c> above).</p>
<warning>
<p>Use this function only if you know what you are doing,
that is, on your own applications. It is very application
@@ -448,8 +459,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
<name>Module:start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}</name>
<fsummary>Start an application</fsummary>
<type>
- <v>StartType = normal | {takeover,Node} | {failover,Node}</v>
- <v>&nbsp;Node = node()</v>
+ <v>StartType = <seealso marker="#type-start_type">start_type()</seealso></v>
<v>StartArgs = term()</v>
<v>Pid = pid()</v>
<v>State = term()</v>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 7b8d2940c1..8dae34431b 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -642,6 +642,11 @@
<item>
<p>Symbolic links are not supported on this platform.</p>
</item>
+ <tag><c>eperm</c></tag>
+ <item>
+ <p>User does not have privileges to create symbolic links
+ (<c>SeCreateSymbolicLinkPrivilege</c> on Windows).</p>
+ </item>
</taglist>
</desc>
</func>
@@ -1630,6 +1635,11 @@
<desc>
<p>Sets the current working directory of the file server to
<c><anno>Dir</anno></c>. Returns <c>ok</c> if successful.</p>
+ <p>The functions in the <c>file</c> module usually treat binaries
+ as raw filenames, i.e. they are passed as is even when the encoding
+ of the binary does not agree with <c>file:native_name_encoding()</c>.
+ This function however expects binaries to be encoded according to the
+ value returned by <c>file:native_name_encoding()</c>.</p>
<p>Typical error reasons are:</p>
<taglist>
<tag><c>enoent</c></tag>
@@ -1654,8 +1664,8 @@
<tag><c>no_translation</c></tag>
<item>
<p><c><anno>Dir</anno></c> is a <c>binary()</c> with
- characters coded in ISO-latin-1 and the VM was started
- with the parameter <c>+fnue</c>.</p>
+ characters coded in ISO-latin-1 and the VM is operating
+ with unicode file name encoding.</p>
</item>
</taglist>
<warning>
@@ -1751,7 +1761,7 @@
using async threads potentially makes your system volnerable to slow
client attacks. If set to true and no async threads are available,
the sendfile call will return <c>{error,einval}</c>.
- Introduced in 17.0. Default is false.</item>
+ Introduced in Erlang/OTP 17.0. Default is false.</item>
</taglist>
</p>
</desc>
diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml
index c1b9eac9d7..bd25d1e78d 100644
--- a/lib/kernel/doc/src/ref_man.xml
+++ b/lib/kernel/doc/src/ref_man.xml
@@ -13,12 +13,12 @@
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.
-
+
</legalnotice>
<title>Kernel Reference Manual</title>
@@ -65,4 +65,3 @@
<xi:include href="app.xml"/>
<xi:include href="config.xml"/>
</application>
-
diff --git a/lib/kernel/doc/src/ref_man.xml.src b/lib/kernel/doc/src/ref_man.xml.src
new file mode 100644
index 0000000000..bd25d1e78d
--- /dev/null
+++ b/lib/kernel/doc/src/ref_man.xml.src
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1996</year><year>2013</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.
+
+ </legalnotice>
+
+ <title>Kernel Reference Manual</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p>The <em>Kernel</em> application has all the code necessary to run
+ the Erlang runtime system itself: file servers and code servers
+ and so on.</p>
+ </description>
+ <xi:include href="kernel_app.xml"/>
+ <xi:include href="application.xml"/>
+ <xi:include href="auth.xml"/>
+ <xi:include href="code.xml"/>
+ <xi:include href="disk_log.xml"/>
+ <xi:include href="erl_boot_server.xml"/>
+ <xi:include href="erl_ddll.xml"/>
+ <xi:include href="erl_prim_loader_stub.xml"/>
+ <xi:include href="erlang_stub.xml"/>
+ <xi:include href="error_handler.xml"/>
+ <xi:include href="error_logger.xml"/>
+ <xi:include href="file.xml"/>
+ <xi:include href="gen_tcp.xml"/>
+ <xi:include href="gen_udp.xml"/>
+ <xi:include href="gen_sctp.xml"/>
+ <xi:include href="global.xml"/>
+ <xi:include href="global_group.xml"/>
+ <xi:include href="heart.xml"/>
+ <xi:include href="inet.xml"/>
+ <xi:include href="inet_res.xml"/>
+ <xi:include href="init_stub.xml"/>
+ <xi:include href="net_adm.xml"/>
+ <xi:include href="net_kernel.xml"/>
+ <xi:include href="os.xml"/>
+ <xi:include href="pg2.xml"/>
+ <xi:include href="rpc.xml"/>
+ <xi:include href="seq_trace.xml"/>
+ <xi:include href="user.xml"/>
+ <xi:include href="wrap_log_reader.xml"/>
+ <xi:include href="zlib_stub.xml"/>
+ <xi:include href="app.xml"/>
+ <xi:include href="config.xml"/>
+</application>
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index e32c112e63..77556d1303 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -37,3 +37,4 @@
-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
-define(DFLAG_UTF8_ATOMS, 16#10000).
+-define(DFLAG_MAP_TAG, 16#20000).
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index 4e8ba1b78a..c4bef5188a 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -30,6 +30,8 @@
-export([get_application/0, get_application/1, info/0]).
-export([start_type/0]).
+-export_type([start_type/0]).
+
%%%-----------------------------------------------------------------
-type start_type() :: 'normal'
@@ -58,8 +60,7 @@
%%------------------------------------------------------------------
--callback start(StartType :: normal | {takeover, node()} | {failover, node()},
- StartArgs :: term()) ->
+-callback start(StartType :: start_type(), StartArgs :: term()) ->
{'ok', pid()} | {'ok', pid(), State :: term()} | {'error', Reason :: term()}.
-callback stop(State :: term()) ->
@@ -132,7 +133,7 @@ ensure_all_started(Application, Type) ->
{ok, Started} ->
{ok, lists:reverse(Started)};
{error, Reason, Started} ->
- [stop(App) || App <- Started],
+ _ = [stop(App) || App <- Started],
{error, Reason}
end.
@@ -285,16 +286,18 @@ info() ->
set_env(Application, Key, Val) ->
application_controller:set_env(Application, Key, Val).
--spec set_env(Application, Par, Val, Timeout) -> 'ok' when
+-spec set_env(Application, Par, Val, Opts) -> 'ok' when
Application :: atom(),
Par :: atom(),
Val :: term(),
- Timeout :: timeout().
+ Opts :: [{timeout, timeout()} | {persistent, boolean()}].
set_env(Application, Key, Val, infinity) ->
- application_controller:set_env(Application, Key, Val, infinity);
+ set_env(Application, Key, Val, [{timeout, infinity}]);
set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 ->
- application_controller:set_env(Application, Key, Val, Timeout).
+ set_env(Application, Key, Val, [{timeout, Timeout}]);
+set_env(Application, Key, Val, Opts) when is_list(Opts) ->
+ application_controller:set_env(Application, Key, Val, Opts).
-spec unset_env(Application, Par) -> 'ok' when
Application :: atom(),
@@ -303,15 +306,17 @@ set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 ->
unset_env(Application, Key) ->
application_controller:unset_env(Application, Key).
--spec unset_env(Application, Par, Timeout) -> 'ok' when
+-spec unset_env(Application, Par, Opts) -> 'ok' when
Application :: atom(),
Par :: atom(),
- Timeout :: timeout().
+ Opts :: [{timeout, timeout()} | {persistent, boolean()}].
unset_env(Application, Key, infinity) ->
- application_controller:unset_env(Application, Key, infinity);
+ unset_env(Application, Key, [{timeout, infinity}]);
unset_env(Application, Key, Timeout) when is_integer(Timeout), Timeout>=0 ->
- application_controller:unset_env(Application, Key, Timeout).
+ unset_env(Application, Key, [{timeout, Timeout}]);
+unset_env(Application, Key, Opts) when is_list(Opts) ->
+ application_controller:unset_env(Application, Key, Opts).
-spec get_env(Par) -> 'undefined' | {'ok', Val} when
Par :: atom(),
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index 1a4473593a..ed13035104 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -461,14 +461,16 @@ permit_application(ApplName, Flag) ->
set_env(AppName, Key, Val) ->
- gen_server:call(?AC, {set_env, AppName, Key, Val}).
-set_env(AppName, Key, Val, Timeout) ->
- gen_server:call(?AC, {set_env, AppName, Key, Val}, Timeout).
+ gen_server:call(?AC, {set_env, AppName, Key, Val, []}).
+set_env(AppName, Key, Val, Opts) ->
+ Timeout = proplists:get_value(timeout, Opts, 5000),
+ gen_server:call(?AC, {set_env, AppName, Key, Val, Opts}, Timeout).
unset_env(AppName, Key) ->
- gen_server:call(?AC, {unset_env, AppName, Key}).
-unset_env(AppName, Key, Timeout) ->
- gen_server:call(?AC, {unset_env, AppName, Key}, Timeout).
+ gen_server:call(?AC, {unset_env, AppName, Key, []}).
+unset_env(AppName, Key, Opts) ->
+ Timeout = proplists:get_value(timeout, Opts, 5000),
+ gen_server:call(?AC, {unset_env, AppName, Key, Opts}, Timeout).
%%%-----------------------------------------------------------------
%%% call-back functions from gen_server
@@ -609,8 +611,8 @@ check_para([Else | _ParaList], AppName) ->
| {'change_application_data', _, _}
| {'permit_application', atom() | {'application',atom(),_},_}
| {'start_application', _, _}
- | {'unset_env', _, _}
- | {'set_env', _, _, _}.
+ | {'unset_env', _, _, _}
+ | {'set_env', _, _, _, _}.
-spec handle_call(calls(), {pid(), term()}, state()) ->
{'noreply', state()} | {'reply', term(), state()}.
@@ -858,13 +860,25 @@ handle_call(which_applications, _From, S) ->
end, S#state.running),
{reply, Reply, S};
-handle_call({set_env, AppName, Key, Val}, _From, S) ->
+handle_call({set_env, AppName, Key, Val, Opts}, _From, S) ->
ets:insert(ac_tab, {{env, AppName, Key}, Val}),
- {reply, ok, S};
+ case proplists:get_value(persistent, Opts, false) of
+ true ->
+ Fun = fun(Env) -> lists:keystore(Key, 1, Env, {Key, Val}) end,
+ {reply, ok, S#state{conf_data = change_app_env(S#state.conf_data, AppName, Fun)}};
+ false ->
+ {reply, ok, S}
+ end;
-handle_call({unset_env, AppName, Key}, _From, S) ->
+handle_call({unset_env, AppName, Key, Opts}, _From, S) ->
ets:delete(ac_tab, {env, AppName, Key}),
- {reply, ok, S};
+ case proplists:get_value(persistent, Opts, false) of
+ true ->
+ Fun = fun(Env) -> lists:keydelete(Key, 1, Env) end,
+ {reply, ok, S#state{conf_data = change_app_env(S#state.conf_data, AppName, Fun)}};
+ false ->
+ {reply, ok, S}
+ end;
handle_call({control_application, AppName}, {Pid, _Tag}, S) ->
Control = S#state.control,
@@ -1640,6 +1654,16 @@ merge_env([{App, AppEnv1} | T], Env2, Res) ->
merge_env([], Env2, Res) ->
Env2 ++ Res.
+%% Changes the environment for the given application
+%% If there is no application, an empty one is created
+change_app_env(Env, App, Fun) ->
+ case get_env_key(App, Env) of
+ {value, AppEnv, RestEnv} ->
+ [{App, Fun(AppEnv)} | RestEnv];
+ _ ->
+ [{App, Fun([])} | Env]
+ end.
+
%% Merges envs for an application. Env2 overrides Env1
merge_app_env(Env1, Env2) ->
merge_app_env(Env1, Env2, []).
diff --git a/lib/kernel/src/application_master.erl b/lib/kernel/src/application_master.erl
index 68f78c6eb8..bc15b5a7de 100644
--- a/lib/kernel/src/application_master.erl
+++ b/lib/kernel/src/application_master.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -27,7 +27,7 @@
-include("application_master.hrl").
--record(state, {child, appl_data, children = [], procs = 0, gleader}).
+-record(state, {child, appl_data, children = [], procs = 0, gleader, req=[]}).
%%-----------------------------------------------------------------
%% Func: start_link/1
@@ -205,22 +205,25 @@ terminate_loop(Child, State) ->
%%-----------------------------------------------------------------
%% The Application Master is linked to *all* processes in the group
-%% (application).
+%% (application).
%%-----------------------------------------------------------------
handle_msg({get_child, Tag, From}, State) ->
- From ! {Tag, get_child_i(State#state.child)},
- State;
+ get_child_i(State, Tag, From);
handle_msg({stop, Tag, From}, State) ->
catch terminate(normal, State),
From ! {Tag, ok},
exit(normal);
+handle_msg({child, Ref, GrandChild, Mod}, #state{req=Reqs0}=State) ->
+ {value, {_, Tag, From}, Reqs} = lists:keytake(Ref, 1, Reqs0),
+ From ! {Tag, {GrandChild, Mod}},
+ State#state{req=Reqs};
handle_msg(_, State) ->
State.
-
-terminate(Reason, State) ->
- terminate_child(State#state.child, State),
- kill_children(State#state.children),
+terminate(Reason, State = #state{child=Child, children=Children, req=Reqs}) ->
+ _ = [From ! {Tag, error} || {_, Tag, From} <- Reqs],
+ terminate_child(Child, State),
+ kill_children(Children),
exit(Reason).
@@ -342,8 +345,8 @@ start_supervisor(Type, M, A) ->
loop_it(Parent, Child, Mod, AppState) ->
receive
- {Parent, get_child} ->
- Parent ! {self(), Child, Mod},
+ {Parent, get_child, Ref} ->
+ Parent ! {child, Ref, Child, Mod},
loop_it(Parent, Child, Mod, AppState);
{Parent, terminate} ->
NewAppState = prep_stop(Mod, AppState),
@@ -382,10 +385,15 @@ prep_stop(Mod, AppState) ->
NewAppState
end.
-get_child_i(Child) ->
- Child ! {self(), get_child},
- receive
- {Child, GrandChild, Mod} -> {GrandChild, Mod}
+get_child_i(#state{child=Child, req=Reqs}=State, Tag, From) ->
+ Ref = erlang:make_ref(),
+ case erlang:is_process_alive(Child) of
+ true ->
+ Child ! {self(), get_child, Ref},
+ State#state{req=[{Ref, Tag, From}|Reqs]};
+ false ->
+ From ! {Tag, error},
+ State
end.
terminate_child_i(Child, State) ->
diff --git a/lib/kernel/src/disk_log_server.erl b/lib/kernel/src/disk_log_server.erl
index 684ea5b5db..45334912eb 100644
--- a/lib/kernel/src/disk_log_server.erl
+++ b/lib/kernel/src/disk_log_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -199,7 +199,7 @@ do_open({open, W, #arg{name = Name}=A}=Req, From, State) ->
false when W =:= local ->
case A#arg.distributed of
{true, Nodes} ->
- Fun = fun() -> open_distr_rpc(Nodes, A, From) end,
+ Fun = open_distr_rpc_fun(Nodes, A, From),
_Pid = spawn(Fun),
%% No pending reply is expected, but don't reply yet.
{pending, State};
@@ -225,11 +225,15 @@ do_open({open, W, #arg{name = Name}=A}=Req, From, State) ->
end
end.
+-spec open_distr_rpc_fun([node()], _, _) -> % XXX: underspecified
+ fun(() -> no_return()).
+
+open_distr_rpc_fun(Nodes, A, From) ->
+ fun() -> open_distr_rpc(Nodes, A, From) end.
+
%% Spawning a process is a means to avoid deadlock when
%% disk_log_servers mutually open disk_logs.
--spec open_distr_rpc([node()], _, _) -> no_return(). % XXX: underspecified
-
open_distr_rpc(Nodes, A, From) ->
{AllReplies, BadNodes} = rpc:multicall(Nodes, ?MODULE, dist_open, [A]),
{Ok, Bad} = cr(AllReplies, [], []),
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index fc50ec6717..b127fe2e33 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -116,7 +116,8 @@ make_this_flags(RequestType, OtherNode) ->
?DFLAG_UNICODE_IO bor
?DFLAG_DIST_HDR_ATOM_CACHE bor
?DFLAG_SMALL_ATOM_TAGS bor
- ?DFLAG_UTF8_ATOMS).
+ ?DFLAG_UTF8_ATOMS bor
+ ?DFLAG_MAP_TAG).
handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
{PreOtherFlags,Node,Version} = recv_name(HSData0),
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 23cf74f80f..20b703e084 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -199,7 +199,8 @@ get_cwd(Drive) ->
check_and_call(get_cwd, [file_name(Drive)]).
-spec set_cwd(Dir) -> ok | {error, Reason} when
- Dir :: name(),
+ Dir :: name() | EncodedBinary,
+ EncodedBinary :: binary(),
Reason :: posix() | badarg | no_translation.
set_cwd(Dirname) ->
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index ef878b8d0c..0a4edea452 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -1513,14 +1513,18 @@ delete_global_name(_Name, _Pid) ->
-record(him, {node, locker, vsn, my_tag}).
start_the_locker(DoTrace) ->
- spawn_link(fun() -> init_the_locker(DoTrace) end).
-
-init_the_locker(DoTrace) ->
- process_flag(trap_exit, true), % needed?
- S0 = #multi{do_trace = DoTrace},
- S1 = update_locker_known({add, get_known()}, S0),
- loop_the_locker(S1),
- erlang:error(locker_exited).
+ spawn_link(init_the_locker_fun(DoTrace)).
+
+-spec init_the_locker_fun(boolean()) -> fun(() -> no_return()).
+
+init_the_locker_fun(DoTrace) ->
+ fun() ->
+ process_flag(trap_exit, true), % needed?
+ S0 = #multi{do_trace = DoTrace},
+ S1 = update_locker_known({add, get_known()}, S0),
+ loop_the_locker(S1),
+ erlang:error(locker_exited)
+ end.
loop_the_locker(S) ->
?trace({loop_the_locker,S}),
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index e111cb800e..e5928c7b63 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -194,6 +194,13 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
CodeSize, CodeBinary, Refs,
0,[] % ColdSize, CRrefs
] = binary_to_term(Bin),
+ ?debug_msg("***** ErLLVM *****~nVersion: ~s~nCheckSum: ~w~nConstAlign: ~w~n" ++
+ "ConstSize: ~w~nConstMap: ~w~nLabelMap: ~w~nExportMap ~w~nRefs ~w~n",
+ [Version, CheckSum, ConstAlign, ConstSize, ConstMap, LabelMap, ExportMap,
+ Refs]),
+ %% Write HiPE binary code to a file in the current directory in order to
+ %% debug by disassembling.
+ %% file:write_file("erl.o", CodeBinary, [binary]),
%% Check that we are loading up-to-date code.
version_check(Version, Mod),
case hipe_bifs:check_crc(CheckSum) of
@@ -203,6 +210,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
"please regenerate native code for this runtime system\n", [Mod]),
bad_crc;
true ->
+ put(closures_to_patch, []),
%% Create data segment
{ConstAddr,ConstMap2} =
create_data_segment(ConstAlign, ConstSize, ConstMap),
@@ -220,24 +228,33 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
{MFAs,Addresses} = exports(ExportMap, CodeAddress),
%% Remove references to old versions of the module.
ReferencesToPatch = get_refs_from(MFAs, []),
+ %% io:format("References to patch: ~w~n", [ReferencesToPatch]),
ok = remove_refs_from(MFAs),
%% Patch all dynamic references in the code.
%% Function calls, Atoms, Constants, System calls
ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
+
%% Tell the system where the loaded funs are.
%% (patches the BEAM code to redirect to native.)
case Beam of
[] ->
+ %% This module was previously loaded as BEAM code during system
+ %% start-up before the code server has started (-enable-native-libs
+ %% is active), so we must now patch the pre-existing entries in the
+ %% fun table with the native code addresses for all closures.
+ lists:foreach(fun({FE, DestAddress}) ->
+ hipe_bifs:set_native_address_in_fe(FE, DestAddress)
+ end, erase(closures_to_patch)),
export_funs(Addresses),
ok;
BeamBinary when is_binary(BeamBinary) ->
%% Find all closures in the code.
+ [] = erase(closures_to_patch), %Clean up, assertion.
ClosurePatches = find_closure_patches(Refs),
AddressesOfClosuresToPatch =
calculate_addresses(ClosurePatches, CodeAddress, Addresses),
export_funs(Addresses),
- export_funs(Mod, BeamBinary, Addresses, AddressesOfClosuresToPatch),
- ok
+ export_funs(Mod, BeamBinary, Addresses, AddressesOfClosuresToPatch)
end,
%% Redirect references to the old module to the new module's BEAM stub.
patch_to_emu_step2(OldReferencesToPatch),
@@ -245,6 +262,9 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
%% The call to export_funs/1 above updated the native addresses
%% for the targets, so passing 'Addresses' is not needed.
redirect(ReferencesToPatch),
+ %% Final clean up.
+ _ = erase(hipe_patch_closures),
+ _ = erase(hipe_assert_code_area),
?debug_msg("****************Loader Finished****************\n", []),
{module,Mod} % for compatibility with code:load_file/1
end.
@@ -562,12 +582,17 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) ->
case get(hipe_patch_closures) of
false ->
[]; % This is taken care of when registering the module.
- true -> % We are not loading a module patch these closures
+ true ->
+ %% We are replacing a previosly loaded BEAM module with native code,
+ %% so we must reference the pre-existing entries in the fun table
+ %% from the native code. We must delay actually patching the native
+ %% address into the fun entry to ensure that the native code cannot
+ %% be called until it has been completely fixed up.
RemoteOrLocal = local, % closure code refs are local
DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal),
BEAMAddress = hipe_bifs:fun_to_address(DestMFA),
- FE = hipe_bifs:make_fe(DestAddress, mod(DestMFA),
- {Uniq, Index, BEAMAddress}),
+ FE = hipe_bifs:get_fe(mod(DestMFA), {Uniq, Index, BEAMAddress}),
+ put(closures_to_patch, [{FE,DestAddress}|get(closures_to_patch)]),
?debug_msg("Patch FE(~w) to 0x~.16b->0x~.16b (emu:0x~.16b)\n",
[DestMFA, FE, DestAddress, BEAMAddress]),
?ASSERT(assert_local_patch(Address)),
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 792593246a..41d422d7d4 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -597,8 +597,7 @@ getservbyname(Name, Protocol) when is_atom(Name) ->
Error -> Error
end.
--spec ntoa(IpAddress) ->
- {ok, Address} | {error, einval} when
+-spec ntoa(IpAddress) -> Address | {error, einval} when
Address :: string(),
IpAddress :: ip_address().
ntoa(Addr) ->
diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl
index 2461f3ff25..fdc244f959 100644
--- a/lib/kernel/src/inet_config.erl
+++ b/lib/kernel/src/inet_config.erl
@@ -197,6 +197,9 @@ do_load_resolv({win32,Type}, longnames) ->
win32_load_from_registry(Type),
inet_db:set_lookup([native]);
+do_load_resolv({ose,_}, _) ->
+ inet_db:set_lookup([file]);
+
do_load_resolv(_, _) ->
inet_db:set_lookup([native]).
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index cb8c98ab06..5658c6b6cf 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -114,6 +114,7 @@
pg2]},
{applications, []},
{env, [{error_logger, tty}]},
- {mod, {kernel, []}}
+ {mod, {kernel, []}},
+ {runtime_dependencies, ["erts-6.0", "stdlib-2.0", "sasl-2.4"]}
]
}.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index b946c2d1af..f8f4cc1ec2 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -16,12 +16,10 @@
%%
%% %CopyrightEnd%
{"%VSN%",
- %% Up from - max two major revisions back
+ %% Up from - max one major revision back
[{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
- {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
- {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15
- %% Down to - max two major revisions back
+ {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16
+ %% Down to - max one major revision back
[{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
- {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
- {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15
+ {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16
}.
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index 9ffa9adeab..187fd0001b 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -67,7 +67,7 @@ unsetenv(_) ->
%%% End of BIFs
-spec type() -> {Osfamily, Osname} when
- Osfamily :: unix | win32,
+ Osfamily :: unix | win32 | ose,
Osname :: atom().
type() ->
@@ -230,7 +230,9 @@ unix_cmd(Cmd) ->
%% and the commands are read from standard input. We set the
%% $1 parameter for easy identification of the resident shell.
%%
--define(SHELL, "/bin/sh -s unix:cmd 2>&1").
+-define(ROOT, "/").
+-define(ROOT_ANDROID, "/system").
+-define(SHELL, "bin/sh -s unix:cmd 2>&1").
-define(PORT_CREATOR_NAME, os_cmd_port_creator).
%%
@@ -280,7 +282,12 @@ start_port_srv(Request) ->
end.
start_port_srv_handle({Ref,Client}) ->
- Reply = try open_port({spawn, ?SHELL},[stream]) of
+ Path = case lists:reverse(erlang:system_info(system_architecture)) of
+ % androideabi
+ "ibaediordna" ++ _ -> filename:join([?ROOT_ANDROID, ?SHELL]);
+ _ -> filename:join([?ROOT, ?SHELL])
+ end,
+ Reply = try open_port({spawn, Path},[stream]) of
Port when is_port(Port) ->
(catch port_connect(Port, Client)),
unlink(Port),
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 7dc51495f7..2300b7e901 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -66,7 +66,7 @@
%%------------------------------------------------------------------------
--type state() :: gb_tree().
+-type state() :: gb_trees:tree(pid(), {pid(), reference()}).
%%------------------------------------------------------------------------
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 9ec8a15861..c6cbd1a0ef 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -33,10 +33,10 @@
permit_false_start_local/1, permit_false_start_dist/1, script_start/1,
nodedown_start/1, init2973/0, loop2973/0, loop5606/1]).
--export([config_change/1,
+-export([config_change/1, persistent_env/1,
distr_changed_tc1/1, distr_changed_tc2/1,
ensure_started/1, ensure_all_started/1,
- shutdown_func/1, do_shutdown/1, shutdown_timeout/1]).
+ shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1]).
-define(TESTCASE, testcase_name).
-define(testcase, ?config(?TESTCASE, Config)).
@@ -53,7 +53,9 @@ all() ->
load_use_cache, ensure_started, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
permit_false_start_dist, get_key, get_env, ensure_all_started,
- {group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
+ {group, distr_changed}, config_change, shutdown_func, shutdown_timeout,
+ shutdown_deadlock,
+ persistent_env].
groups() ->
[{reported_bugs, [],
@@ -960,7 +962,7 @@ nodedown_start(Conf) when is_list(Conf) ->
ensure_started(suite) -> [];
ensure_started(doc) -> ["Test application:ensure_started/1."];
-ensure_started(Conf) ->
+ensure_started(_Conf) ->
{ok, Fd} = file:open("app1.app", [write]),
w_app1(Fd),
@@ -980,7 +982,7 @@ ensure_started(Conf) ->
ensure_all_started(suite) -> [];
ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."];
-ensure_all_started(Conf) ->
+ensure_all_started(_Conf) ->
{ok, Fd1} = file:open("app1.app", [write]),
w_app1(Fd1),
@@ -1987,6 +1989,50 @@ get_appls([_ | T], Res) ->
get_appls([], Res) ->
Res.
+persistent_env(suite) ->
+ [];
+persistent_env(doc) ->
+ ["Test set_env/4 and unset_env/3 with persistent true"];
+persistent_env(Conf) when is_list(Conf) ->
+ ok = application:set_env(appinc, own2, persist, [{persistent, true}]),
+ ok = application:set_env(appinc, key1, persist, [{persistent, true}]),
+
+ %% own_env1 and own2 are set in appinc
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, key1),
+
+ %% Changing the environment after loaded reflects and should persist
+ ok = application:set_env(appinc, own_env1, persist, [{persistent, true}]),
+ {ok, persist} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, key1),
+
+ %% On reload, own_env1, own2 and key1 should all persist
+ ok = application:unload(appinc),
+ ok = application:load(appinc()),
+ {ok, persist} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, key1),
+
+ %% Unset own_env1 and key1, own2 should still persist
+ ok = application:unset_env(appinc, own_env1, [{persistent, true}]),
+ ok = application:unset_env(appinc, key1, [{persistent, true}]),
+ undefined = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ undefined = application:get_env(appinc, key1),
+
+ %% own_env1 should be back to its application value on reload
+ ok = application:unload(appinc),
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ undefined = application:get_env(appinc, key1),
+
+ %% Clean up
+ ok = application:unload(appinc).
+
%%%-----------------------------------------------------------------
%%% Tests the 'shutdown_func' kernel config parameter
%%%-----------------------------------------------------------------
@@ -2051,7 +2097,32 @@ shutdown_timeout(Config) when is_list(Config) ->
end,
ok.
+%%%-----------------------------------------------------------------
+%%% Provokes a (previous) application shutdown deadlock
+%%%-----------------------------------------------------------------
+shutdown_deadlock(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir,Config),
+ code:add_path(filename:join([DataDir,deadlock])),
+ %% ok = rpc:call(Cp1, application, start, [sasl]),
+ ok = application:start(deadlock),
+ Tester = self(),
+ application:set_env(deadlock, fail_stop, Tester),
+ spawn(fun() -> Tester ! {stop, application:stop(deadlock)} end),
+ receive
+ {deadlock, Server} ->
+ spawn(fun() ->
+ Master = application_controller:get_master(deadlock),
+ Child = application_master:get_child(Master),
+ Tester ! {child, Child}
+ end),
+ timer:sleep(100),
+ erlang:display({self(), "Sending Continue", Server}),
+ Server ! continue
+ end,
+ [_|_] = application:which_applications(),
+ application:unload(deadlock), % clean up!
+ ok.
%%-----------------------------------------------------------------
diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
index 0c1001bed6..233c7a3f76 100644
--- a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
+++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
@@ -4,5 +4,5 @@
{applications, [kernel, stdlib, sasl]},
{modules, [deadlock]},
{mod, {deadlock, []}},
- {env, [{fail_start, false}]}
+ {env, [{fail_start, false}, {fail_stop, false}]}
]}.
diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
index 5f68bf9078..3ef6105371 100644
--- a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
+++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
@@ -21,7 +21,7 @@ init([sup]) ->
{ok, {{one_for_one, 5, 10}, [
{
sasl_syslog_dm, {?MODULE, start_link, []},
- permanent, brutal_kill, worker,
+ permanent, 25000, worker,
[deadlock]
}
]}};
@@ -32,6 +32,8 @@ init([sup]) ->
init([child]) ->
case application:get_env(deadlock, fail_start) of
{ok, false} ->
+ process_flag(trap_exit, true),
+ io:format("~p: Traps exit~n",[?MODULE]),
%% we must not fail on the first init, otherwise supervisor
%% terminates immediately
{ok, []};
@@ -50,6 +52,14 @@ handle_info(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
+ case application:get_env(deadlock, fail_stop) of
+ {ok, false} -> ok;
+ {ok, Tester} ->
+ Tester ! {deadlock, self()},
+ io:format("~p: Waiting in terminate (~p)~n",[?MODULE,Tester]),
+ receive continue -> ok end
+ end,
+ io:format("~p: terminates~n", [?MODULE]),
ok.
code_change(_OldVsn, State, _Extra) ->
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index c75639ae7e..f6d6cd94ab 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -161,7 +161,13 @@ init_per_suite(Config) when is_list(Config) ->
ok ->
[{sasl,started}]
end,
- ok = application:start(os_mon),
+ ok = case os:type() of
+ {ose,_} ->
+ ok;
+ _ ->
+ application:start(os_mon)
+ end,
+
case os:type() of
{win32, _} ->
Priv = ?config(priv_dir, Config),
@@ -185,7 +191,13 @@ end_per_suite(Config) when is_list(Config) ->
_ ->
ok
end,
- application:stop(os_mon),
+
+ case os:type() of
+ {ose,_} ->
+ ok;
+ _ ->
+ application:stop(os_mon)
+ end,
case proplists:get_value(sasl, Config) of
started ->
application:stop(sasl);
@@ -416,7 +428,13 @@ make_del_dir(Config) when is_list(Config) ->
% because there are processes having that directory as current.
?line ok = ?FILE_MODULE:make_dir(NewDir),
?line {ok,CurrentDir} = file:get_cwd(),
- ?line ok = ?FILE_MODULE:set_cwd(NewDir),
+ case {os:type(), length(NewDir) >= 260 } of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ ?line ok = ?FILE_MODULE:set_cwd(NewDir)
+ end,
try
%% Check that we get an error when trying to create...
%% a deep directory
@@ -473,32 +491,39 @@ cur_dir_0(Config) when is_list(Config) ->
atom_to_list(?MODULE)
++"_curdir"),
?line ok = ?FILE_MODULE:make_dir(NewDir),
- ?line io:format("cd to ~s",[NewDir]),
- ?line ok = ?FILE_MODULE:set_cwd(NewDir),
-
- %% Create a file in the new current directory, and check that it
- %% really is created there
- ?line UncommonName = "uncommon.fil",
- ?line {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write),
- ?line ok = ?FILE_MODULE:close(Fd),
- ?line {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."),
- ?line true = lists:member(UncommonName,NewDirFiles),
-
- %% Delete the directory and return to the old current directory
- %% and check that the created file isn't there (too!)
- ?line expect({error, einval}, {error, eacces},
- ?FILE_MODULE:del_dir(NewDir)),
- ?line ?FILE_MODULE:delete(UncommonName),
- ?line {ok,[]} = ?FILE_MODULE:list_dir("."),
- ?line ok = ?FILE_MODULE:set_cwd(Dir1),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line ok = ?FILE_MODULE:del_dir(NewDir),
- ?line {error, enoent} = ?FILE_MODULE:set_cwd(NewDir),
- ?line ok = ?FILE_MODULE:set_cwd(Dir1),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."),
- ?line false = lists:member(UncommonName,OldDirFiles),
-
+ case {os:type(), length(NewDir) >= 260} of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ io:format("cd to ~s",[NewDir]),
+ ok = ?FILE_MODULE:set_cwd(NewDir),
+
+ %% Create a file in the new current directory, and check that it
+ %% really is created there
+ UncommonName = "uncommon.fil",
+ {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write),
+ ok = ?FILE_MODULE:close(Fd),
+ {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."),
+ true = lists:member(UncommonName,NewDirFiles),
+
+ %% Delete the directory and return to the old current directory
+ %% and check that the created file isn't there (too!)
+ expect({error, einval}, {error, eacces},
+ ?FILE_MODULE:del_dir(NewDir)),
+ ?FILE_MODULE:delete(UncommonName),
+ {ok,[]} = ?FILE_MODULE:list_dir("."),
+ ok = ?FILE_MODULE:set_cwd(Dir1),
+ io:format("cd back to ~s",[Dir1]),
+
+ ok = ?FILE_MODULE:del_dir(NewDir),
+ {error, enoent} = ?FILE_MODULE:set_cwd(NewDir),
+ ok = ?FILE_MODULE:set_cwd(Dir1),
+ io:format("cd back to ~s",[Dir1]),
+ {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."),
+ false = lists:member(UncommonName,OldDirFiles)
+ end,
+
%% Try doing some bad things
?line {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}),
?line {error, enoent} = ?FILE_MODULE:set_cwd(""),
@@ -525,11 +550,11 @@ cur_dir_1(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:seconds(5)),
?line case os:type() of
- {unix, _} ->
- ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
- {win32, _} ->
- win_cur_dir_1(Config)
- end,
+ {win32, _} ->
+ win_cur_dir_1(Config);
+ _ ->
+ ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:")
+ end,
?line [] = flush(),
?line test_server:timetrap_cancel(Dog),
ok.
@@ -712,7 +737,10 @@ open1(Config) when is_list(Config) ->
?line io:format(Fd1,Str,[]),
?line {ok,0} = ?FILE_MODULE:position(Fd1,bof),
?line Str = io:get_line(Fd1,''),
- ?line Str = io:get_line(Fd2,''),
+ ?line case io:get_line(Fd2,'') of
+ Str -> Str;
+ eof -> Str
+ end,
?line ok = ?FILE_MODULE:close(Fd2),
?line {ok,0} = ?FILE_MODULE:position(Fd1,bof),
?line ok = ?FILE_MODULE:truncate(Fd1),
@@ -1246,7 +1274,7 @@ file_info_basic_directory(Config) when is_list(Config) ->
?line test_directory("/", read_write),
?line test_directory("c:/", read_write),
?line test_directory("c:\\", read_write);
- {unix, _} ->
+ _ ->
?line test_directory("/", read)
end,
test_server:timetrap_cancel(Dog).
@@ -1967,7 +1995,6 @@ names(Config) when is_list(Config) ->
?line Name1 = filename:join(RootDir, FileName),
?line Name2 = [RootDir,"/","foo1",".","fil"],
?line Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il],
- ?line Name4 = list_to_atom(Name1),
?line {ok,Fd0} = ?FILE_MODULE:open(Name1,write),
?line ok = ?FILE_MODULE:close(Fd0),
@@ -1980,23 +2007,33 @@ names(Config) when is_list(Config) ->
?line ok = ?FILE_MODULE:close(Fd2),
?line {ok,Fd3} = ?FILE_MODULE:open(Name3,read),
?line ok = ?FILE_MODULE:close(Fd3),
- ?line {ok,Fd4} = ?FILE_MODULE:open(Name4,read),
- ?line ok = ?FILE_MODULE:close(Fd4),
+ case length(Name1) > 255 of
+ true ->
+ io:format("Path too long for an atom:\n\n~p\n", [Name1]);
+ false ->
+ Name4 = list_to_atom(Name1),
+ {ok,Fd4} = ?FILE_MODULE:open(Name4,read),
+ ok = ?FILE_MODULE:close(Fd4)
+ end,
%% Try some path names
?line Path1 = RootDir,
?line Path2 = [RootDir],
?line Path3 = ['',[],[RootDir,[[]]]],
- ?line Path4 = list_to_atom(Path1),
?line {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read),
?line ok = ?FILE_MODULE:close(Fd11),
?line {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read),
?line ok = ?FILE_MODULE:close(Fd12),
?line {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read),
?line ok = ?FILE_MODULE:close(Fd13),
- ?line {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read),
- ?line ok = ?FILE_MODULE:close(Fd14),
-
+ case length(Path1) > 255 of
+ true->
+ io:format("Path too long for an atom:\n\n~p\n", [Path1]);
+ false ->
+ Path4 = list_to_atom(Path1),
+ {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read),
+ ok = ?FILE_MODULE:close(Fd14)
+ end,
?line [] = flush(),
?line test_server:timetrap_cancel(Dog),
ok.
@@ -2030,15 +2067,15 @@ e_delete(Config) when is_list(Config) ->
%% No permission.
?line case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ %% Remove a character device.
+ ?line {error, eacces} = ?FILE_MODULE:delete("nul");
+ _ ->
?line ?FILE_MODULE:write_file_info(
Base, #file_info {mode=0}),
?line {error, eacces} = ?FILE_MODULE:delete(Afile),
?line ?FILE_MODULE:write_file_info(
- Base, #file_info {mode=8#600});
- {win32, _} ->
- %% Remove a character device.
- ?line {error, eacces} = ?FILE_MODULE:delete("nul")
+ Base, #file_info {mode=8#600})
end,
?line [] = flush(),
@@ -2140,6 +2177,9 @@ e_rename(Config) when is_list(Config) ->
%% At least Windows NT can
%% successfully move a file to
%% another drive.
+ ok;
+ {ose, _} ->
+ %% disabled for now
ok
end,
[] = flush(),
@@ -2171,13 +2211,13 @@ e_make_dir(Config) when is_list(Config) ->
%% No permission (on Unix only).
case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ ok;
+ _ ->
?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
{error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
?FILE_MODULE:write_file_info(
- Base, #file_info {mode=8#600});
- {win32, _} ->
- ok
+ Base, #file_info {mode=8#600})
end,
test_server:timetrap_cancel(Dog),
ok.
@@ -2220,14 +2260,14 @@ e_del_dir(Config) when is_list(Config) ->
%% No permission.
case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ ok;
+ _ ->
ADirectory = filename:join(Base, "no_perm"),
ok = ?FILE_MODULE:make_dir(ADirectory),
?FILE_MODULE:write_file_info( Base, #file_info {mode=0}),
{error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
- ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600});
- {win32, _} ->
- ok
+ ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600})
end,
[] = flush(),
test_server:timetrap_cancel(Dog),
@@ -2655,6 +2695,9 @@ symlinks(Config) when is_list(Config) ->
case ?FILE_MODULE:make_symlink(Name, Alias) of
{error, enotsup} ->
{skipped, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skipped, "Windows user not privileged to create symlinks"};
ok ->
?line {ok, Info1} = ?FILE_MODULE:read_file_info(Name),
?line {ok, Info1} = ?FILE_MODULE:read_file_info(Alias),
@@ -3581,7 +3624,11 @@ otp_10852(Config) when is_list(Config) ->
ok = rpc_call(Node, list_dir_all, [B]),
ok = rpc_call(Node, read_file, [B]),
ok = rpc_call(Node, make_link, [B,B]),
- ok = rpc_call(Node, make_symlink, [B,B]),
+ case rpc_call(Node, make_symlink, [B,B]) of
+ ok -> ok;
+ {error, E} when (E =:= enotsup) or (E =:= eperm) ->
+ {win32,_} = os:type()
+ end,
ok = rpc_call(Node, delete, [B]),
ok = rpc_call(Node, make_dir, [B]),
ok = rpc_call(Node, del_dir, [B]),
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index ee8bfcceb1..2df4bf7c95 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -50,6 +50,14 @@
oct_acceptor/1,
otp_7731_server/1, zombie_server/2, do_iter_max_socks/2]).
+init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
+ Dog = case os:type() of
+ {ose,_} ->
+ test_server:timetrap(test_server:minutes(20));
+ _Else ->
+ test_server:timetrap(test_server:seconds(600))
+ end,
+ [{watchdog, Dog}|Config];
init_per_testcase(iter_max_socks, Config) when is_list(Config) ->
Dog = case os:type() of
{win32,_} ->
@@ -58,6 +66,17 @@ init_per_testcase(iter_max_socks, Config) when is_list(Config) ->
test_server:timetrap(test_server:seconds(240))
end,
[{watchdog, Dog}|Config];
+init_per_testcase(accept_system_limit, Config) when is_list(Config) ->
+ case os:type() of
+ {ose,_} ->
+ {skip,"Skip in OSE"};
+ _ ->
+ Dog = test_server:timetrap(test_server:seconds(240)),
+ [{watchdog,Dog}|Config]
+ end;
+init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(600)),
+ [{watchdog, Dog}|Config];
init_per_testcase(_Func, Config) when is_list(Config) ->
Dog = test_server:timetrap(test_server:seconds(240)),
[{watchdog, Dog}|Config].
@@ -537,7 +556,6 @@ otp_3924(Config) when is_list(Config) ->
otp_3924_1(MaxDelay).
otp_3924_1(MaxDelay) ->
- Dog = test_server:timetrap(test_server:seconds(240)),
?line {ok, Node} = start_node(otp_3924),
?line DataLen = 100*1024,
?line Data = otp_3924_data(DataLen),
@@ -548,7 +566,6 @@ otp_3924_1(MaxDelay) ->
?line ok = otp_3924(MaxDelay, Node, Data, DataLen, N)
end),
?line test_server:stop_node(Node),
- test_server:timetrap_cancel(Dog),
ok.
otp_3924(MaxDelay, Node, Data, DataLen, N) ->
@@ -1953,7 +1970,9 @@ accept_system_limit(doc) ->
accept_system_limit(Config) when is_list(Config) ->
?line {ok, LS} = gen_tcp:listen(0, []),
?line {ok, TcpPort} = inet:port(LS),
- ?line Connector = spawn_link(fun () -> connector(TcpPort) end),
+ Me = self(),
+ ?line Connector = spawn_link(fun () -> connector(TcpPort, Me) end),
+ receive {Connector, sync} -> Connector ! {self(), continue} end,
?line ok = acceptor(LS, false, []),
?line Connector ! stop,
ok.
@@ -1970,8 +1989,10 @@ acceptor(LS, GotSL, A) ->
error
end.
-connector(TcpPort) ->
+connector(TcpPort, Tester) ->
ManyPorts = open_ports([]),
+ Tester ! {self(), sync},
+ receive {Tester, continue} -> timer:sleep(100) end,
ConnF = fun (Port) ->
case catch gen_tcp:connect({127,0,0,1}, TcpPort, []) of
{ok, Sock} ->
@@ -2705,13 +2726,11 @@ wrapping_oct(doc) ->
wrapping_oct(suite) ->
[];
wrapping_oct(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(600)),
{ok,Sock} = gen_tcp:listen(0,[{active,false},{mode,binary}]),
{ok,Port} = inet:port(Sock),
spawn_link(?MODULE,oct_acceptor,[Sock]),
Res = oct_datapump(Port,16#1FFFFFFFF),
gen_tcp:close(Sock),
- test_server:timetrap_cancel(Dog),
ok = Res,
ok.
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 320b23bea1..35d3b75b34 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -93,10 +93,10 @@ start_check(Type, Name) ->
start_check(Type, Name, []).
start_check(Type, Name, Envs) ->
Args = case ?t:os_type() of
- {win32,_} ->
- "-heart " ++ env_encode([{"HEART_COMMAND", no_reboot}|Envs]);
+ {win32,_} ->
+ "+t50000 -heart " ++ env_encode([{"HEART_COMMAND", no_reboot}|Envs]);
_ ->
- "-heart " ++ env_encode(Envs)
+ "+t50000 -heart " ++ env_encode(Envs)
end,
{ok, Node} = case Type of
loose ->
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 75f51eb076..78f5e93fc3 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -23,9 +23,6 @@
-include_lib("test_server/include/test_server.hrl").
-% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(1)).
-
% Test server specific exports
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
@@ -59,11 +56,8 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+ Config.
+end_per_testcase(_Case, _Config) ->
ok.
%
@@ -78,17 +72,18 @@ app_test(Config) when is_list(Config) ->
ok.
-%% Test that appup allows upgrade from/downgrade to a maximum of two
-%% major releases back.
+%% Test that appup allows upgrade from/downgrade to a maximum of one
+%% major release back.
appup_test(_Config) ->
- do_appup_tests(create_test_vsns()).
+ appup_tests(kernel,create_test_vsns(kernel)).
-do_appup_tests({[],[]}) ->
+appup_tests(_App,{[],[]}) ->
{skip,"no previous releases available"};
-do_appup_tests({OkVsns,NokVsns}) ->
- application:load(kernel),
- {_,_,Vsn} = lists:keyfind(kernel,1,application:loaded_applications()),
- AppupFile = filename:join([code:lib_dir(kernel),ebin,"kernel.appup"]),
+appup_tests(App,{OkVsns,NokVsns}) ->
+ application:load(App),
+ {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),
+ AppupFileName = atom_to_list(App) ++ ".appup",
+ AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),
{ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
ct:log("~p~n",[AppupScript]),
ct:log("Testing ok versions: ~p~n",[OkVsns]),
@@ -99,13 +94,12 @@ do_appup_tests({OkVsns,NokVsns}) ->
check_appup(NokVsns,DownTo,error),
ok.
-create_test_vsns() ->
+create_test_vsns(App) ->
This = erlang:system_info(otp_release),
FirstMajor = previous_major(This),
SecondMajor = previous_major(FirstMajor),
- ThirdMajor = previous_major(SecondMajor),
- Ok = kernel_vsn([FirstMajor,SecondMajor]),
- Nok0 = kernel_vsn([ThirdMajor]),
+ Ok = app_vsn(App,[FirstMajor]),
+ Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
[Ok1|_] ->
[Ok1 ++ ",1" | Nok0]; % illegal
@@ -121,18 +115,36 @@ previous_major("r"++Rel) ->
previous_major(Rel) ->
integer_to_list(list_to_integer(Rel)-1).
-kernel_vsn([R|Rs]) ->
- case test_server:is_release_available(R) of
- true ->
- {ok,N} = test_server:start_node(prevrel,peer,[{erl,[{release,R}]}]),
- As = rpc:call(N,application,which_applications,[]),
- {_,_,KV} = lists:keyfind(kernel,1,As),
- test_server:stop_node(N),
- [KV|kernel_vsn(Rs)];
+app_vsn(App,[R|Rs]) ->
+ OldRel =
+ case test_server:is_release_available(R) of
+ true ->
+ {release,R};
+ false ->
+ case ct:get_config({otp_releases,list_to_atom(R)}) of
+ undefined ->
+ false;
+ Prog0 ->
+ case os:find_executable(Prog0) of
+ false ->
+ false;
+ Prog ->
+ {prog,Prog}
+ end
+ end
+ end,
+ case OldRel of
false ->
- kernel_vsn(Rs)
+ app_vsn(App,Rs);
+ _ ->
+ {ok,N} = test_server:start_node(prevrel,peer,[{erl,[OldRel]}]),
+ _ = rpc:call(N,application,load,[App]),
+ As = rpc:call(N,application,loaded_applications,[]),
+ {_,_,V} = lists:keyfind(App,1,As),
+ test_server:stop_node(N),
+ [V|app_vsn(App,Rs)]
end;
-kernel_vsn([]) ->
+app_vsn(_App,[]) ->
[].
check_appup([Vsn|Vsns],Instrs,Expected) ->
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 199e597e78..05bd5b3a3d 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. 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
@@ -78,7 +78,7 @@
_ -> apply(?PRIM_FILE, F, [H | A])
end).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [].
all() ->
[read_write_file, {group, dirs}, {group, files},
@@ -183,7 +183,6 @@ time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) ->
read_write_file(suite) -> [];
read_write_file(doc) -> [];
read_write_file(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -232,7 +231,6 @@ read_write_file(Config) when is_list(Config) ->
?line {ok,Bin5} = ?PRIM_FILE:read_file(Name),
?line {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -254,7 +252,6 @@ make_del_dir_b(Config) when is_list(Config) ->
Result.
make_del_dir(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -269,7 +266,13 @@ make_del_dir(Config, Handle, Suffix) ->
% because there are processes having that directory as current.
?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
?line {ok, CurrentDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+ case {os:type(), length(NewDir) >= 260 } of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir])
+ end,
try
%% Check that we get an error when trying to create...
%% a deep directory
@@ -302,9 +305,7 @@ make_del_dir(Config, Handle, Suffix) ->
{error, einval} -> ok %FreeBSD
end,
?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]),
- ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]),
-
- ?line test_server:timetrap_cancel(Dog)
+ ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]])
after
?line ok = ?PRIM_FILE_call(set_cwd, Handle, [CurrentDir])
end,
@@ -324,7 +325,6 @@ cur_dir_0b(Config) when is_list(Config) ->
Result.
cur_dir_0(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
%% Find out the current dir, and cd to it ;-)
?line {ok,BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
?line Dir1 = BaseDir ++ "", %% Check that it's a string
@@ -341,31 +341,37 @@ cur_dir_0(Config, Handle) ->
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir, DirName),
?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
- ?line io:format("cd to ~s",[NewDir]),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
-
- %% Create a file in the new current directory, and check that it
- %% really is created there
- ?line UncommonName = "uncommon.fil",
- ?line {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]),
- ?line ok = ?PRIM_FILE:close(Fd),
- ?line {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
- ?line true = lists:member(UncommonName,NewDirFiles),
-
- %% Delete the directory and return to the old current directory
- %% and check that the created file isn't there (too!)
- ?line expect({error, einval}, {error, eacces}, {error, eexist},
+ case {os:type(), length(NewDir) >= 260} of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ io:format("cd to ~s",[NewDir]),
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+
+ %% Create a file in the new current directory, and check that it
+ %% really is created there
+ UncommonName = "uncommon.fil",
+ {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]),
+ ok = ?PRIM_FILE:close(Fd),
+ {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ true = lists:member(UncommonName,NewDirFiles),
+
+ %% Delete the directory and return to the old current directory
+ %% and check that the created file isn't there (too!)
+ expect({error, einval}, {error, eacces}, {error, eexist},
?PRIM_FILE_call(del_dir, Handle, [NewDir])),
- ?line ?PRIM_FILE_call(delete, Handle, [UncommonName]),
- ?line {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]),
- ?line {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
- ?line false = lists:member(UncommonName,OldDirFiles),
+ ?PRIM_FILE_call(delete, Handle, [UncommonName]),
+ {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ io:format("cd back to ~s",[Dir1]),
+ ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]),
+ {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ io:format("cd back to ~s",[Dir1]),
+ {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ false = lists:member(UncommonName,OldDirFiles)
+ end,
%% Try doing some bad things
?line {error, badarg} =
@@ -385,7 +391,6 @@ cur_dir_0(Config, Handle) ->
?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
?line false = lists:member($\\, BaseDir),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Tests ?PRIM_FILE:get_cwd/1.
@@ -404,16 +409,13 @@ cur_dir_1b(Config) when is_list(Config) ->
Result.
cur_dir_1(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
?line case os:type() of
- {unix, _} ->
- ?line {error, enotsup} =
- ?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
{win32, _} ->
- win_cur_dir_1(Config, Handle)
+ win_cur_dir_1(Config, Handle);
+ _ ->
+ ?line {error, enotsup} =
+ ?PRIM_FILE_call(get_cwd, Handle, ["d:"])
end,
- ?line test_server:timetrap_cancel(Dog),
ok.
win_cur_dir_1(_Config, Handle) ->
@@ -439,7 +441,6 @@ win_cur_dir_1(_Config, Handle) ->
open1(suite) -> [];
open1(doc) -> [];
open1(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -453,7 +454,10 @@ open1(Config) when is_list(Config) ->
?line ?PRIM_FILE:write(Fd1,Str),
?line {ok,0} = ?PRIM_FILE:position(Fd1,bof),
?line {ok, Str} = ?PRIM_FILE:read(Fd1,Length),
- ?line {ok, Str} = ?PRIM_FILE:read(Fd2,Length),
+ ?line case ?PRIM_FILE:read(Fd2,Length) of
+ {ok,Str} -> Str;
+ eof -> Str
+ end,
?line ok = ?PRIM_FILE:close(Fd2),
?line {ok,0} = ?PRIM_FILE:position(Fd1,bof),
?line ok = ?PRIM_FILE:truncate(Fd1),
@@ -462,7 +466,6 @@ open1(Config) when is_list(Config) ->
?line {ok,Fd3} = ?PRIM_FILE:open(Name, [read]),
?line eof = ?PRIM_FILE:read(Fd3,Length),
?line ok = ?PRIM_FILE:close(Fd3),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Tests all open modes.
@@ -514,7 +517,6 @@ modes(Config) when is_list(Config) ->
close(suite) -> [];
close(doc) -> [];
close(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -531,13 +533,11 @@ close(Config) when is_list(Config) ->
?line Val = ?PRIM_FILE:close(Fd1),
?line io:format("Second close gave: ~p", [Val]),
- ?line test_server:timetrap_cancel(Dog),
ok.
access(suite) -> [];
access(doc) -> [];
access(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -559,7 +559,6 @@ access(Config) when is_list(Config) ->
?line {ok, Str} = ?PRIM_FILE:read(Fd3,length(Str)),
?line ok = ?PRIM_FILE:close(Fd3),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Tests ?PRIM_FILE:read/2 and ?PRIM_FILE:write/2.
@@ -567,7 +566,6 @@ access(Config) when is_list(Config) ->
read_write(suite) -> [];
read_write(doc) -> [];
read_write(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -579,7 +577,6 @@ read_write(Config) when is_list(Config) ->
?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
?line read_write_test(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
read_write_test(File) ->
@@ -597,7 +594,6 @@ read_write_test(File) ->
pread_write(suite) -> [];
pread_write(doc) -> [];
pread_write(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -609,7 +605,6 @@ pread_write(Config) when is_list(Config) ->
?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
?line pread_write_test(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
pread_write_test(File) ->
@@ -629,7 +624,6 @@ pread_write_test(File) ->
append(doc) -> "Test appending to a file.";
append(suite) -> [];
append(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -656,13 +650,11 @@ append(Config) when is_list(Config) ->
?line Expected = list_to_binary([First, Second, Third]),
?line {ok, Expected} = ?PRIM_FILE:read_file(Name1),
- ?line test_server:timetrap_cancel(Dog),
ok.
exclusive(suite) -> [];
exclusive(doc) -> "Test exclusive access to a file.";
exclusive(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -672,7 +664,6 @@ exclusive(Config) when is_list(Config) ->
?line {ok,Fd} = ?PRIM_FILE:open(Name, [write, exclusive]),
?line {error, eexist} = ?PRIM_FILE:open(Name, [write, exclusive]),
?line ok = ?PRIM_FILE:close(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -681,7 +672,6 @@ exclusive(Config) when is_list(Config) ->
pos1(suite) -> [];
pos1(doc) -> [];
pos1(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -738,13 +728,11 @@ pos1(Config) when is_list(Config) ->
?line {ok, 0} = ?PRIM_FILE:position(Fd2,{eof,-8}),
?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1),
?line {error, einval} = ?PRIM_FILE:position(Fd2,{eof,-9}),
- ?line test_server:timetrap_cancel(Dog),
ok.
pos2(suite) -> [];
pos2(doc) -> [];
pos2(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -761,7 +749,6 @@ pos2(Config) when is_list(Config) ->
?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1),
?line io:format("DONE"),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -779,7 +766,6 @@ file_info_basic_file_b(Config) when is_list(Config) ->
Result.
file_info_basic_file(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
%% Create a short file.
@@ -808,7 +794,6 @@ file_info_basic_file(Config, Handle, Suffix) ->
?line {MD, MT} = ModifyTime,
?line all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)),
- ?line test_server:timetrap_cancel(Dog),
ok.
file_info_basic_directory_a(suite) -> [];
@@ -825,8 +810,6 @@ file_info_basic_directory_b(Config) when is_list(Config) ->
Result.
file_info_basic_directory(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
%% Note: filename:join/1 removes any trailing slash,
%% which is essential for ?PRIM_FILE:read_file_info/1 to work on
%% platforms such as Windows95.
@@ -843,10 +826,10 @@ file_info_basic_directory(Config, Handle) ->
?line test_directory("/", read_write, Handle),
?line test_directory("c:/", read_write, Handle),
?line test_directory("c:\\", read_write, Handle);
- {unix, _} ->
+ _ ->
?line test_directory("/", read, Handle)
end,
- ?line test_server:timetrap_cancel(Dog).
+ ok.
test_directory(Name, ExpectedAccess, Handle) ->
?line {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]),
@@ -887,14 +870,12 @@ file_info_bad_b(Config) when is_list(Config) ->
Result.
file_info_bad(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = filename:join([?config(priv_dir, Config)]),
?line {error, enoent} =
?PRIM_FILE_call(
read_file_info, Handle,
[filename:join(RootDir,
atom_to_list(?MODULE)++"_nonexistent")]),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Test that the file times behave as they should.
@@ -1189,7 +1170,6 @@ get_good_directory(Config) ->
truncate(suite) -> [];
truncate(doc) -> [];
truncate(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -1215,14 +1195,12 @@ truncate(Config) when is_list(Config) ->
?line {ok, 5} = ?PRIM_FILE:position(Fd2, 5),
?line {error, _} = ?PRIM_FILE:truncate(Fd2),
- ?line test_server:timetrap_cancel(Dog),
ok.
datasync(suite) -> [];
datasync(doc) -> "Tests that ?PRIM_FILE:datasync/1 at least doesn't crash.";
datasync(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Sync = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1233,14 +1211,12 @@ datasync(Config) when is_list(Config) ->
?line ok = ?PRIM_FILE:datasync(Fd),
?line ok = ?PRIM_FILE:close(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
sync(suite) -> [];
sync(doc) -> "Tests that ?PRIM_FILE:sync/1 at least doesn't crash.";
sync(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Sync = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1251,14 +1227,12 @@ sync(Config) when is_list(Config) ->
?line ok = ?PRIM_FILE:sync(Fd),
?line ok = ?PRIM_FILE:close(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
advise(suite) -> [];
advise(doc) -> "Tests that ?PRIM_FILE:advise/4 at least doesn't crash.";
advise(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Advise = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1322,7 +1296,6 @@ advise(Config) when is_list(Config) ->
?line eof = ?PRIM_FILE:read_line(Fd9),
?line ok = ?PRIM_FILE:close(Fd9),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1366,7 +1339,6 @@ check_large_write(Dog, Fd, _, _, []) ->
allocate(suite) -> [];
allocate(doc) -> "Tests that ?PRIM_FILE:allocate/3 at least doesn't crash.";
allocate(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Allocate = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1399,7 +1371,6 @@ allocate(Config) when is_list(Config) ->
?line ok = ?PRIM_FILE:write(Fd4, Line2),
?line ok = ?PRIM_FILE:close(Fd4),
- ?line test_server:timetrap_cancel(Dog),
ok.
allocate_and_assert(Fd, Offset, Length) ->
@@ -1447,7 +1418,6 @@ delete_b(Config) when is_list(Config) ->
Result.
delete(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -1463,7 +1433,6 @@ delete(Config, Handle, Suffix) ->
?line {error, _} = ?PRIM_FILE:open(Name, [read]),
%% Try deleting a nonexistent file
?line {error, enoent} = ?PRIM_FILE_call(delete, Handle, [Name]),
- ?line test_server:timetrap_cancel(Dog),
ok.
rename_a(suite) ->[];
@@ -1480,7 +1449,6 @@ rename_b(Config) when is_list(Config) ->
Result.
rename(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line FileName1 = atom_to_list(?MODULE)++"_rename"++Suffix++".fil",
?line FileName2 = atom_to_list(?MODULE)++"_rename"++Suffix++".ful",
@@ -1533,7 +1501,6 @@ rename(Config, Handle, Suffix) ->
?PRIM_FILE_call(rename, Handle, [DirName2, Name2foo]),
?line io:format("Errmsg2: ~p",[Msg2]),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1568,15 +1535,15 @@ e_delete(Config) when is_list(Config) ->
%% No permission.
?line case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ %% Remove a character device.
+ ?line {error, eacces} = ?PRIM_FILE:delete("nul");
+ _ ->
?line ?PRIM_FILE:write_file_info(
Base, #file_info {mode=0}),
?line {error, eacces} = ?PRIM_FILE:delete(Afile),
?line ?PRIM_FILE:write_file_info(
- Base, #file_info {mode=8#600});
- {win32, _} ->
- %% Remove a character device.
- ?line {error, eacces} = ?PRIM_FILE:delete("nul")
+ Base, #file_info {mode=8#600})
end,
?line test_server:timetrap_cancel(Dog),
@@ -1656,7 +1623,12 @@ e_rename(Config) when is_list(Config) ->
%% XXX - Gross hack!
?line Comment =
case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ %% At least Windows NT can
+ %% successfully move a file to
+ %% another drive.
+ ok;
+ {unix, _ } ->
OtherFs = "/tmp",
?line NameOnOtherFs =
filename:join(OtherFs,
@@ -1681,10 +1653,8 @@ e_rename(Config) when is_list(Config) ->
Else
end,
Com;
- {win32, _} ->
- %% At least Windows NT can
- %% successfully move a file to
- %% another drive.
+ {ose, _} ->
+ %% disabled for now
ok
end,
?line test_server:timetrap_cancel(Dog),
@@ -1714,14 +1684,14 @@ e_make_dir(Config) when is_list(Config) ->
%% No permission (on Unix only).
case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ ok;
+ _ ->
?line ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}),
?line {error, eacces} =
?PRIM_FILE:make_dir(filename:join(Base, "xxxx")),
?line
- ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600});
- {win32, _} ->
- ok
+ ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600})
end,
?line test_server:timetrap_cancel(Dog),
ok.
@@ -1767,15 +1737,15 @@ e_del_dir(Config) when is_list(Config) ->
%% No permission.
case os:type() of
- {unix, _} ->
+ {win32, _} ->
+ ok;
+ _ ->
?line ADirectory = filename:join(Base, "no_perm"),
?line ok = ?PRIM_FILE:make_dir(ADirectory),
?line ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}),
?line {error, eacces} = ?PRIM_FILE:del_dir(ADirectory),
?line ?PRIM_FILE:write_file_info(
- Base, #file_info {mode=8#600});
- {win32, _} ->
- ok
+ Base, #file_info {mode=8#600})
end,
?line test_server:timetrap_cancel(Dog),
ok.
@@ -2023,6 +1993,9 @@ symlinks(Config, Handle, Suffix) ->
case ?PRIM_FILE_call(make_symlink, Handle, [Name, Alias]) of
{error, enotsup} ->
{skipped, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skipped, "Windows user not privileged to create links"};
ok ->
?line {ok, Info1} =
?PRIM_FILE_call(read_file_info, Handle, [Name]),
diff --git a/lib/megaco/aclocal.m4 b/lib/megaco/aclocal.m4
index 46b30a16b3..2b47f7c4bc 100644
--- a/lib/megaco/aclocal.m4
+++ b/lib/megaco/aclocal.m4
@@ -74,6 +74,21 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re
AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)])
+dnl Cross compilation variables for OSE
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
+
])
AC_DEFUN(ERL_XCOMP_SYSROOT_INIT,
@@ -488,6 +503,8 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support,
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -500,6 +517,8 @@ else
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -728,6 +747,12 @@ if test "X$host_os" = "Xwin32"; then
THR_LIBS=
THR_LIB_NAME=win32_threads
THR_LIB_TYPE=win32_threads
+elif test "X$host_os" = "Xose"; then
+ AC_MSG_RESULT(yes)
+ THR_DEFS="-DOSE_THREADS"
+ THR_LIBS=
+ THR_LIB_NAME=ose_threads
+ THR_LIB_TYPE=ose_threads
else
AC_MSG_RESULT(no)
THR_DEFS=
@@ -1078,9 +1103,22 @@ case "$THR_LIB_NAME" in
test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes
;;
- pthread)
- ETHR_THR_LIB_BASE_DIR=pthread
- AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ pthread|ose_threads)
+ case "$THR_LIB_NAME" in
+ pthread)
+ ETHR_THR_LIB_BASE_DIR=pthread
+ AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ ;;
+ ose_threads)
+ AC_DEFINE(ETHR_OSE_THREADS, 1,
+ [Define if you have OSE style threads])
+ ETHR_THR_LIB_BASE_DIR=ose
+ AC_CHECK_HEADER(ose_spi/ose_spi.h,
+ AC_DEFINE(HAVE_OSE_SPI_H, 1,
+ [Define if you have the "ose_spi/ose_spi.h" header file.]))
+ ;;
+ esac
+ if test "x$THR_LIB_NAME" == "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1139,6 +1177,7 @@ case "$THR_LIB_NAME" in
*) ;;
esac
+ fi
dnl We sometimes need ETHR_DEFS in order to find certain headers
dnl (at least for pthread.h on osf1).
saved_cppflags="$CPPFLAGS"
@@ -1151,7 +1190,6 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for headers
dnl
-
AC_CHECK_HEADER(pthread.h, \
AC_DEFINE(ETHR_HAVE_PTHREAD_H, 1, \
[Define if you have the <pthread.h> header file.]))
@@ -1184,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
-
+ if test "x$THR_LIB_NAME" == "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
@@ -1311,6 +1349,8 @@ case "$THR_LIB_NAME" in
AC_MSG_RESULT([$linux_futex])
test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.])
+ fi
+
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(long long)
diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src
index 40265166ae..6ab85a1bbc 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -112,7 +112,10 @@
megaco_trans_sup, megaco_misc_sup, megaco_sup]},
{applications, [stdlib, kernel]},
{env, []},
- {mod, {megaco_sup, []}}
+ {mod, {megaco_sup, []}},
+ {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","kernel-3.0",
+ "et-1.5","erts-6.0","debugger-4.0",
+ "asn1-3.0"]}
]}.
diff --git a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
index 8a4f4e7509..7d82262a59 100644
--- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
+++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
@@ -66,7 +66,7 @@ version_of(_EC, Binary, 3, [AsnModV1, AsnModV2, AsnModV3])
version_of([], _Binary, Err) ->
{error, {decode_failed, lists:reverse(Err)}};
version_of([AsnMod|AsnMods], Binary, Errs) when is_atom(AsnMod) ->
- case (catch asn1rt:decode(AsnMod, 'MegacoMessage', Binary)) of
+ case (catch AsnMod:decode('MegacoMessage', Binary)) of
{ok, M} ->
V = (M#'MegacoMessage'.mess)#'Message'.version,
{ok, V};
@@ -82,14 +82,14 @@ version_of([AsnMod|AsnMods], Binary, Errs) when is_atom(AsnMod) ->
encode_message([native], MegaMsg, AsnMod, _TransMod, binary)
when is_record(MegaMsg, 'MegacoMessage') ->
- asn1rt:encode(AsnMod, 'MegacoMessage', MegaMsg);
+ AsnMod:encode('MegacoMessage', MegaMsg);
encode_message(EC, MegaMsg, AsnMod, TransMod, binary)
when is_list(EC) andalso is_record(MegaMsg, 'MegacoMessage') ->
case (catch TransMod:tr_message(MegaMsg, encode, EC)) of
{'EXIT', Reason} ->
{error, Reason};
MegaMsg2 ->
- asn1rt:encode(AsnMod, 'MegacoMessage', MegaMsg2)
+ AsnMod:encode('MegacoMessage', MegaMsg2)
end;
encode_message(EC, MegaMsg, AsnMod, TransMod, io_list) ->
case encode_message(EC, MegaMsg, AsnMod, TransMod, binary) of
@@ -276,7 +276,7 @@ decode_message_dynamic(_EC, _BadBin, _Mods, _Type) ->
decode_message(EC, Bin, AsnMod, TransMod, _) ->
- case asn1rt:decode(AsnMod, 'MegacoMessage', Bin) of
+ case AsnMod:decode('MegacoMessage', Bin) of
{ok, MegaMsg} ->
case EC of
[native] ->
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 01d429d0ae..373f5199bf 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.17.0.3
+MEGACO_VSN = 3.17.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
index 914ec77721..72e9bd7e8f 100644
--- a/lib/mnesia/doc/src/mnesia.xml
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1204,7 +1204,11 @@ mnesia:create_table(person,
<desc>
<p>Performs a user initiated dump of the local log file.
This is usually not necessary since Mnesia, by default,
- manages this automatically.</p>
+ manages this automatically.
+ See configuration parameters
+ <seealso marker="#dump_log_time_threshold">dump_log_time_threshold</seealso> and
+ <seealso marker="#dump_log_write_threshold">dump_log_write_threshold</seealso>.
+ </p>
</desc>
</func>
<func>
@@ -2208,6 +2212,18 @@ mnesia:create_table(employee,
</desc>
</func>
<func>
+ <name>sync_log() -> ok | {error, Reason} </name>
+ <fsummary>Perform a file sync of the local log file.</fsummary>
+ <desc>
+ <p>Ensures that the local transaction log file is synced to disk.
+ On a single node system data written to disk tables, since the last dump,
+ can be lost in case of a power outage.
+ See <seealso marker="#dump_log/0">dump_log/0</seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>sync_transaction(Fun, [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun} </name>
<fsummary>Synchronously execute a transaction.</fsummary>
<desc>
@@ -2445,7 +2461,7 @@ mnesia:create_table(employee,
<name>table(Tab [,[Option]]) -> QueryHandle </name>
<fsummary>Return a QLC query handle.</fsummary>
<desc>
- <p> <marker id="qlc_table"></marker>
+ <p><marker id="qlc_table"></marker>
Returns a QLC (Query List Comprehension) query handle, see
<seealso marker="stdlib:qlc">qlc(3)</seealso>.The module <c>qlc</c> implements a query language, it
can use mnesia tables as sources of data. Calling
@@ -3015,6 +3031,7 @@ raise(Name, Amount) ->
performed on the original data file. The default is <c>true</c></p>
</item>
<item>
+ <marker id=" dump_log_write_threshold"></marker>
<p><c>-mnesia dump_log_write_threshold Max</c>, where
<c>Max</c> is an integer which specifies the maximum number of writes
allowed to the transaction log before a new dump of the log
@@ -3022,13 +3039,14 @@ raise(Name, Amount) ->
</p>
</item>
<item>
+ <marker id=" dump_log_time_threshold"></marker>
<p><c>-mnesia dump_log_time_threshold Max</c>,
where <c>Max</c> is an integer which
specifies the dump log interval in milliseconds. It defaults
to 3 minutes. If a dump has not been performed within
<c>dump_log_time_threshold</c> milliseconds, then a new dump is
performed regardless of how many writes have been
- performed.
+ performed.
</p>
</item>
<item>
diff --git a/lib/mnesia/src/mnesia.app.src b/lib/mnesia/src/mnesia.app.src
index 3715488ec2..e755864792 100644
--- a/lib/mnesia/src/mnesia.app.src
+++ b/lib/mnesia/src/mnesia.app.src
@@ -47,6 +47,7 @@
mnesia_tm
]},
{applications, [kernel, stdlib]},
- {mod, {mnesia_sup, []}}]}.
+ {mod, {mnesia_sup, []}},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 70466d10d7..b7d80c1370 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -104,7 +104,8 @@
set_master_nodes/1, set_master_nodes/2,
%% Misc admin
- dump_log/0, subscribe/1, unsubscribe/1, report_event/1,
+ dump_log/0, sync_log/0,
+ subscribe/1, unsubscribe/1, report_event/1,
%% Snmp
snmp_open_table/2, snmp_close_table/1,
@@ -1808,7 +1809,7 @@ do_dirty_rpc(Tab, Node, M, F, Args) ->
{badrpc, Reason} ->
timer:sleep(20), %% Do not be too eager, and can't use yield on SMP
%% Sync with mnesia_monitor
- try sys:get_status(mnesia_monitor) catch _:_ -> ok end,
+ _ = try sys:get_status(mnesia_monitor) catch _:_ -> ok end,
case mnesia_controller:call({check_w2r, Node, Tab}) of % Sync
NewNode when NewNode =:= Node ->
ErrorTag = mnesia_lib:dirty_rpc_error_tag(Reason),
@@ -2554,6 +2555,9 @@ set_master_nodes(Tab, Nodes) ->
dump_log() ->
mnesia_controller:sync_dump_log(user).
+sync_log() ->
+ mnesia_monitor:sync_log(latest_log).
+
subscribe(What) ->
mnesia_subscr:subscribe(self(), What).
diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl
index 2855792646..c8010d5466 100644
--- a/lib/mnesia/src/mnesia.hrl
+++ b/lib/mnesia/src/mnesia.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -35,7 +35,7 @@
-define(ets_last(Tab), ets:last(Tab)).
-define(ets_prev(Tab, Key), ets:prev(Tab, Key)).
-define(ets_slot(Tab, Pos), ets:slot(Tab, Pos)).
--define(ets_new_table(Tab, Props), ets:new(Tab, Props)).
+-define(ets_new_table(Tab, Props), _ = ets:new(Tab, Props)).
-define(ets_delete_table(Tab), ets:delete(Tab)).
-define(ets_fixtable(Tab, Bool), ets:fixtable(Tab, Bool)).
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 78f7bfa325..a83e55ac62 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -198,7 +198,8 @@ sync_dump_log(InitBy) ->
call({sync_dump_log, InitBy}).
async_dump_log(InitBy) ->
- ?SERVER_NAME ! {async_dump_log, InitBy}.
+ ?SERVER_NAME ! {async_dump_log, InitBy},
+ ok.
%% Wait for tables to be active
%% If needed, we will wait for Mnesia to start
@@ -293,10 +294,11 @@ update(Fun) ->
mnesia_down(Node) ->
- case cast({mnesia_down, Node}) of
- {error, _} -> mnesia_monitor:mnesia_down(?SERVER_NAME, Node);
- _Pid -> ok
+ case whereis(?SERVER_NAME) of
+ undefined -> mnesia_monitor:mnesia_down(?SERVER_NAME, Node);
+ Pid -> gen_server:cast(Pid, {mnesia_down, Node})
end.
+
wait_for_schema_commit_lock() ->
link(whereis(?SERVER_NAME)),
unsafe_call(wait_for_schema_commit_lock).
@@ -467,7 +469,7 @@ connect_nodes2(Father, Ns, UserFun) ->
process_flag(trap_exit, true),
Res = try_merge_schema(New, [], UserFun),
Msg = {schema_is_merged, [], late_merge, []},
- multicall([node()|Ns], Msg),
+ _ = multicall([node()|Ns], Msg),
After = val({current, db_nodes}),
Father ! {?MODULE, self(), Res, mnesia_lib:intersect(Ns,After)},
unlink(Father),
@@ -548,7 +550,7 @@ schema_is_merged() ->
cast(Msg) ->
case whereis(?SERVER_NAME) of
- undefined ->{error, {node_not_running, node()}};
+ undefined -> ok;
Pid -> gen_server:cast(Pid, Msg)
end.
@@ -1789,7 +1791,7 @@ sync_and_block_table_whereabouts(Tab, ToNode, RemoteS, AccessMode) when Tab /= s
true -> Current -- [ToNode];
false -> Current
end,
- remote_call(ToNode, block_table, [Tab]),
+ _ = remote_call(ToNode, block_table, [Tab]),
[remote_call(Node, add_active_replica, [Tab, ToNode, RemoteS, AccessMode]) ||
Node <- [ToNode | Ns]],
ok.
diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl
index e2a0aa3bda..14665797a0 100644
--- a/lib/mnesia/src/mnesia_dumper.erl
+++ b/lib/mnesia/src/mnesia_dumper.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -85,7 +85,7 @@ adjust_log_writes(DoCast) ->
%% Don't care if we lost a few writes
mnesia_lib:set_counter(trans_log_writes_left, Max),
Diff = Max - Left,
- mnesia_lib:incr_counter(trans_log_writes, Diff),
+ _ = mnesia_lib:incr_counter(trans_log_writes, Diff),
global:del_lock(Token, [node()])
end.
@@ -451,7 +451,8 @@ disc_delete_table(Tab, Storage) ->
Storage == disc_only_copies; Tab == schema ->
mnesia_monitor:unsafe_close_dets(Tab),
Dat = mnesia_lib:tab2dat(Tab),
- file:delete(Dat);
+ file:delete(Dat),
+ ok;
true ->
DclFile = mnesia_lib:tab2dcl(Tab),
case get({?MODULE,Tab}) of
@@ -466,13 +467,14 @@ disc_delete_table(Tab, Storage) ->
file:delete(DcdFile),
ok
end,
- erase({?MODULE, Tab});
+ erase({?MODULE, Tab}),
+ ok;
false ->
- ignore
+ ok
end.
disc_delete_indecies(_Tab, _Cs, Storage) when Storage /= disc_only_copies ->
- ignore;
+ ok;
disc_delete_indecies(Tab, Cs, disc_only_copies) ->
Indecies = Cs#cstruct.index,
mnesia_index:del_transient(Tab, Indecies, disc_only_copies).
@@ -522,10 +524,11 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace,
{disc_copies, ram_copies} when Tab == schema ->
mnesia_lib:set(use_dir, false),
mnesia_monitor:unsafe_close_dets(Tab),
- file:delete(Dat);
+ ok = file:delete(Dat);
{disc_copies, ram_copies} ->
- file:delete(Dcl),
- file:delete(Dcd);
+ _ = file:delete(Dcl),
+ _ = file:delete(Dcd),
+ ok;
{ram_copies, disc_only_copies} ->
ok = ensure_rename(Dmp, Dat),
true = open_files(Tab, disc_only_copies, InPlace, InitBy),
@@ -544,7 +547,8 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace,
startup ->
ignore;
_ ->
- mnesia_controller:get_disc_copy(Tab)
+ mnesia_controller:get_disc_copy(Tab),
+ ok
end,
disc_delete_table(Tab, disc_only_copies);
{disc_copies, disc_only_copies} ->
@@ -553,8 +557,9 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace,
mnesia_schema:ram_delete_table(Tab, FromS),
PosList = Cs#cstruct.index,
mnesia_index:init_indecies(Tab, disc_only_copies, PosList),
- file:delete(Dcl),
- file:delete(Dcd);
+ _ = file:delete(Dcl),
+ _ = file:delete(Dcd),
+ ok;
{disc_only_copies, disc_copies} ->
mnesia_monitor:unsafe_close_dets(Tab),
disc_delete_indecies(Tab, Cs, disc_only_copies),
diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl
index 35fe2d4035..67ec9d7399 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -63,7 +63,7 @@ handle_event(Event, State) ->
%%-----------------------------------------------------------------
handle_info(Msg, State) ->
- handle_any_event(Msg, State),
+ {ok, _} = handle_any_event(Msg, State),
{ok, State}.
%%-----------------------------------------------------------------
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index 54db45e3ba..87cb58dae1 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -52,7 +52,11 @@
val(Var) ->
case ?catch_val(Var) of
- {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ {'EXIT', _ReASoN_} ->
+ case mnesia_lib:other_val(Var) of
+ error -> mnesia_lib:pr_other(Var, _ReASoN_);
+ Val -> Val
+ end;
_VaLuE_ -> _VaLuE_
end.
@@ -229,7 +233,7 @@ del_transient(Tab, Storage) ->
PosList = val({Tab, index}),
del_transient(Tab, PosList, Storage).
-del_transient(_, [], _) -> done;
+del_transient(_, [], _) -> ok;
del_transient(Tab, [Pos | Tail], Storage) ->
delete_transient_index(Tab, Pos, Storage),
del_transient(Tab, Tail, Storage).
@@ -237,7 +241,7 @@ del_transient(Tab, [Pos | Tail], Storage) ->
delete_transient_index(Tab, Pos, disc_only_copies) ->
Tag = {Tab, index, Pos},
mnesia_monitor:unsafe_close_dets(Tag),
- file:delete(tab2filename(Tab, Pos)),
+ _ = file:delete(tab2filename(Tab, Pos)),
del_index_info(Tab, Pos), %% Uses val(..)
mnesia_lib:unset({Tab, {index, Pos}});
@@ -255,7 +259,7 @@ init_disc_index(_Tab, []) ->
init_disc_index(Tab, [Pos | Tail]) when is_integer(Pos) ->
Fn = tab2filename(Tab, Pos),
IxTag = {Tab, index, Pos},
- file:delete(Fn),
+ _ = file:delete(Fn),
Args = [{file, Fn}, {keypos, 1}, {type, bag}],
mnesia_monitor:open_dets(IxTag, Args),
Storage = disc_only_copies,
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index ae6631646c..a32c69c59e 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -115,6 +115,8 @@
mkcore/1,
not_active_here/1,
other_val/2,
+ other_val/1,
+ pr_other/2,
overload_read/0,
overload_read/1,
overload_set/2,
@@ -296,11 +298,7 @@ active_here(Tab) ->
not_active_here(Tab) ->
not active_here(Tab).
-exists(Fname) ->
- case file:open(Fname, [raw,read]) of
- {ok, F} ->file:close(F), true;
- _ -> false
- end.
+exists(Fname) -> filelib:is_regular(Fname).
dir() -> mnesia_monitor:get_env(dir).
@@ -393,16 +391,19 @@ unset(Var) ->
?ets_delete(mnesia_gvar, Var).
other_val(Var, Other) ->
+ case other_val(Var) of
+ error -> pr_other(Var, Other);
+ Val -> Val
+ end.
+
+other_val(Var) ->
case Var of
{_, where_to_read} -> nowhere;
{_, where_to_write} -> [];
{_, active_replicas} -> [];
- _ ->
- pr_other(Var, Other)
+ _ -> error
end.
--spec pr_other(_,_) -> no_return().
-
pr_other(Var, Other) ->
Why =
case is_running() of
@@ -596,7 +597,7 @@ coredump(CrashInfo) ->
Core = mkcore(CrashInfo),
Out = core_file(),
important("Writing Mnesia core to file: ~p...~p~n", [Out, CrashInfo]),
- file:write_file(Out, Core),
+ _ = file:write_file(Out, Core),
Out.
core_file() ->
@@ -620,7 +621,7 @@ mkcore(CrashInfo) ->
Core = [
CrashInfo,
{time, {date(), time()}},
- {self, catch process_info(self())},
+ {self, proc_dbg_info(self())},
{nodes, catch rpc:multicall(Nodes, ?MODULE, get_node_number, [])},
{applications, catch lists:sort(application:loaded_applications())},
{flags, catch init:get_arguments()},
@@ -697,7 +698,7 @@ relatives() ->
Info = fun(Name) ->
case whereis(Name) of
undefined -> false;
- Pid -> {true, {Name, Pid, catch process_info(Pid)}}
+ Pid -> {true, {Name, Pid, proc_dbg_info(Pid)}}
end
end,
lists:zf(Info, mnesia:ms()).
@@ -706,14 +707,14 @@ workers({workers, Loaders, Senders, Dumper}) ->
Info = fun({Pid, {send_table, Tab, _Receiver, _St}}) ->
case Pid of
undefined -> false;
- Pid -> {true, {Pid, Tab, catch process_info(Pid)}}
+ Pid -> {true, {Pid, Tab, proc_dbg_info(Pid)}}
end;
({Pid, What}) when is_pid(Pid) ->
- {true, {Pid, What, catch process_info(Pid)}};
+ {true, {Pid, What, proc_dbg_info(Pid)}};
({Name, Pid}) ->
case Pid of
undefined -> false;
- Pid -> {true, {Name, Pid, catch process_info(Pid)}}
+ Pid -> {true, {Name, Pid, proc_dbg_info(Pid)}}
end
end,
SInfo = lists:zf(Info, Senders),
@@ -727,13 +728,21 @@ locking_procs(LockList) when is_list(LockList) ->
Pid = Tid#tid.pid,
case node(Pid) == node() of
true ->
- {true, {Pid, catch process_info(Pid)}};
+ {true, {Pid, proc_dbg_info(Pid)}};
_ ->
false
end
end,
lists:zf(Info, UT).
+proc_dbg_info(Pid) ->
+ try
+ [process_info(Pid, current_stacktrace)|
+ process_info(Pid)]
+ catch _:R ->
+ [{process_info,crashed,R}]
+ end.
+
view() ->
Bin = mkcore({crashinfo, {"view only~n", []}}),
vcore(Bin).
@@ -806,9 +815,9 @@ vcore(File) ->
vcore_elem({schema_file, {ok, B}}) ->
Fname = "/tmp/schema.DAT",
- file:write_file(Fname, B),
- dets:view(Fname),
- file:delete(Fname);
+ _ = file:write_file(Fname, B),
+ _ = dets:view(Fname),
+ _ = file:delete(Fname);
vcore_elem({logfile, {ok, BinList}}) ->
Fun = fun({F, Info}) ->
@@ -922,7 +931,7 @@ random_time(Retries, _Counter0) ->
case get(random_seed) of
undefined ->
{X, Y, Z} = erlang:now(), %% time()
- random:seed(X, Y, Z),
+ _ = random:seed(X, Y, Z),
Time = Dup + random:uniform(MaxIntv),
%% dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
Time;
@@ -958,20 +967,17 @@ report_system_event({'EXIT', Reason}, Event) ->
unlink(Pid),
%% We get an exit signal if server dies
- receive
- {'EXIT', Pid, _Reason} ->
- {error, {node_not_running, node()}}
- after 0 ->
- gen_event:stop(mnesia_event),
- ok
+ receive {'EXIT', Pid, _Reason} -> ok
+ after 0 -> gen_event:stop(mnesia_event)
end;
Error ->
Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n",
error_logger:format(Msg, [node(), Event, Reason, Error])
- end;
+ end,
+ ok;
report_system_event(_Res, _Event) ->
- ignore.
+ ok.
%% important messages are reported regardless of debug level
important(Format, Args) ->
@@ -1025,8 +1031,8 @@ copy_file(From, To) ->
case file:open(To, [raw, binary, write]) of
{ok, T} ->
Res = copy_file_loop(F, T, 8000),
- file:close(F),
- file:close(T),
+ ok = file:close(F),
+ ok = file:close(T),
Res;
{error, Reason} ->
{error, Reason}
@@ -1038,7 +1044,7 @@ copy_file(From, To) ->
copy_file_loop(F, T, ChunkSize) ->
case file:read(F, ChunkSize) of
{ok, Bin} ->
- file:write(T, Bin),
+ ok = file:write(T, Bin),
copy_file_loop(F, T, ChunkSize);
eof ->
ok;
@@ -1205,7 +1211,7 @@ dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) ->
{keypos, 2}, {repair, Rep}]) of
{ok, Tabname} ->
Res = dets:to_ets(Tabname, Tab),
- Close(Tabname),
+ ok = Close(Tabname),
trav_ret(Res, Tab);
Other ->
Other
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index c4fe370ec1..81b435c6dc 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -103,7 +103,8 @@ val(Var) ->
end.
reply(From, R) ->
- From ! {?MODULE, node(), R}.
+ From ! {?MODULE, node(), R},
+ true. %% Quiets dialyzer
l_request(Node, X, Store) ->
{?MODULE, Node} ! {self(), X},
@@ -269,7 +270,8 @@ try_sticky_lock(Tid, Op, Pid, {Tab, _} = Oid) ->
try_lock(Tid, Op, Pid, Oid);
[{_,N}] ->
Req = {Pid, {Op, Tid, Oid}},
- Pid ! {?MODULE, node(), {switch, N, Req}}
+ Pid ! {?MODULE, node(), {switch, N, Req}},
+ true
end.
try_lock(Tid, read_write, Pid, Oid) ->
diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl
index 18303869ed..d2fd04a60b 100644
--- a/lib/mnesia/src/mnesia_log.erl
+++ b/lib/mnesia/src/mnesia_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -393,13 +393,15 @@ unsafe_close_log(Log) ->
purge_some_logs() ->
mnesia_monitor:unsafe_close_log(latest_log),
- file:delete(latest_log_file()),
- file:delete(decision_tab_file()).
+ _ = file:delete(latest_log_file()),
+ _ = file:delete(decision_tab_file()),
+ ok.
purge_all_logs() ->
- file:delete(previous_log_file()),
- file:delete(latest_log_file()),
- file:delete(decision_tab_file()).
+ _ = file:delete(previous_log_file()),
+ _ = file:delete(latest_log_file()),
+ _ = file:delete(decision_tab_file()),
+ ok.
%% Prepare dump by renaming the open logfile if possible
%% Returns a tuple on the following format: {Res, OpenLog}
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index c7b905a1bf..6fc1a394a6 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -44,6 +44,7 @@
set_env/2,
start/0,
start_proc/4,
+ sync_log/1,
terminate_proc/3,
unsafe_close_dets/1,
unsafe_close_log/1,
@@ -118,6 +119,9 @@ open_log(Args) ->
reopen_log(Name, Fname, Head) ->
unsafe_call({reopen_log, Name, Fname, Head}).
+sync_log(Name) ->
+ unsafe_call({sync_log, Name}).
+
close_log(Name) ->
unsafe_call({close_log, Name}).
@@ -202,7 +206,7 @@ needs_protocol_conversion(Node) ->
cast(Msg) ->
case whereis(?MODULE) of
- undefined -> ignore;
+ undefined -> ok;
Pid -> gen_server:cast(Pid, Msg)
end.
@@ -382,6 +386,9 @@ handle_call({reopen_log, Name, Fname, Head}, _From, State) ->
{noreply, State}
end;
+handle_call({sync_log, Name}, _From, State) ->
+ {reply, disk_log:sync(Name), State};
+
handle_call({close_log, Name}, _From, State) ->
case disk_log:close(Name) of
ok ->
@@ -395,7 +402,7 @@ handle_call({close_log, Name}, _From, State) ->
end;
handle_call({unsafe_close_log, Name}, _From, State) ->
- disk_log:close(Name),
+ _ = disk_log:close(Name),
{reply, ok, State};
handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State)
@@ -439,7 +446,7 @@ handle_call({negotiate_protocol, Nodes}, From, State) ->
end;
handle_call(init, _From, State) ->
- net_kernel:monitor_nodes(true),
+ _ = net_kernel:monitor_nodes(true),
EarlyNodes = State#state.early_connects,
State2 = State#state{tm_started = true},
{reply, EarlyNodes, State2};
diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl
index 7aa03bda37..b6492707e2 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -178,7 +178,11 @@ log_decision(D) ->
val(Var) ->
case ?catch_val(Var) of
- {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ {'EXIT', Reason} ->
+ case mnesia_lib:other_val(Var) of
+ error -> mnesia_lib:pr_other(Var, Reason);
+ Val -> Val
+ end;
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia_snmp_hook.erl b/lib/mnesia/src/mnesia_snmp_hook.erl
index 893b39f3c0..256f83b029 100644
--- a/lib/mnesia/src/mnesia_snmp_hook.erl
+++ b/lib/mnesia/src/mnesia_snmp_hook.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -32,7 +32,11 @@
val(Var) ->
case ?catch_val(Var) of
- {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ {'EXIT', _ReASoN_} ->
+ case mnesia_lib:other_val(Var) of
+ error -> mnesia_lib:pr_other(Var, _ReASoN_);
+ Val -> Val
+ end;
_VaLuE_ -> _VaLuE_
end.
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index 17af0cad44..af658150da 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -183,7 +183,8 @@ mnesia_down(Node) ->
undefined ->
mnesia_monitor:mnesia_down(?MODULE, Node);
Pid ->
- Pid ! {mnesia_down, Node}
+ Pid ! {mnesia_down, Node},
+ ok
end.
prepare_checkpoint(Nodes, Cp) ->
diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl
index e0004ecb51..921ebb71e9 100644
--- a/lib/mnesia/test/mnesia_SUITE.erl
+++ b/lib/mnesia/test/mnesia_SUITE.erl
@@ -21,6 +21,7 @@
-module(mnesia_SUITE).
-author('[email protected]').
-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
-include("mnesia_test_lib.hrl").
init_per_testcase(Func, Conf) ->
@@ -50,7 +51,7 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
%% and do not involve the normal test machinery.
all() ->
- [{group, light}, {group, medium}, {group, heavy},
+ [app, appup, {group, light}, {group, medium}, {group, heavy},
clean_up_suite].
groups() ->
@@ -144,6 +145,18 @@ silly() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Test structure of the mnesia application resource file
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(mnesia).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Test that all required versions have appup directives
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(mnesia).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where"
"started during the tests"];
clean_up_suite(suite) ->
diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl
index 6baf86a4a5..c495bce63f 100644
--- a/lib/mnesia/test/mnesia_config_test.erl
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -639,10 +639,10 @@ send_compressed(Config) ->
end,
?match([], mnesia_test_lib:kill_mnesia([N2])),
-
+ sys:get_status(mnesia_monitor), %% sync N1
?match([], mnesia_test_lib:kill_mnesia([N1])),
?match(ok, mnesia:start([{send_compressed, 9}])),
- ?match(ok, mnesia:wait_for_tables([t0,t1,t2], 5000)),
+ ?match(ok, mnesia:wait_for_tables([t0,t1,t2], 25000)),
?match({atomic, ok}, mnesia:transaction(Create, [t0])),
?match({atomic, ok}, mnesia:transaction(Create, [t1])),
@@ -1158,6 +1158,7 @@ dynamic_basic(Config) when is_list(Config) ->
%%% SYNC!!!
timer:sleep(1000),
+ sys:get_status(mnesia_monitor),
?match([N3,N1], sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
?match([N3,N1], sort(rpc:call(N3, mnesia, system_info, [running_db_nodes]))),
diff --git a/lib/mnesia/test/mnesia_durability_test.erl b/lib/mnesia/test/mnesia_durability_test.erl
index 4434abaa1e..366fda7044 100644
--- a/lib/mnesia/test/mnesia_durability_test.erl
+++ b/lib/mnesia/test/mnesia_durability_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -48,19 +48,19 @@ groups() ->
load_directly_when_all_are_ram_copiesB,
{group, late_load_when_all_are_ram_copies_on_ram_nodes},
load_when_last_replica_becomes_available,
- load_when_we_have_down_from_all_other_replica_nodes,
+ load_when_down_from_all_other_replica_nodes,
late_load_transforms_into_disc_load,
late_load_leads_to_hanging,
force_load_when_nobody_intents_to_load,
force_load_when_someone_has_decided_to_load,
- force_load_when_someone_else_already_has_loaded,
+ force_load_when_someone_else_has_loaded,
force_load_when_we_has_loaded,
force_load_on_a_non_local_table,
force_load_when_the_table_does_not_exist,
{group, load_tables_with_master_tables}]},
{late_load_when_all_are_ram_copies_on_ram_nodes, [],
- [late_load_when_all_are_ram_copies_on_ram_nodes1,
- late_load_when_all_are_ram_copies_on_ram_nodes2]},
+ [late_load_all_ram_cs_ram_nodes1,
+ late_load_all_ram_cs_ram_nodes2]},
{load_tables_with_master_tables, [],
[master_nodes, starting_master_nodes,
master_on_non_local_tables,
@@ -292,8 +292,8 @@ load_directly_when_all_are_ram_copiesB(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-late_load_when_all_are_ram_copies_on_ram_nodes1(suite) -> [];
-late_load_when_all_are_ram_copies_on_ram_nodes1(Config) when is_list(Config) ->
+late_load_all_ram_cs_ram_nodes1(suite) -> [];
+late_load_all_ram_cs_ram_nodes1(Config) when is_list(Config) ->
[N1, N2] = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
delete_schema,
{reload_appls, [mnesia]}],
@@ -303,8 +303,8 @@ late_load_when_all_are_ram_copies_on_ram_nodes1(Config) when is_list(Config) ->
2, Config, ?FILE, ?LINE),
Res.
-late_load_when_all_are_ram_copies_on_ram_nodes2(suite) -> [];
-late_load_when_all_are_ram_copies_on_ram_nodes2(Config) when is_list(Config) ->
+late_load_all_ram_cs_ram_nodes2(suite) -> [];
+late_load_all_ram_cs_ram_nodes2(Config) when is_list(Config) ->
[N1, N2, N3] = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
delete_schema,
{reload_appls, [mnesia]}],
@@ -439,13 +439,13 @@ load_when_last_replica_becomes_available(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-load_when_we_have_down_from_all_other_replica_nodes(doc) ->
+load_when_down_from_all_other_replica_nodes(doc) ->
["The table can be loaded if this node was the last one surviving. ",
"Check this by having N1, N2, N3 and a table replicated on all those ",
"nodes. Then kill them in the N1, N2, N3 order. Then start N3 and ",
"verify that the table is available with correct contents."];
-load_when_we_have_down_from_all_other_replica_nodes(suite) -> [];
-load_when_we_have_down_from_all_other_replica_nodes(Config) when is_list(Config) ->
+load_when_down_from_all_other_replica_nodes(suite) -> [];
+load_when_down_from_all_other_replica_nodes(Config) when is_list(Config) ->
[N1, N2, N3] = Nodes = ?acquire_nodes(3, Config),
?match({atomic,ok},
mnesia:create_table(test_rec,
@@ -773,14 +773,14 @@ wait_for_signal() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-force_load_when_someone_else_already_has_loaded(doc) ->
+force_load_when_someone_else_has_loaded(doc) ->
["Normal case. Do a force load when somebody else has loaded the table. ",
"Start N1, N2, kill in N1, N2 order. Start N2 load the table, start N1 ",
"force load. Did it work? (i.e: did N1 load the table from N2 as that",
"one is the latest version and it is available on N2)"];
-force_load_when_someone_else_already_has_loaded(suite) -> [];
-force_load_when_someone_else_already_has_loaded(Config) when is_list(Config) ->
+force_load_when_someone_else_has_loaded(suite) -> [];
+force_load_when_someone_else_has_loaded(Config) when is_list(Config) ->
[N1, N2] = Nodes = ?acquire_nodes(2, Config),
Table = test_rec,
Trec1 = #test_rec{key=1,val=111},
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
index 91820238e5..2d1623b6ca 100644
--- a/lib/mnesia/test/mnesia_evil_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -671,13 +671,16 @@ add_copy_when_going_down(Config) ->
?acquire_nodes(2, Config ++ [{tc_timeout, timer:minutes(2)}]),
?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, [Node1]}])),
%% Grab a write lock
+ Tester = self(),
WriteAndWait = fun() ->
mnesia:write({a,1,1}),
- receive continue -> ok
+ Tester ! {self(), got_lock},
+ receive continue -> ok
end
end,
- _Lock = spawn(fun() -> mnesia:transaction(WriteAndWait) end),
- Tester = self(),
+ Locker = spawn(fun() -> mnesia:transaction(WriteAndWait) end),
+ receive {Locker, got_lock} -> ok end,
+
spawn_link(fun() -> Res = rpc:call(Node2, mnesia, add_table_copy,
[a, Node2, ram_copies]),
Tester ! {test, Res}
diff --git a/lib/mnesia/test/mnesia_frag_test.erl b/lib/mnesia/test/mnesia_frag_test.erl
index d3f6762af7..6695fbc880 100644
--- a/lib/mnesia/test/mnesia_frag_test.erl
+++ b/lib/mnesia/test/mnesia_frag_test.erl
@@ -461,7 +461,7 @@ nice_iter_access(Tab, FragNames, RawRead) ->
ExpectedLast = lists:last(Keys),
?match(ExpectedLast, mnesia:last(Tab)),
- ExpectedAllPrev = ['$end_of_table' | lists:reverse(tl(lists:reverse(Keys)))],
+ ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)],
?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
@@ -477,7 +477,7 @@ evil_iter_access(Tab, FragNames, RawRead) ->
ExpectedLast = lists:last(Keys),
?match(ExpectedLast, mnesia:last(Tab)),
- ExpectedAllPrev = ['$end_of_table' | lists:reverse(tl(lists:reverse(Keys)))],
+ ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)],
?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
diff --git a/lib/mnesia/test/mnesia_nice_coverage_test.erl b/lib/mnesia/test/mnesia_nice_coverage_test.erl
index 78eab67b11..4b28ac634f 100644
--- a/lib/mnesia/test/mnesia_nice_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_nice_coverage_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -189,6 +189,7 @@ adm(Attrs, Node1, Node2) ->
?match({atomic, ok}, mnesia:move_table_copy(nice_tab, Node2, Node1)),
?match(yes, mnesia:force_load_table(nice_counter_tab)),
+ ?match(ok, mnesia:sync_log()),
?match(dumped, mnesia:dump_log()),
ok.
diff --git a/lib/mnesia/test/mnesia_schema_recovery_test.erl b/lib/mnesia/test/mnesia_schema_recovery_test.erl
index 0fe26efd0b..2301b291c2 100644
--- a/lib/mnesia/test/mnesia_schema_recovery_test.erl
+++ b/lib/mnesia/test/mnesia_schema_recovery_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -49,66 +49,69 @@ groups() ->
[{interrupted_before_log_dump, [],
[interrupted_before_create_ram,
interrupted_before_create_disc,
- interrupted_before_create_disc_only,
+ interrupted_before_create_do,
interrupted_before_create_nostore,
interrupted_before_delete_ram,
interrupted_before_delete_disc,
- interrupted_before_delete_disc_only,
- interrupted_before_add_ram, interrupted_before_add_disc,
- interrupted_before_add_disc_only,
+ interrupted_before_delete_do,
+ interrupted_before_add_ram,
+ interrupted_before_add_disc,
+ interrupted_before_add_do,
interrupted_before_add_kill_copier,
interrupted_before_move_ram,
interrupted_before_move_disc,
- interrupted_before_move_disc_only,
+ interrupted_before_move_do,
interrupted_before_move_kill_copier,
interrupted_before_delcopy_ram,
interrupted_before_delcopy_disc,
- interrupted_before_delcopy_disc_only,
+ interrupted_before_delcopy_do,
interrupted_before_delcopy_kill_copier,
interrupted_before_addindex_ram,
interrupted_before_addindex_disc,
- interrupted_before_addindex_disc_only,
+ interrupted_before_addindex_do,
interrupted_before_delindex_ram,
interrupted_before_delindex_disc,
- interrupted_before_delindex_disc_only,
+ interrupted_before_delindex_do,
interrupted_before_change_type_ram2disc,
- interrupted_before_change_type_ram2disc_only,
+ interrupted_before_change_type_ram2do,
interrupted_before_change_type_disc2ram,
- interrupted_before_change_type_disc2disc_only,
- interrupted_before_change_type_disc_only2ram,
- interrupted_before_change_type_disc_only2disc,
+ interrupted_before_change_type_disc2do,
+ interrupted_before_change_type_do2ram,
+ interrupted_before_change_type_do2disc,
interrupted_before_change_type_other_node,
interrupted_before_change_schema_type]},
{interrupted_after_log_dump, [],
[interrupted_after_create_ram,
interrupted_after_create_disc,
- interrupted_after_create_disc_only,
+ interrupted_after_create_do,
interrupted_after_create_nostore,
interrupted_after_delete_ram,
interrupted_after_delete_disc,
- interrupted_after_delete_disc_only,
- interrupted_after_add_ram, interrupted_after_add_disc,
- interrupted_after_add_disc_only,
+ interrupted_after_delete_do,
+ interrupted_after_add_ram,
+ interrupted_after_add_disc,
+ interrupted_after_add_do,
interrupted_after_add_kill_copier,
- interrupted_after_move_ram, interrupted_after_move_disc,
- interrupted_after_move_disc_only,
+ interrupted_after_move_ram,
+ interrupted_after_move_disc,
+ interrupted_after_move_do,
interrupted_after_move_kill_copier,
interrupted_after_delcopy_ram,
interrupted_after_delcopy_disc,
- interrupted_after_delcopy_disc_only,
+ interrupted_after_delcopy_do,
interrupted_after_delcopy_kill_copier,
interrupted_after_addindex_ram,
interrupted_after_addindex_disc,
- interrupted_after_addindex_disc_only,
+ interrupted_after_addindex_do,
interrupted_after_delindex_ram,
interrupted_after_delindex_disc,
- interrupted_after_delindex_disc_only,
+ interrupted_after_delindex_do,
interrupted_after_change_type_ram2disc,
- interrupted_after_change_type_ram2disc_only,
+ interrupted_after_change_type_ram2do,
interrupted_after_change_type_disc2ram,
- interrupted_after_change_type_disc2disc_only,
- interrupted_after_change_type_disc_only2ram,
- interrupted_after_change_type_disc_only2disc,
+ interrupted_after_change_type_disc2do,
+ interrupted_after_change_type_do2ram,
+ interrupted_after_change_type_do2disc,
interrupted_after_change_type_other_node,
interrupted_after_change_schema_type]}].
@@ -128,8 +131,8 @@ interrupted_before_create_disc(Config) when is_list(Config) ->
KillAt = {mnesia_dumper, dump_schema_op},
interrupted_create(Config, disc_copies, all, KillAt).
-interrupted_before_create_disc_only(suite) -> [];
-interrupted_before_create_disc_only(Config) when is_list(Config) ->
+interrupted_before_create_do(suite) -> [];
+interrupted_before_create_do(Config) when is_list(Config) ->
KillAt = {mnesia_dumper, dump_schema_op},
interrupted_create(Config, disc_only_copies, all, KillAt).
@@ -148,8 +151,8 @@ interrupted_after_create_disc(Config) when is_list(Config) ->
KillAt = {mnesia_dumper, post_dump},
interrupted_create(Config, disc_copies, all, KillAt).
-interrupted_after_create_disc_only(suite) -> [];
-interrupted_after_create_disc_only(Config) when is_list(Config) ->
+interrupted_after_create_do(suite) -> [];
+interrupted_after_create_do(Config) when is_list(Config) ->
KillAt = {mnesia_dumper, post_dump},
interrupted_create(Config, disc_only_copies, all, KillAt).
@@ -204,8 +207,8 @@ interrupted_before_delete_disc(suite) -> [];
interrupted_before_delete_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_delete(Config, disc_copies, Debug_Point).
-interrupted_before_delete_disc_only(suite) -> [];
-interrupted_before_delete_disc_only(Config) when is_list(Config) ->
+interrupted_before_delete_do(suite) -> [];
+interrupted_before_delete_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_delete(Config, disc_only_copies, Debug_Point).
@@ -217,8 +220,8 @@ interrupted_after_delete_disc(suite) -> [];
interrupted_after_delete_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_delete(Config, disc_copies, Debug_Point).
-interrupted_after_delete_disc_only(suite) -> [];
-interrupted_after_delete_disc_only(Config) when is_list(Config) ->
+interrupted_after_delete_do(suite) -> [];
+interrupted_after_delete_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_delete(Config, disc_only_copies, Debug_Point).
@@ -249,8 +252,8 @@ interrupted_before_add_disc(suite) -> [];
interrupted_before_add_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_add(Config, disc_copies, kill_reciever, Debug_Point).
-interrupted_before_add_disc_only(suite) -> [];
-interrupted_before_add_disc_only(Config) when is_list(Config) ->
+interrupted_before_add_do(suite) -> [];
+interrupted_before_add_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point).
interrupted_before_add_kill_copier(suite) -> [];
@@ -266,8 +269,8 @@ interrupted_after_add_disc(suite) -> [];
interrupted_after_add_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_add(Config, disc_copies, kill_reciever, Debug_Point).
-interrupted_after_add_disc_only(suite) -> [];
-interrupted_after_add_disc_only(Config) when is_list(Config) ->
+interrupted_after_add_do(suite) -> [];
+interrupted_after_add_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point).
interrupted_after_add_kill_copier(suite) -> [];
@@ -327,8 +330,8 @@ interrupted_before_move_disc(suite) -> [];
interrupted_before_move_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_move(Config, disc_copies, kill_reciever, Debug_Point).
-interrupted_before_move_disc_only(suite) -> [];
-interrupted_before_move_disc_only(Config) when is_list(Config) ->
+interrupted_before_move_do(suite) -> [];
+interrupted_before_move_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point).
interrupted_before_move_kill_copier(suite) -> [];
@@ -344,8 +347,8 @@ interrupted_after_move_disc(suite) -> [];
interrupted_after_move_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_move(Config, disc_copies, kill_reciever, Debug_Point).
-interrupted_after_move_disc_only(suite) -> [];
-interrupted_after_move_disc_only(Config) when is_list(Config) ->
+interrupted_after_move_do(suite) -> [];
+interrupted_after_move_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point).
interrupted_after_move_kill_copier(suite) -> [];
@@ -408,8 +411,8 @@ interrupted_before_delcopy_disc(suite) -> [];
interrupted_before_delcopy_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point).
-interrupted_before_delcopy_disc_only(suite) -> [];
-interrupted_before_delcopy_disc_only(Config) when is_list(Config) ->
+interrupted_before_delcopy_do(suite) -> [];
+interrupted_before_delcopy_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point).
interrupted_before_delcopy_kill_copier(suite) -> [];
@@ -425,8 +428,8 @@ interrupted_after_delcopy_disc(suite) -> [];
interrupted_after_delcopy_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point).
-interrupted_after_delcopy_disc_only(suite) -> [];
-interrupted_after_delcopy_disc_only(Config) when is_list(Config) ->
+interrupted_after_delcopy_do(suite) -> [];
+interrupted_after_delcopy_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point).
interrupted_after_delcopy_kill_copier(suite) -> [];
@@ -487,8 +490,8 @@ interrupted_before_addindex_disc(suite) -> [];
interrupted_before_addindex_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_addindex(Config, disc_copies, Debug_Point).
-interrupted_before_addindex_disc_only(suite) -> [];
-interrupted_before_addindex_disc_only(Config) when is_list(Config) ->
+interrupted_before_addindex_do(suite) -> [];
+interrupted_before_addindex_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_addindex(Config, disc_only_copies, Debug_Point).
@@ -500,8 +503,8 @@ interrupted_after_addindex_disc(suite) -> [];
interrupted_after_addindex_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_addindex(Config, disc_copies, Debug_Point).
-interrupted_after_addindex_disc_only(suite) -> [];
-interrupted_after_addindex_disc_only(Config) when is_list(Config) ->
+interrupted_after_addindex_do(suite) -> [];
+interrupted_after_addindex_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_addindex(Config, disc_only_copies, Debug_Point).
@@ -555,8 +558,8 @@ interrupted_before_delindex_disc(suite) -> [];
interrupted_before_delindex_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_delindex(Config, disc_copies, Debug_Point).
-interrupted_before_delindex_disc_only(suite) -> [];
-interrupted_before_delindex_disc_only(Config) when is_list(Config) ->
+interrupted_before_delindex_do(suite) -> [];
+interrupted_before_delindex_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_delindex(Config, disc_only_copies, Debug_Point).
@@ -568,8 +571,8 @@ interrupted_after_delindex_disc(suite) -> [];
interrupted_after_delindex_disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_delindex(Config, disc_copies, Debug_Point).
-interrupted_after_delindex_disc_only(suite) -> [];
-interrupted_after_delindex_disc_only(Config) when is_list(Config) ->
+interrupted_after_delindex_do(suite) -> [];
+interrupted_after_delindex_do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_delindex(Config, disc_only_copies, Debug_Point).
@@ -613,24 +616,24 @@ interrupted_before_change_type_ram2disc(suite) -> [];
interrupted_before_change_type_ram2disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point).
-interrupted_before_change_type_ram2disc_only(suite) -> [];
-interrupted_before_change_type_ram2disc_only(Config) when is_list(Config) ->
+interrupted_before_change_type_ram2do(suite) -> [];
+interrupted_before_change_type_ram2do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point).
interrupted_before_change_type_disc2ram(suite) -> [];
interrupted_before_change_type_disc2ram(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point).
-interrupted_before_change_type_disc2disc_only(suite) -> [];
-interrupted_before_change_type_disc2disc_only(Config) when is_list(Config) ->
+interrupted_before_change_type_disc2do(suite) -> [];
+interrupted_before_change_type_disc2do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point).
-interrupted_before_change_type_disc_only2ram(suite) -> [];
-interrupted_before_change_type_disc_only2ram(Config) when is_list(Config) ->
+interrupted_before_change_type_do2ram(suite) -> [];
+interrupted_before_change_type_do2ram(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point).
-interrupted_before_change_type_disc_only2disc(suite) -> [];
-interrupted_before_change_type_disc_only2disc(Config) when is_list(Config) ->
+interrupted_before_change_type_do2disc(suite) -> [];
+interrupted_before_change_type_do2disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, dump_schema_op},
interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point).
interrupted_before_change_type_other_node(suite) -> [];
@@ -642,24 +645,24 @@ interrupted_after_change_type_ram2disc(suite) -> [];
interrupted_after_change_type_ram2disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point).
-interrupted_after_change_type_ram2disc_only(suite) -> [];
-interrupted_after_change_type_ram2disc_only(Config) when is_list(Config) ->
+interrupted_after_change_type_ram2do(suite) -> [];
+interrupted_after_change_type_ram2do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point).
interrupted_after_change_type_disc2ram(suite) -> [];
interrupted_after_change_type_disc2ram(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point).
-interrupted_after_change_type_disc2disc_only(suite) -> [];
-interrupted_after_change_type_disc2disc_only(Config) when is_list(Config) ->
+interrupted_after_change_type_disc2do(suite) -> [];
+interrupted_after_change_type_disc2do(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point).
-interrupted_after_change_type_disc_only2ram(suite) -> [];
-interrupted_after_change_type_disc_only2ram(Config) when is_list(Config) ->
+interrupted_after_change_type_do2ram(suite) -> [];
+interrupted_after_change_type_do2ram(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point).
-interrupted_after_change_type_disc_only2disc(suite) -> [];
-interrupted_after_change_type_disc_only2disc(Config) when is_list(Config) ->
+interrupted_after_change_type_do2disc(suite) -> [];
+interrupted_after_change_type_do2disc(Config) when is_list(Config) ->
Debug_Point = {mnesia_dumper, post_dump},
interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point).
interrupted_after_change_type_other_node(suite) -> [];
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index 064ba43791..c596f98c81 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.11
+MNESIA_VSN = 4.12
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 0266047ee3..d5fbceff1e 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -21,26 +21,26 @@
detail_pages/0]).
%% Callbacks for cdv_detail_wx
-get_details({_, {T,Key}}) ->
+get_details({Type, {T,Key}}) ->
[{Key,Term}] = ets:lookup(T,Key),
- {ok,{"Expanded Binary", Term, []}};
+ {ok,{"Expanded Binary", {Type, Term}, []}};
get_details({cdv, Id}) ->
{ok,Bin} = crashdump_viewer:expand_binary(Id),
- {ok,{"Expanded Binary", Bin, []}}.
+ {ok,{"Expanded Binary", {cvd, Bin}, []}}.
detail_pages() ->
[{"Binary", fun init_bin_page/2}].
-init_bin_page(Parent,Bin) ->
+init_bin_page(Parent,{Type,Bin}) ->
cdv_multi_wx:start_link(
Parent,
- [{"Format \~p",cdv_html_wx,format_bin_fun("~p",Bin)},
- {"Format \~tp",cdv_html_wx,format_bin_fun("~tp",Bin)},
- {"Format \~w",cdv_html_wx,format_bin_fun("~w",Bin)},
- {"Format \~s",cdv_html_wx,format_bin_fun("~s",Bin)},
- {"Format \~ts",cdv_html_wx,format_bin_fun("~ts",Bin)},
- {"Hex",cdv_html_wx,hex_binary_fun(Bin)},
- {"Term",cdv_html_wx,binary_to_term_fun(Bin)}]).
+ [{"Format \~p",cdv_html_wx,{Type,format_bin_fun("~p",Bin)}},
+ {"Format \~tp",cdv_html_wx,{Type,format_bin_fun("~tp",Bin)}},
+ {"Format \~w",cdv_html_wx,{Type,format_bin_fun("~w",Bin)}},
+ {"Format \~s",cdv_html_wx,{Type,format_bin_fun("~s",Bin)}},
+ {"Format \~ts",cdv_html_wx,{Type,format_bin_fun("~ts",Bin)}},
+ {"Hex",cdv_html_wx,{Type,hex_binary_fun(Bin)}},
+ {"Term",cdv_html_wx,{Type,binary_to_term_fun(Bin)}}]).
format_bin_fun(Format,Bin) ->
fun() ->
diff --git a/lib/observer/src/cdv_dist_cb.erl b/lib/observer/src/cdv_dist_cb.erl
index 3860324d6f..f7e6c9aded 100644
--- a/lib/observer/src/cdv_dist_cb.erl
+++ b/lib/observer/src/cdv_dist_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -22,7 +22,8 @@
get_info/1,
get_detail_cols/1,
get_details/1,
- detail_pages/0]).
+ detail_pages/0,
+ format/1]).
-include_lib("wx/include/wx.hrl").
-include("crashdump_viewer.hrl").
@@ -75,6 +76,11 @@ init_gen_page(Parent, Info) ->
Fields = info_fields(),
cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
+format({creations,Creations}) ->
+ string:join([integer_to_list(C) || C <- Creations],",");
+format(D) ->
+ D.
+
%%%-----------------------------------------------------------------
%%% Internal
info_fields() ->
@@ -83,7 +89,7 @@ info_fields() ->
{"Type", conn_type},
{"Channel", channel},
{"Controller", {click,controller}},
- {"Creation", creation},
+ {"Creation", {{format,fun format/1},creation}},
{"Extra Info", error}]},
{scroll_boxes,
[{"Remote Links",1,{click,remote_links}},
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index fe77a41f59..b79c647f63 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -30,6 +30,7 @@
%% Records
-record(state,
{panel,
+ app, %% which tool is the user
expand_table,
expand_wins=[]}).
@@ -38,16 +39,21 @@ start_link(ParentWin, Info) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([ParentWin, Fun]) when is_function(Fun) ->
- init([ParentWin, Fun()]);
+init([ParentWin, {App, Fun}]) when is_function(Fun) ->
+ init([ParentWin, {App, Fun()}]);
init([ParentWin, {expand,HtmlText,Tab}]) ->
- HtmlWin = observer_lib:html_window(ParentWin),
- wxHtmlWindow:setPage(HtmlWin,HtmlText),
- {HtmlWin, #state{panel=HtmlWin,expand_table=Tab}};
+ init(ParentWin, HtmlText, Tab, cdv);
+init([ParentWin, {App, {expand,HtmlText,Tab}}]) ->
+ init(ParentWin, HtmlText, Tab, App);
+init([ParentWin, {App,HtmlText}]) ->
+ init(ParentWin, HtmlText, undefined, App);
init([ParentWin, HtmlText]) ->
+ init(ParentWin, HtmlText, undefined, cdv).
+
+init(ParentWin, HtmlText, Tab, App) ->
HtmlWin = observer_lib:html_window(ParentWin),
wxHtmlWindow:setPage(HtmlWin,HtmlText),
- {HtmlWin, #state{panel=HtmlWin}}.
+ {HtmlWin, #state{panel=HtmlWin,expand_table=Tab,app=App}}.
%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -78,7 +84,7 @@ handle_cast(Msg, State) ->
handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
linkInfo=#wxHtmlLinkInfo{href=Target}}},
- #state{expand_table=Tab}=State) ->
+ #state{expand_table=Tab, app=App}=State) ->
NewState=
case Target of
"#Binary?" ++ BinSpec ->
@@ -102,10 +108,12 @@ handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
list_to_integer(Key2),
list_to_integer(Key3)}}},
expand(Id,cdv_term_cb,State);
+ _ when App =:= obs ->
+ observer ! {open_link, Target};
_ ->
cdv_virtual_list_wx:start_detail_win(Target),
State
- end,
+ end,
{noreply, NewState};
handle_event(Event, State) ->
diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl
index 6426cc0803..4451045012 100644
--- a/lib/observer/src/cdv_term_cb.erl
+++ b/lib/observer/src/cdv_term_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -21,23 +21,23 @@
detail_pages/0]).
%% Callbacks for cdv_detail_wx
-get_details({_, {T,Key}}) ->
+get_details({Type, {T,Key}}) ->
[{Key,Term}] = ets:lookup(T,Key),
- {ok,{"Expanded Term", [Term, T], []}}.
+ {ok,{"Expanded Term", {Type,[Term, T]}, []}}.
detail_pages() ->
[{"Term", fun init_term_page/2}].
-init_term_page(ParentWin, [Term, Tab]) ->
+init_term_page(ParentWin, {Type, [Term, Tab]}) ->
Expanded = expand(Term, true),
BinSaved = expand(Term, Tab),
cdv_multi_wx:start_link(
ParentWin,
- [{"Format \~p",cdv_html_wx,format_term_fun("~p",BinSaved,Tab)},
- {"Format \~tp",cdv_html_wx,format_term_fun("~tp",BinSaved,Tab)},
- {"Format \~w",cdv_html_wx,format_term_fun("~w",BinSaved,Tab)},
- {"Format \~s",cdv_html_wx,format_term_fun("~s",Expanded,Tab)},
- {"Format \~ts",cdv_html_wx,format_term_fun("~ts",Expanded,Tab)}]).
+ [{"Format \~p",cdv_html_wx,{Type, format_term_fun("~p",BinSaved,Tab)}},
+ {"Format \~tp",cdv_html_wx,{Type,format_term_fun("~tp",BinSaved,Tab)}},
+ {"Format \~w",cdv_html_wx,{Type,format_term_fun("~w",BinSaved,Tab)}},
+ {"Format \~s",cdv_html_wx,{Type,format_term_fun("~s",Expanded,Tab)}},
+ {"Format \~ts",cdv_html_wx,{Type,format_term_fun("~ts",Expanded,Tab)}}]).
format_term_fun(Format,Term,Tab) ->
fun() ->
@@ -56,8 +56,9 @@ expand(['#CDVBin',Offset,Size,Pos], true) ->
{ok,Bin} = crashdump_viewer:expand_binary({Offset,Size,Pos}),
Bin;
expand(Bin, Tab) when is_binary(Bin), not is_boolean(Tab) ->
- <<Preview:80, _/binary>> = Bin,
Size = byte_size(Bin),
+ PrevSize = min(Size, 10) * 8,
+ <<Preview:PrevSize, _/binary>> = Bin,
Hash = erlang:phash2(Bin),
Key = {Preview, Size, Hash},
ets:insert(Tab, {Key,Bin}),
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index a17efbccb0..a08d27d070 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -1576,7 +1576,13 @@ get_nodeinfo(Fd,Nod) ->
"Controller" ->
get_nodeinfo(Fd,Nod#nod{controller=val(Fd)});
"Creation" ->
- get_nodeinfo(Fd,Nod#nod{creation=list_to_integer(val(Fd))});
+ %% Throwing away elements like "(refc=1)", which might be
+ %% printed from a debug compiled emulator.
+ Creations = lists:flatmap(fun(C) -> try [list_to_integer(C)]
+ catch error:badarg -> []
+ end
+ end, string:tokens(val(Fd)," ")),
+ get_nodeinfo(Fd,Nod#nod{creation={creations,Creations}});
"Remote link" ->
Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>"
{Local,Remote} = split(Procs),
@@ -2559,11 +2565,11 @@ progress_pmap(Report,File,Fun,List) ->
{L1,L2} = if length(L)>=NPerProc -> lists:split(NPerProc,L);
true -> {L,[]} % last chunk
end,
- P = spawn(
+ {P,_Ref} =
+ spawn_monitor(
fun() ->
progress_map(Collector,ReportInterval,File,Fun,L1)
end),
- erlang:monitor(process,P),
{L2,[P|Ps]}
end,
{List,[]},
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index f14f0ee849..97a54cd6f9 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -60,6 +60,9 @@
ttb_et]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.8.14",
+ "kernel-3.0","inets-5.10","et-1.5",
+ "erts-6.0"]}]}.
diff --git a/lib/observer/src/observer.appup.src b/lib/observer/src/observer.appup.src
index 1d5a0d93f5..9fde365ff3 100644
--- a/lib/observer/src/observer.appup.src
+++ b/lib/observer/src/observer.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -15,5 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-{"%VSN%",[],[]}.
+{"%VSN%",
+ [{<<".*">>,[{restart_application, observer}]}],
+ [{<<".*">>,[{restart_application, observer}]}]
+}.
diff --git a/lib/observer/src/observer.erl b/lib/observer/src/observer.erl
index 098100e8ee..a30ceecc63 100644
--- a/lib/observer/src/observer.erl
+++ b/lib/observer/src/observer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -18,8 +18,11 @@
-module(observer).
--export([start/0]).
+-export([start/0, stop/0]).
start() ->
observer_wx:start().
+
+stop() ->
+ observer_wx:stop().
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index 9f77891426..c279218707 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -146,7 +146,8 @@ all_or_expand(Tab,Term) ->
Check = io_lib:format("~P",[Term,100]),
Exp = Preview=/=Check,
all_or_expand(Tab,Term,Preview,Exp).
-all_or_expand(_Tab,_Term,Str,false) ->
+all_or_expand(_Tab,Term,Str,false)
+ when not is_binary(Term) ->
href_proc_port(lists:flatten(Str));
all_or_expand(Tab,Term,Preview,true)
when not is_binary(Term) ->
@@ -158,20 +159,16 @@ all_or_expand(Tab,Term,Preview,true)
"&key2="++integer_to_list(Key2)++
"&key3="++integer_to_list(Key3)],
"Click to expand above term")];
-all_or_expand(Tab,Bin,PreviewStr,true) when is_binary(Bin) ->
- <<Preview:80, _/binary>> = Bin,
+all_or_expand(Tab,Bin,_PreviewStr,_Expand)
+ when is_binary(Bin) ->
Size = byte_size(Bin),
+ PrevSize = min(Size, 10) * 8,
+ <<Preview:PrevSize, _/binary>> = Bin,
Hash = erlang:phash2(Bin),
Key = {Preview, Size, Hash},
ets:insert(Tab,{Key,Bin}),
- [href_proc_port(lists:flatten(PreviewStr), false), $\n,
- href("TARGET=\"expanded\"",
- ["#OBSBinary?key1="++integer_to_list(Preview)++
- "&key2="++integer_to_list(Size)++
- "&key3="++integer_to_list(Hash)],
- "Click to expand above term")].
-
-
+ Term = io_lib:format("~p", [['#OBSBin',Preview,Size,Hash]]),
+ href_proc_port(lists:flatten(Term), true).
color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN));
color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)).
@@ -242,7 +239,7 @@ href(Args,Link,Text) ->
["<A HREF=\"",Link,"\" ",Args,">",Text,"</A>"].
font(Args,Text) ->
["<FONT ",Args,">\n",Text,"\n</FONT>\n"].
-p(Text) ->
+p(Text) ->
["<P>",Text,"</P>\n"].
br() ->
"<BR>\n".
@@ -336,34 +333,28 @@ href_proc_bin(From, T, Acc, LTB) ->
{OffsetSizePos,Rest} = split($],T),
BinStr =
case string:tokens(OffsetSizePos,",.| \n") of
- [Offset,Size,Pos] when From =:= cdv ->
+ [Offset,SizeStr,Pos] when From =:= cdv ->
Id = {list_to_integer(Offset),10,list_to_integer(Pos)},
{ok,PreviewBin} = crashdump_viewer:expand_binary(Id),
- PreviewStr = ["&lt;&lt;",
- [integer_to_list(X)++"," || <<X:8>> <= PreviewBin],
- "...(",
- observer_lib:to_str({bytes,Size}),
- ")&gt;&gt;"],
+ PreviewStr = preview_string(list_to_integer(SizeStr), PreviewBin),
if LTB ->
href("TARGET=\"expanded\"",
["#Binary?offset="++Offset++
- "&size="++Size++
+ "&size="++SizeStr++
"&pos="++Pos],
PreviewStr);
true ->
PreviewStr
end;
- [Preview,Size,Md5] when From =:= obs ->
- PreviewBin = <<(list_to_integer(Preview)):80>>,
- PreviewStr = ["&lt;&lt;",
- [integer_to_list(X)++"," || <<X:8>> <= PreviewBin],
- "...(",
- observer_lib:to_str({bytes,list_to_integer(Size)}),
- ")&gt;&gt;"],
+ [Preview,SizeStr,Md5] when From =:= obs ->
+ Size = list_to_integer(SizeStr),
+ PrevSize = min(Size, 10) * 8,
+ PreviewStr = preview_string(Size,
+ <<(list_to_integer(Preview)):PrevSize>>),
if LTB ->
href("TARGET=\"expanded\"",
["#OBSBinary?key1="++Preview++
- "&key2="++Size++
+ "&key2="++SizeStr++
"&key3="++Md5],
PreviewStr);
true ->
@@ -374,6 +365,35 @@ href_proc_bin(From, T, Acc, LTB) ->
end,
href_proc_port(Rest,[BinStr|Acc],LTB).
+preview_string(Size, PreviewBin) when Size > 10 ->
+ ["&lt;&lt;",
+ remove_lgt(io_lib:format("~p",[PreviewBin])),
+ "...(",
+ observer_lib:to_str({bytes,Size}),
+ ")",
+ "&gt;&gt"];
+preview_string(_, PreviewBin) ->
+ ["&lt;&lt;",
+ remove_lgt(io_lib:format("~p",[PreviewBin])),
+ "&gt;&gt"].
+
+remove_lgt(Deep) ->
+ remove_lgt_1(lists:flatten(Deep)).
+
+remove_lgt_1([$<,$<|Rest]) ->
+ [$>,$>|BinStr] = lists:reverse(Rest),
+ replace_lgt(lists:reverse(BinStr)).
+
+replace_lgt([$<|R]) ->
+ ["&lt;"|replace_lgt(R)];
+replace_lgt([$>|R]) ->
+ ["&gt;"|replace_lgt(R)];
+replace_lgt([L=[_|_]|R]) ->
+ [replace_lgt(L)|replace_lgt(R)];
+replace_lgt([A|R]) ->
+ [A|replace_lgt(R)];
+replace_lgt([]) -> [].
+
split(Char,Str) ->
split(Char,Str,[]).
split(Char,[Char|Str],Acc) -> % match Char
@@ -381,7 +401,6 @@ split(Char,[Char|Str],Acc) -> % match Char
split(Char,[H|T],Acc) ->
split(Char,T,[H|Acc]).
-
warn([]) ->
[];
warn(Warning) ->
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index e0f7bf482b..34c7b127ff 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -249,6 +249,8 @@ to_str({func, {F,A}}) when is_atom(F), is_integer(A) ->
lists:concat([F, "/", A]);
to_str({func, {F,'_'}}) when is_atom(F) ->
atom_to_list(F);
+to_str({{format,Fun},Value}) when is_function(Fun) ->
+ Fun(Value);
to_str({A, B}) when is_atom(A), is_atom(B) ->
lists:concat([A, ":", B]);
to_str({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) ->
@@ -699,7 +701,9 @@ progress_handler(Caller,Env,Title,Str) ->
register(?progress_handler,self()),
wx:set_env(Env),
PD = progress_dialog(Env,Title,Str),
- progress_loop(Title,PD,Caller).
+ try progress_loop(Title,PD,Caller)
+ catch closed -> normal end.
+
progress_loop(Title,PD,Caller) ->
receive
{progress,{ok,done}} -> % to make wait_for_progress/0 return
@@ -738,8 +742,12 @@ progress_dialog(_Env,Title,Str) ->
PD.
update_progress(PD,Value) ->
- wxProgressDialog:update(PD,Value).
+ try wxProgressDialog:update(PD,Value)
+ catch _:_ -> throw(closed) %% Port or window have died
+ end.
update_progress_text(PD,Text) ->
- wxProgressDialog:update(PD,0,[{newmsg,Text}]).
+ try wxProgressDialog:update(PD,0,[{newmsg,Text}])
+ catch _:_ -> throw(closed) %% Port or window have died
+ end.
finish_progress(PD) ->
wxProgressDialog:destroy(PD).
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 988e04993c..8e8a37fc93 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -34,6 +34,7 @@
-record(state, {parent,
frame,
+ notebook,
pid,
pages=[],
expand_table,
@@ -76,6 +77,7 @@ init([Pid, ParentFrame, Parent]) ->
{Frame, #state{parent=Parent,
pid=Pid,
frame=Frame,
+ notebook=Notebook,
pages=[ProcessPage,MessagePage,DictPage,StackPage,StatePage],
expand_table=Table
}}
@@ -136,9 +138,9 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}},
{noreply, State};
Callback ->
[{"key1",Key1},{"key2",Key2},{"key3",Key3}] = httpd:parse_query(Rest),
- Id = {observer, {T,{list_to_integer(Key1),
- list_to_integer(Key2),
- list_to_integer(Key3)}}},
+ Id = {obs, {T,{list_to_integer(Key1),
+ list_to_integer(Key2),
+ list_to_integer(Key3)}}},
Opened =
case lists:keyfind(Id,1,Opened0) of
false ->
@@ -158,6 +160,9 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Info}}}, State)
handle_event(Event, _State) ->
error({unhandled_event, Event}).
+handle_info({get_debug_info, From}, State = #state{notebook=Notebook}) ->
+ From ! {procinfo_debug, Notebook},
+ {noreply, State};
handle_info(_Info, State) ->
%% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index b276965f83..da4cb8e041 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -132,8 +132,8 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
observer_lib:set_listctrl_col_size(Grid, W),
{noreply, State};
-handle_event(#wx{obj=Grid, event=#wxList{type=command_list_item_activated,
- itemIndex=Index}},
+handle_event(#wx{event=#wxList{type=command_list_item_activated,
+ itemIndex=Index}},
State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs}) ->
Table = lists:nth(Index+1, Tabs),
case Table#tab.protection of
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 4c385b76aa..ced26f7119 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -19,7 +19,7 @@
-behaviour(wx_object).
--export([start/0]).
+-export([start/0, stop/0]).
-export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1,
create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
@@ -69,6 +69,9 @@ start() ->
_Obj -> ok
end.
+stop() ->
+ wx_object:call(observer, stop).
+
create_menus(Object, Menus) when is_list(Menus) ->
wx_object:call(Object, {create_menus, Menus}).
@@ -214,7 +217,7 @@ handle_event(#wx{event = #wxClose{}}, State) ->
{stop, normal, State};
handle_event(#wx{id = ?ID_CDV, event = #wxCommand{type = command_menu_selected}}, State) ->
- crashdump_viewer:start(),
+ spawn(crashdump_viewer, start, []),
{noreply, State};
handle_event(#wx{id = ?wxID_EXIT, event = #wxCommand{type = command_menu_selected}}, State) ->
@@ -331,6 +334,10 @@ handle_call({get_attrib, Attrib}, _From, State) ->
handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
{reply, TraceP, State};
+handle_call(stop, _, State = #state{frame = Frame}) ->
+ wxFrame:destroy(Frame),
+ {stop, normal, ok, State};
+
handle_call(_Msg, _From, State) ->
{reply, ok, State}.
@@ -367,6 +374,10 @@ handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}
end,
{noreply, State};
+handle_info({get_debug_info, From}, State = #state{notebook=Notebook, active_tab=Pid}) ->
+ From ! {observer_debug, wx:get_env(), Notebook, Pid},
+ {noreply, State};
+
handle_info({'EXIT', Pid, _Reason}, State) ->
io:format("Child (~s) crashed exiting: ~p ~p~n",
[pid2panel(Pid, State), Pid,_Reason]),
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 7a582436b4..f6e7d18f65 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -120,27 +120,63 @@ start_stop(Config) when is_list(Config) ->
Dump = hd(?config(dumps,Config)),
timer:sleep(1000),
- ProcsBefore = length(processes()),
+ ProcsBefore = processes(),
+ NumProcsBefore = length(ProcsBefore),
ok = crashdump_viewer:start(Dump),
- true = is_pid(whereis(crashdump_viewer_server)),
- true = is_pid(whereis(cdv_wx)),
- true = is_pid(whereis(cdv_proc_cb)),
- true = is_pid(whereis(cdv_port_cb)),
- true = is_pid(whereis(cdv_ets_cb)),
- true = is_pid(whereis(cdv_timer_cb)),
- true = is_pid(whereis(cdv_fun_cb)),
- true = is_pid(whereis(cdv_atom_cb)),
- true = is_pid(whereis(cdv_dist_cb)),
- true = is_pid(whereis(cdv_mod_cb)),
+ ExpectedRegistered = [crashdump_viewer_server,
+ cdv_wx,
+ cdv_proc_cb,
+ cdv_proc_cb__holder,
+ cdv_port_cb,
+ cdv_port_cb__holder,
+ cdv_ets_cb,
+ cdv_ets_cb__holder,
+ cdv_timer_cb,
+ cdv_timer_cb__holder,
+ cdv_fun_cb,
+ cdv_fun_cb__holder,
+ cdv_atom_cb,
+ cdv_atom_cb__holder,
+ cdv_dist_cb,
+ cdv_dist_cb__holder,
+ cdv_mod_cb,
+ cdv_mod_cb__holder],
+ Regs=[begin
+ P=whereis(N),
+ {P,N,erlang:monitor(process,P)}
+ end || N <- ExpectedRegistered],
+ ct:log("CDV procs: ~n~p~n",[Regs]),
+ [true=is_pid(P) || {P,_,_} <- Regs],
timer:sleep(5000), % give some time to live
ok = crashdump_viewer:stop(),
- timer:sleep(1000), % give some time to stop
- undefined = whereis(crashdump_viewer_server),
- undefined = whereis(cdv_wx),
- ProcsAfter=length(processes()),
- ProcsAfter=ProcsBefore,
+ recv_downs(Regs),
+ timer:sleep(2000),
+ ProcsAfter = processes(),
+ NumProcsAfter = length(ProcsAfter),
+ if NumProcsAfter=/=NumProcsBefore ->
+ ct:log("Before but not after:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsBefore -- ProcsAfter]]),
+ ct:log("After but not before:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsAfter -- ProcsBefore]]),
+ ct:fail("leaking processes");
+ true ->
+ ok
+ end,
ok.
+recv_downs([]) ->
+ ok;
+recv_downs(Regs) ->
+ receive
+ {'DOWN',Ref,process,_Pid,_} ->
+ ct:log("Got 'DOWN' for process ~n~p~n",[_Pid]),
+ recv_downs(lists:keydelete(Ref,3,Regs))
+ after 30000 ->
+ ct:log("Timeout waiting for down:~n~p~n",
+ [[{Reg,process_info(P)} || {P,_,_}=Reg <- Regs]]),
+ ct:log("Message queue:~n~p~n",[process_info(self(),messages)])
+ end.
+
%% Try to load nonexisting file
non_existing(Config) when is_list(Config) ->
ExpectedReason = "non-existing-file is not an Erlang crash dump\n",
@@ -428,8 +464,10 @@ do_create_dumps(DataDir,Rel) ->
end.
-%% Create a dump which has two visible nodes, one hidden and one
+%% Create a dump which has three visible nodes, one hidden and one
%% not connected node, and with monitors and links between nodes.
+%% One of the visible nodes is stopped and started again in order to
+%% get multiple creations.
full_dist_dump(DataDir,Rel) ->
Opt = rel_opt(Rel),
Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
@@ -450,6 +488,15 @@ full_dist_dump(DataDir,Rel) ->
get_response(P4),
get_response(P1),
+ %% start, stop and start a node in order to get multiple 'creations'
+ {ok,N5} = ?t:start_node(n5,peer,Opt ++ PzOpt),
+ P51 = rpc:call(N5,?helper_mod,remote_proc,[P1,Creator]),
+ get_response(P51),
+ ?t:stop_node(N5),
+ {ok,N5} = ?t:start_node(n5,peer,Opt ++ PzOpt),
+ P52 = rpc:call(N5,?helper_mod,remote_proc,[P1,Creator]),
+ get_response(P52),
+
{aaaaaaaa,N1} ! {hello,from,other,node}, % distribution message
?t:stop_node(N3),
@@ -458,6 +505,7 @@ full_dist_dump(DataDir,Rel) ->
?t:stop_node(N2),
?t:stop_node(N4),
+ ?t:stop_node(N5),
CD.
get_response(P) ->
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index b6665cb70b..af07165456 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -19,34 +19,35 @@
-module(observer_SUITE).
-include_lib("test_server/include/test_server.hrl").
+-include_lib("wx/include/wx.hrl").
+-include_lib("observer/src/observer_tv.hrl").
%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0]).
+-export([init_per_testcase/2, end_per_testcase/2,
+ init_per_group/2, end_per_group/2,
+ init_per_suite/1, end_per_suite/1
+ ]).
%% Test cases
--export([app_file/1]).
+-export([app_file/1, appup_file/1,
+ basic/1, process_win/1, table_win/1
+ ]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
-init_per_testcase(_Case, Config) ->
- Dog = ?t:timetrap(?default_timeout),
- [{watchdog, Dog} | Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [app_file].
+all() ->
+ [app_file, appup_file, {group, gui}].
-groups() ->
- [].
+groups() ->
+ [{gui, [],
+ [basic
+ %% , process_win, table_win
+ ]
+ }].
init_per_suite(Config) ->
Config.
@@ -54,12 +55,40 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+init_per_group(gui, Config) ->
+ try
+ case os:type() of
+ {unix,darwin} ->
+ exit("Can not test on MacOSX");
+ {unix, _} ->
+ io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
+ case ct:get_config(xserver, none) of
+ none -> ignore;
+ Server -> os:putenv("DISPLAY", Server)
+ end;
+ _ -> ignore
+ end,
+ wx:new(),
+ wx:destroy(),
+ Config
+ catch
+ _:undef ->
+ {skipped, "No wx compiled for this platform"};
+ _:Reason ->
+ SkipReason = io_lib:format("Start wx failed: ~p", [Reason]),
+ {skipped, lists:flatten(SkipReason)}
+ end.
+end_per_group(_, _) ->
+ ok.
app_file(suite) ->
[];
@@ -68,3 +97,166 @@ app_file(doc) ->
app_file(Config) when is_list(Config) ->
?line ok = ?t:app_test(observer),
ok.
+
+%% Testing .appup file
+appup_file(Config) when is_list(Config) ->
+ ok = ?t:appup_test(observer).
+
+-define(DBG(Foo), io:format("~p: ~p~n",[?LINE, catch Foo])).
+
+basic(suite) -> [];
+basic(doc) -> [""];
+basic(Config) when is_list(Config) ->
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+
+ io:format("Notebook ~p~n",[Notebook]),
+ Count = wxNotebook:getPageCount(Notebook),
+ true = Count >= 6,
+ 0 = wxNotebook:getSelection(Notebook),
+ timer:sleep(500),
+ Check = fun(N, TestMore) ->
+ ok = wxNotebook:advanceSelection(Notebook),
+ TestMore andalso
+ test_page(wxNotebook:getPageText(Notebook, N),
+ wxNotebook:getCurrentPage(Notebook)),
+ timer:sleep(200)
+ end,
+ %% Just verify that we can toogle trough all pages
+ [_|_] = [Check(N, false) || N <- lists:seq(1, Count)],
+ %% Cause it to resize
+ Frame = get_top_level_parent(Notebook),
+ {W,H} = wxWindow:getSize(Frame),
+ wxWindow:setSize(Frame, W+10, H+10),
+ [_|_] = [Check(N, true) || N <- lists:seq(1, Count)],
+
+ ok = observer:stop().
+
+test_page("Load Charts" ++ _, _Window) ->
+ %% Just let it display some info and hopefully it doesn't crash
+ timer:sleep(2000),
+ ok;
+test_page("Applications" ++ _, _Window) ->
+ ok = application:start(mnesia),
+ timer:sleep(1000), %% Give it time to refresh
+ Active = get_active(),
+ FakeEv = #wx{event=#wxCommand{type=command_listbox_selected, cmdString="mnesia"}},
+ Active ! FakeEv,
+ timer:sleep(1000), %% Give it time to refresh
+ ok = application:stop(mnesia),
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page("Processes" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
+ Focus = #wx{event=#wxList{type=command_list_item_focused, itemIndex=2}},
+ Active ! Focus,
+ Activate = #wx{event=#wxList{type=command_list_item_activated}},
+ Active ! Activate,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page("Table" ++ _, _Window) ->
+ Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
+ Active = get_active(),
+ Active ! refresh_interval,
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
+ timer:sleep(1000),
+ Table = lists:nth(3, Tables),
+ ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
+ Focus = #wx{event=#wxList{type=command_list_item_selected, itemIndex=2}},
+ Active ! Focus,
+ Activate = #wx{event=#wxList{type=command_list_item_activated, itemIndex=2}},
+ Active ! Activate,
+
+ Info = 407, %% whitebox...
+ Active ! #wx{id=Info},
+ timer:sleep(1000),
+ ok;
+
+test_page(Title, Window) ->
+ io:format("Page ~p: ~p~n", [Title, Window]),
+ %% Just let it display some info and hopefully it doesn't crash
+ timer:sleep(1000),
+ ok.
+
+
+process_win(suite) -> [];
+process_win(doc) -> [""];
+process_win(Config) when is_list(Config) ->
+ ok = observer:start(),
+ ObserverNB = setup_whitebox_testing(),
+ Parent = get_top_level_parent(ObserverNB),
+ Frame = observer_procinfo:start(self(), Parent, self()),
+ PIPid = wx_object:get_pid(Frame),
+ PIPid ! {get_debug_info, self()},
+ Notebook = receive {procinfo_debug, NB} -> NB end,
+ Count = wxNotebook:getPageCount(Notebook),
+ Check = fun(_N) ->
+ ok = wxNotebook:advanceSelection(Notebook),
+ timer:sleep(400)
+ end,
+ [_|_] = [Check(N) || N <- lists:seq(1, Count)],
+ PIPid ! #wx{event=#wxClose{type=close_window}},
+ observer:stop(),
+ ok.
+
+table_win(suite) -> [];
+table_win(doc) -> [""];
+table_win(Config) when is_list(Config) ->
+ Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
+ Table = lists:nth(3, Tables),
+ ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+ Parent = get_top_level_parent(Notebook),
+ TObj = observer_tv_table:start_link(Parent, [{node,node()}, {type,ets}, {table,#tab{name=foo, id=Table}}]),
+ %% Modal can not test edit..
+ %% TPid = wx_object:get_pid(TObj),
+ %% TPid ! #wx{event=#wxList{type=command_list_item_activated, itemIndex=12}},
+ timer:sleep(2000),
+ wx_object:get_pid(TObj) ! #wx{event=#wxClose{type=close_window}},
+ observer:stop(),
+ ok.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_top_level_parent(Window) ->
+ Parent = wxWindow:getParent(Window),
+ case wx:is_null(Parent) of
+ true -> Window;
+ false -> get_top_level_parent(Parent)
+ end.
+
+setup_whitebox_testing() ->
+ %% So that if we die observer exists
+ link(whereis(observer)),
+ {Env, Notebook, _Active} = get_observer_debug(),
+ wx:set_env(Env),
+ Notebook.
+
+get_active() ->
+ {_, _, Active} = get_observer_debug(),
+ Active.
+
+get_observer_debug() ->
+ observer ! {get_debug_info, self()},
+ receive
+ {observer_debug, Env, Notebook, Active} ->
+ {Env, Notebook, Active}
+ end.
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index f48809a839..a6300eeb18 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 1.3.1.2
+OBSERVER_VSN = 2.0
diff --git a/lib/odbc/aclocal.m4 b/lib/odbc/aclocal.m4
index 46b30a16b3..2b47f7c4bc 100644
--- a/lib/odbc/aclocal.m4
+++ b/lib/odbc/aclocal.m4
@@ -74,6 +74,21 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re
AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)])
+dnl Cross compilation variables for OSE
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
+
])
AC_DEFUN(ERL_XCOMP_SYSROOT_INIT,
@@ -488,6 +503,8 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support,
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -500,6 +517,8 @@ else
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -728,6 +747,12 @@ if test "X$host_os" = "Xwin32"; then
THR_LIBS=
THR_LIB_NAME=win32_threads
THR_LIB_TYPE=win32_threads
+elif test "X$host_os" = "Xose"; then
+ AC_MSG_RESULT(yes)
+ THR_DEFS="-DOSE_THREADS"
+ THR_LIBS=
+ THR_LIB_NAME=ose_threads
+ THR_LIB_TYPE=ose_threads
else
AC_MSG_RESULT(no)
THR_DEFS=
@@ -1078,9 +1103,22 @@ case "$THR_LIB_NAME" in
test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes
;;
- pthread)
- ETHR_THR_LIB_BASE_DIR=pthread
- AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ pthread|ose_threads)
+ case "$THR_LIB_NAME" in
+ pthread)
+ ETHR_THR_LIB_BASE_DIR=pthread
+ AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ ;;
+ ose_threads)
+ AC_DEFINE(ETHR_OSE_THREADS, 1,
+ [Define if you have OSE style threads])
+ ETHR_THR_LIB_BASE_DIR=ose
+ AC_CHECK_HEADER(ose_spi/ose_spi.h,
+ AC_DEFINE(HAVE_OSE_SPI_H, 1,
+ [Define if you have the "ose_spi/ose_spi.h" header file.]))
+ ;;
+ esac
+ if test "x$THR_LIB_NAME" == "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1139,6 +1177,7 @@ case "$THR_LIB_NAME" in
*) ;;
esac
+ fi
dnl We sometimes need ETHR_DEFS in order to find certain headers
dnl (at least for pthread.h on osf1).
saved_cppflags="$CPPFLAGS"
@@ -1151,7 +1190,6 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for headers
dnl
-
AC_CHECK_HEADER(pthread.h, \
AC_DEFINE(ETHR_HAVE_PTHREAD_H, 1, \
[Define if you have the <pthread.h> header file.]))
@@ -1184,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
-
+ if test "x$THR_LIB_NAME" == "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
@@ -1311,6 +1349,8 @@ case "$THR_LIB_NAME" in
AC_MSG_RESULT([$linux_futex])
test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.])
+ fi
+
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(long long)
diff --git a/lib/odbc/src/odbc.app.src b/lib/odbc/src/odbc.app.src
index 5229b28c08..b2c5775de2 100644
--- a/lib/odbc/src/odbc.app.src
+++ b/lib/odbc/src/odbc.app.src
@@ -11,5 +11,6 @@
]},
{applications, [kernel, stdlib]},
{env,[]},
- {mod, {odbc_app, []}}]}.
+ {mod, {odbc_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
index c7c83ea079..bf8872eae4 100644
--- a/lib/odbc/src/odbc.appup.src
+++ b/lib/odbc/src/odbc.appup.src
@@ -1,8 +1,24 @@
%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
{"%VSN%",
[
- {<<"2\\.*">>, [{restart_application, odbc}]}
+ {<<"2\\..*">>, [{restart_application, odbc}]}
],
[
- {<<"2\\.*">>, [{restart_application, odbc}]}
+ {<<"2\\..*">>, [{restart_application, odbc}]}
]}.
diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl
index e3a3440559..a7bb1d0ffe 100644
--- a/lib/odbc/test/odbc_start_SUITE.erl
+++ b/lib/odbc/test/odbc_start_SUITE.erl
@@ -109,8 +109,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
case odbc_test_lib:odbc_check() of
- ok -> [start];
- Other -> {skip, Other}
+ ok -> [app, appup, start];
+ Other -> [app, appup]
end.
groups() ->
@@ -127,6 +127,14 @@ end_per_group(_GroupName, Config) ->
%% Test cases starts here.
%%--------------------------------------------------------------------
+%% Test that the odbc app file is ok
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(odbc).
+
+%% Test that the odbc appup file is ok
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(odbc).
+
start(doc) ->
["Test start/stop of odbc"];
start(suite) ->
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index d9e2ab26a9..1af4751248 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.19
+ODBC_VSN = 2.10.20
diff --git a/lib/orber/src/Makefile b/lib/orber/src/Makefile
index 1c6781e5fd..d96350f4d5 100644
--- a/lib/orber/src/Makefile
+++ b/lib/orber/src/Makefile
@@ -21,9 +21,6 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-# To get hold of SYSTEM_VSN (e.g. R9C).
-#include $(ERL_TOP)/erts/vsn.mk
-
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
diff --git a/lib/orber/src/orber.app.src b/lib/orber/src/orber.app.src
index 88df4162b6..30bd90347d 100644
--- a/lib/orber/src/orber.app.src
+++ b/lib/orber/src/orber.app.src
@@ -103,7 +103,9 @@
orber_iiop_pm, orber_env]},
{applications, [stdlib, kernel, mnesia]},
{env, []},
- {mod, {orber, []}}
+ {mod, {orber, []}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","mnesia-4.12","kernel-3.0",
+ "inets-5.10","erts-6.0"]}
]}.
diff --git a/lib/orber/src/orber_interceptors.erl b/lib/orber/src/orber_interceptors.erl
index 407823ea79..62870b35b5 100644
--- a/lib/orber/src/orber_interceptors.erl
+++ b/lib/orber/src/orber_interceptors.erl
@@ -112,7 +112,7 @@ pop_system_message_interceptor(out) ->
[{_, []}] ->
ok;
[{_, Interceptors}] ->
- ets:insert(orber_interceptors, {message_out_interceptors, remove_last_element(Interceptors)});
+ ets:insert(orber_interceptors, {message_out_interceptors, lists:droplast(Interceptors)});
_ ->
corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO})
end.
@@ -151,12 +151,3 @@ apply_message_interceptors([], F, ObjRef, Bytes) ->
apply_message_interceptors([M | Rest], F, ObjRef, Bytes) ->
apply_message_interceptors(Rest, F, ObjRef, apply(M, F, [ObjRef, Bytes])).
-
-remove_last_element([]) ->
- [];
-remove_last_element([M]) ->
- [];
-remove_last_element([M |Tail]) ->
- remove_last_element([Tail]).
-
-
diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk
index 7bbebc65dc..3ea64b1ff6 100644
--- a/lib/orber/vsn.mk
+++ b/lib/orber/vsn.mk
@@ -1,2 +1,2 @@
-ORBER_VSN = 3.6.26.1
+ORBER_VSN = 3.6.27
diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c
index b5114d10ed..409db84aa7 100644
--- a/lib/os_mon/c_src/memsup.c
+++ b/lib/os_mon/c_src/memsup.c
@@ -324,7 +324,7 @@ get_mem_procfs(memory_ext *me){
/* arch specific functions */
-#if defined(__linux__) /* ifdef SYSINFO */
+#if defined(__linux__) && !defined(__ANDROID__)/* ifdef SYSINFO */
/* sysinfo does not include cached memory which is a problem. */
static int
get_extended_mem_sysinfo(memory_ext *me) {
@@ -395,8 +395,12 @@ get_extended_mem_sgi(memory_ext *me) {
static void
get_extended_mem(memory_ext *me) {
+/* android */
+#if defined(__ANDROID__)
+ if (get_mem_procfs(me)) return;
+
/* linux */
-#if defined(__linux__)
+#elif defined(__linux__)
if (get_mem_procfs(me)) return;
if (get_extended_mem_sysinfo(me)) return;
diff --git a/lib/os_mon/src/os_mon.app.src b/lib/os_mon/src/os_mon.app.src
index 15bbd2663c..cc08cebe3d 100644
--- a/lib/os_mon/src/os_mon.app.src
+++ b/lib/os_mon/src/os_mon.app.src
@@ -29,4 +29,7 @@
{start_disksup, true},
{start_memsup, true},
{start_os_sup, false}]},
- {mod, {os_mon, []}}]}.
+ {mod, {os_mon, []}},
+ {runtime_dependencies, ["stdlib-2.0","snmp-4.25.1","sasl-2.4",
+ "otp_mibs-1.0.9","mnesia-4.12","kernel-3.0",
+ "erts-6.0"]}]}.
diff --git a/lib/os_mon/src/os_mon.appup.src b/lib/os_mon/src/os_mon.appup.src
index f8e09a7d87..480f5d9511 100644
--- a/lib/os_mon/src/os_mon.appup.src
+++ b/lib/os_mon/src/os_mon.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -16,26 +16,7 @@
%%
%% %CopyrightEnd%
%%
-
{"%VSN%",
- [
- {"2.1",
- [{load_module, cpu_sup},
- {load_module, disksup},
- {load_module, memsup},
- {load_module, os_mon},
- {load_module, os_mon_mib}]},
- {"2.1.1",
- [{load_module, os_mon_mib}]}
- ],
- [
- {"2.1",
- [{load_module, cpu_sup},
- {load_module, disksup},
- {load_module, memsup},
- {load_module, os_mon},
- {load_module, os_mon_mib}]},
- {"2.1.1",
- [{load_module, os_mon_mib}]}
- ]
+ [{<<".*">>,[{restart_application, os_mon}]}],
+ [{<<".*">>,[{restart_application, os_mon}]}]
}.
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
index e0382cb0c7..9f58e043db 100644
--- a/lib/os_mon/test/cpu_sup_SUITE.erl
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -257,8 +257,8 @@ port(Config) when is_list(Config) ->
terminate(suite) ->
[];
terminate(Config) when is_list(Config) ->
- ?line ok = application:set_env(os_mon, start_cpu_sup, false),
- ?line ok = supervisor:terminate_child(os_mon_sup, cpu_sup),
+ ok = application:set_env(os_mon, start_cpu_sup, false),
+ _ = supervisor:terminate_child(os_mon_sup, cpu_sup),
ok.
unavailable(suite) ->
diff --git a/lib/os_mon/test/disksup_SUITE.erl b/lib/os_mon/test/disksup_SUITE.erl
index 9c65d8b692..94661cfa77 100644
--- a/lib/os_mon/test/disksup_SUITE.erl
+++ b/lib/os_mon/test/disksup_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -350,15 +350,16 @@ otp_5910(doc) ->
otp_5910(Config) when is_list(Config) ->
%% Make sure disksup sets at least one alarm
- Data = disksup:get_disk_data(),
+ Data = lists:sort(disksup:get_disk_data()),
Threshold0 = disksup:get_almost_full_threshold(),
Threshold = case over_threshold(Data, Threshold0) of
- 0 ->
- [{_Id,_Kbyte,Cap}|_] = Data,
- ok = disksup:set_almost_full_threshold((Cap-1)/100),
- Cap-1;
- _N -> Threshold0
- end,
+ 0 ->
+ [{_Id,_Kbyte,Cap}|_] = Data,
+ io:format("Data ~p Threshold ~p ~n",[Data, Cap-1]),
+ ok = disksup:set_almost_full_threshold((Cap-1)/100),
+ Cap-1;
+ _N -> Threshold0
+ end,
ok = application:set_env(os_mon, disk_almost_full_threshold, Threshold/100),
disksup ! timeout, % force a disk check
Data2 = disksup:get_disk_data(),
diff --git a/lib/os_mon/test/os_mon_SUITE.erl b/lib/os_mon/test/os_mon_SUITE.erl
index f074657d4c..08ad8436dd 100644
--- a/lib/os_mon/test/os_mon_SUITE.erl
+++ b/lib/os_mon/test/os_mon_SUITE.erl
@@ -25,7 +25,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases
--export([app_file/1, config/1]).
+-export([app_file/1, appup_file/1, config/1]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
@@ -43,8 +43,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
case test_server:os_type() of
- {unix, sunos} -> [app_file, config];
- _OS -> [app_file]
+ {unix, sunos} -> [app_file, appup_file, config];
+ _OS -> [app_file, appup_file]
end.
groups() ->
@@ -71,6 +71,9 @@ app_file(Config) when is_list(Config) ->
?line ok = test_server:app_test(os_mon),
ok.
+appup_file(Config) when is_list(Config) ->
+ ok = test_server:appup_test(os_mon).
+
config(suite) ->
[];
config(doc) ->
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index e9e90729f2..74397c2bc6 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.14
+OS_MON_VSN = 2.2.15
diff --git a/lib/ose/Makefile b/lib/ose/Makefile
new file mode 100644
index 0000000000..2959f04c3e
--- /dev/null
+++ b/lib/ose/Makefile
@@ -0,0 +1,36 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-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
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+
+SUB_DIRECTORIES = src doc/src
+
+include vsn.mk
+VSN = $(OSE_VSN)
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
diff --git a/lib/ose/doc/html/.gitignore b/lib/ose/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ose/doc/html/.gitignore
diff --git a/lib/ose/doc/man3/.gitignore b/lib/ose/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ose/doc/man3/.gitignore
diff --git a/lib/ose/doc/man6/.gitignore b/lib/ose/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ose/doc/man6/.gitignore
diff --git a/lib/ose/doc/pdf/.gitignore b/lib/ose/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ose/doc/pdf/.gitignore
diff --git a/lib/ose/doc/src/.gitignore b/lib/ose/doc/src/.gitignore
new file mode 100644
index 0000000000..860e9e703e
--- /dev/null
+++ b/lib/ose/doc/src/.gitignore
@@ -0,0 +1 @@
+ose.xml
diff --git a/lib/ose/doc/src/Makefile b/lib/ose/doc/src/Makefile
new file mode 100644
index 0000000000..dd58029064
--- /dev/null
+++ b/lib/ose/doc/src/Makefile
@@ -0,0 +1,132 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2012. 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
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(OSE_VSN)
+APPLICATION=ose
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Help application directory specification
+# ----------------------------------------------------
+EDOC_DIR = $(ERL_TOP)/lib/edoc
+SYNTAX_TOOLS_DIR = $(ERL_TOP)/lib/syntax_tools
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+
+XML_REF3_FILES = \
+ ose.xml \
+ ose_erl_driver.xml
+
+XML_REF6_FILES = ose_app.xml
+
+XML_PART_FILES = part.xml
+XML_CHAPTER_FILES = notes.xml ose_intro.xml ose_signals_chapter.xml
+
+BOOK_FILES = book.xml
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
+ $(XML_APPLICATION_FILES)
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+SPECS_FILES =
+
+TOP_SPECS_FILE =
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+
+SPECS_FLAGS = -I../../include -I../../../kernel/include
+
+OSE_SRC_DIR = ../../src
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+docs: man pdf html
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: $(HTML_REF_MAN_FILE)
+
+man: $(MAN3_FILES) $(MAN6_FILES)
+
+ose.xml: $(OSE_SRC_DIR)/ose.erl
+ escript $(DOCGEN)/priv/bin/xml_from_edoc.escript\
+ $(OSE_SRC_DIR)/$(@:%.xml=%.erl)
+
+debug opt:
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(MAN6DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f $(SPECDIR)/*
+ rm -f errs core *~
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
+ $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
+
+release_spec:
diff --git a/lib/ose/doc/src/book.xml b/lib/ose/doc/src/book.xml
new file mode 100644
index 0000000000..485806e05b
--- /dev/null
+++ b/lib/ose/doc/src/book.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>2014</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>OSE</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2014-01-08</date>
+ <rev>1.0</rev>
+ <file>book.xml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>OSE</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts>
+ <xi:include href="part.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
diff --git a/lib/ose/doc/src/notes.xml b/lib/ose/doc/src/notes.xml
new file mode 100644
index 0000000000..760b92feed
--- /dev/null
+++ b/lib/ose/doc/src/notes.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2014</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>OSE Release Notes</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the OSE application.</p>
+
+</chapter>
diff --git a/lib/ose/doc/src/ose_app.xml b/lib/ose/doc/src/ose_app.xml
new file mode 100644
index 0000000000..e40656fd7b
--- /dev/null
+++ b/lib/ose/doc/src/ose_app.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE appref SYSTEM "appref.dtd">
+
+<appref>
+ <header>
+ <copyright>
+ <year>2014</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>Enea OSE</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <app>ose</app>
+ <appsummary>The OSE Application</appsummary>
+ <description>
+ <p>The OSE application contains modules and documentation that only
+ applies when running Erlang/OTP on Enea OSE.</p>
+ </description>
+
+</appref>
diff --git a/lib/ose/doc/src/ose_erl_driver.xml b/lib/ose/doc/src/ose_erl_driver.xml
new file mode 100644
index 0000000000..1d89d7aeea
--- /dev/null
+++ b/lib/ose/doc/src/ose_erl_driver.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE cref SYSTEM "cref.dtd">
+
+<cref>
+ <header>
+ <copyright>
+ <year>2013</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>erl_driver for Enea OSE</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2014-01-08</date>
+ <rev>A</rev>
+ <file>ose_erl_driver.xml</file>
+ </header>
+ <lib>ose_erl_driver</lib>
+ <libsummary>Linked-in drivers in Enea OSE</libsummary>
+ <description>
+ <p>Writing Linked-in drivers that also work on Enea OSE is very similar for
+ how you would do it for Unix. The difference from Unix is that
+ driver_select, ready_input and ready_output all work with signals
+ instead of file descriptors. This means that the driver_select is
+ used to specify which type of signal should trigger calls to
+ ready_input/ready_output. The functions described below are available
+ to driver programmers on Enea OSE to facilitate this.
+ </p>
+ </description>
+ <section>
+ <title>DATA TYPES</title>
+
+ <taglist>
+ <tag><marker id="union_SIGNAL"/>union SIGNAL</tag>
+ <item>See the Enea OSE SPI documentation for a description.</item>
+ <tag><marker id="SIGSELECT"/>SIGSELECT</tag>
+ <item>See the Enea OSE SPI documentation for a description.</item>
+ <tag><marker id="ErlDrvEvent"/>ErlDrvEvent</tag>
+ <item>The <c>ErlDrvEvent</c> is a handle to a signal number and id combination. It is passed to <seealso marker="erts:erl_driver#driver_select">driver_select(3)</seealso>.</item>
+ <tag><marker id="ErlDrvOseEventId"/>ErlDrvOseEventId</tag>
+ <item>This is the id used to associate a specific signal to a
+ certain driver instance. </item>
+ </taglist>
+ </section>
+ <funcs>
+ <func>
+ <name><ret>union SIGNAL *</ret><nametext>erl_drv_ose_get_signal(ErlDrvEvent drv_event)</nametext></name>
+ <desc>
+ <marker id="erl_drv_ose_get_signal"></marker>
+ <p>Fetch the next signal associated with <c>drv_event</c>.
+ Signals will be returned in the order which they were received and
+ when no more signals are available <c>NULL</c> will be returned.
+ Use this function in the ready_input/ready_output callbacks
+ to get signals.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvEvent</ret><nametext>erl_drv_ose_event_alloc(SIGSELECT signo, ErlDrvOseEventId id, ErlDrvOseEventId (*resolve_signal)(union SIGNAL* sig), void *extra)</nametext></name>
+ <desc>
+ <marker id="erl_drv_ose_event_alloc"></marker>
+ <p>Create a new <c>ErlDrvEvent</c> associated with <c>signo</c>,
+ <c>id</c> and uses the <c>resolve_signal</c> function to extract
+ the <c>id</c> from a signal with <c>signo</c>. The <c>extra</c>
+ parameter can be used for additional data. See
+ <seealso marker="ose_signals_chapter#driver">
+ Signals in a Linked-in driver</seealso> in the OSE User's Guide.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_ose_event_free(ErlDrvEvent drv_event)</nametext></name>
+ <desc>
+ <marker id="erl_drv_ose_event_free"></marker>
+ <p>Free a <c>ErlDrvEvent</c>. This should always be done in the
+ <seealso marker="erts:driver_entry#stop_select">stop_select</seealso>
+ callback when the event is no longer being used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_ose_event_fetch(ErlDrvEvent drv_event, SIGSELECT *signo, ErlDrvOseEventId *id, void **extra)</nametext></name>
+ <desc>
+ <marker id="erl_drv_ose_event_fetch"></marker>
+ <p>Write the signal number, id and any extra data associated with <c>drv_event</c>
+ into <c>*signo</c> and <c>*id</c> respectively. <c>NULL</c> can be
+ also passed as <c>signo</c> or <c>id</c> in order to ignore that field.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+ <section>
+ <title>SEE ALSO</title>
+ <p>
+ <seealso marker="erts:driver_entry">driver_entry(3)</seealso>,
+ <seealso marker="erts:erl_driver">erl_driver(3)</seealso>
+ </p>
+ </section>
+</cref>
diff --git a/lib/ose/doc/src/ose_intro.xml b/lib/ose/doc/src/ose_intro.xml
new file mode 100644
index 0000000000..b5e3ef8b33
--- /dev/null
+++ b/lib/ose/doc/src/ose_intro.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2013</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2014-01-08</date>
+ <rev>A</rev>
+ <file>ose_intro.xml</file>
+ </header>
+
+ <section>
+ <title>Features</title>
+ </section>
+
+ <section>
+ <title>Starting Erlang/OTP</title>
+ <p>
+ Starting Erlang/OTP on OSE is not as simple as on Unix/Windows (yet).
+ First of all you have to explicitly use the beam (or beam.smp) executables
+ found in erts-X.Y.Z/bin as the load module that you run. This in turn
+ means that you have to supply the raw beam arguments to the emulator
+ when starting. Fortunately <c>erl</c> on Unix/Windows has a
+ undocumented flag called <c>-emu_args_exit</c> that can be used to
+ figure out what the arguments to beam look like. For example:</p>
+ <code># erl +Mut false +A 10 +S 4:4 +Muycs256 +P 2096 +Q 2096 -emu_args_exit
+-Mut
+false
+-A
+10
+-S
+4:4
+-Muycs256
+-P
+2096
+-Q
+2096
+--
+-root
+/usr/local/lib/erlang
+-progname
+erl
+--
+-home
+/home/erlang
+--</code>
+ <p>
+ The arguments are printed on seperate lines to make it possible to know
+ what has to be quoted with &quot;. Each line is one quotable unit.
+ So taking the arguments above you can supply them to pm_create or
+ just execute directly on the command line. For example:</p>
+ <code>rtose@acp3400> pm_install erlang /mst/erlang/erts-6.0/bin/beam.smp
+rtose@acp3400> pm_create -c ARGV="-Mut false -A 10 -S 4:4 -Muycs256 -P 2096 -Q 2099 -- -root /mst/erlang -progname erl -- -home /mst/erlang --" erlang
+pid: 0x110059
+rtose@acp3400> pm_start 0x110059</code>
+ <p>
+ Also note that since we are running erl to figure out the arguments on a
+ seperate machine the paths have to be updated. In the example above
+ <c>/usr/local/lib/erlang</c> was replaced by <c>/mst/erlang/</c>. The
+ goal is to in future releases not have to do the special argument handling
+ but for now (OTP 17.0) you have to do it.
+ </p>
+ <note>
+ Because of a limitation in the way the OSE handles stdio when starting
+ load modules using pm_install/create the Erlang shell only reads every
+ other command from stdin. However if you start Erlang using run_erl
+ you do not have this problem. So it is highly recommended that you
+ start Erlang using run_erl.
+ </note>
+ </section>
+
+ <section>
+ <title>run_erl and to_erl</title>
+ <p>
+ In OSE run_erl and to_erl are combined into a single load module called
+ run_erl_lm. Installing and starting the load module will add two new
+ shell commands called run_erl and to_erl. They work in exactly the same
+ way as the unix variants of run_erl and to_erl, except that the read
+ and write pipes have to be placed under the /pipe vm. One additional
+ option also exists to run_erl on ose:
+ <taglist>
+ <tag><c>-block Name</c></tag>
+ <item>The name of the install handle and block that will be created/used by
+ installing and exectuting the first part of the command. If nothing
+ if given the basename of the load module will be used for this value.
+ Example:
+ <code>pm_install erlang /path/to/erlang/vm/beam.smp
+run_erl -daemon -block erlang /pipe/ /mst/erlang_logs/ "beam.smp -A 1 -- -root /mst/erlang -- -home /mst --"</code>
+ </item>
+ </taglist>
+ The same argument munching as when starting Erlang/OTP without run_erl
+ has to be done. If <c>-daemon</c> is given then all error printouts
+ are sent to the ramlog.
+ See also
+ <seealso marker="erts:run_erl">run_erl</seealso> for more details.
+ </p>
+ <p>
+ Below is an example of how to get started with <c>run_erl_lm</c>.
+ <code>rtose@acp3400> pm_install run_erl_lm /mst/erlang/erts-6.0/bin/run_erl_lm
+rtose@acp3400> pm_create run_erl_lm
+pid: 0x1c005d
+rtose@acp3400> pm_start 0x1c005d
+rtose@acp3400> mkdir /mst/erlang_log
+rtose@acp3400> run_erl -daemon /pipe/ /mst/erlang_log/ "/mst/erlang/erts-6.0/bin/beam.smp -A 1 -- -root /mst/erlang -- -home /mst --"
+rtose@acp3400> to_erl
+Attaching to /pipe/erlang.pipe.1 (^C to exit)
+os:type().
+{ose,release}
+2>
+'to_erl' terminated.</code>
+ Note that Ctrl-C is used instead of Ctrl-D to exit the to_erl shell.
+ </p>
+ </section>
+
+ <section>
+ <title>epmd</title>
+ <p>
+ In OSE epmd will not be started automatically so if you want to use
+ Erlang distribution you have to manually start epmd.
+ </p>
+ </section>
+
+ <section>
+ <title>VM Process Priorities</title>
+ <p>
+ It is possible to set the priorities you want for the OSE processes that
+ thr emulator creates in the lmconf. An example of how to do it can be
+ found in the default lmconf file in
+ $ERL_TOP/erts/emulator/sys/ose/beam.lmconf.
+ </p>
+ </section>
+
+</chapter>
diff --git a/lib/ose/doc/src/ose_signals_chapter.xml b/lib/ose/doc/src/ose_signals_chapter.xml
new file mode 100644
index 0000000000..ff501777cc
--- /dev/null
+++ b/lib/ose/doc/src/ose_signals_chapter.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2013</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>Interacting with Enea OSE</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2014-01-08</date>
+ <rev>A</rev>
+ <file>ose_signals_chapter.xml</file>
+ </header>
+
+ <marker id="introduction"></marker>
+ <section>
+ <title>Introduction</title>
+ <p>The main way which programs on Enea OSE interact is through the
+ usage of message passing, much the same way as Erlang processes
+ communicate. There are two ways in which an Erlang programmer can
+ interact with the signals sent from other Enea OSE processes; either
+ through the provided <c>ose</c> module, or by writing a custom linked-in
+ driver. This User's Guide describes and provides examples for both
+ approaches.
+ </p>
+ </section>
+
+ <marker id="erlang"></marker>
+ <section>
+ <title>Signals in Erlang</title>
+ <p>Erlang/OTP on OSE provides a erlang module called
+ <seealso marker="ose:ose">ose</seealso> that can be used to interact
+ with other OSE processes using message passing. The api in the module
+ is very similar to the native OSE api, so for details of how the
+ functions work please refer to the official OSE documenation. Below
+ is an example usage of the API.
+ </p>
+ <code>1> P1 = ose:open("p1").
+#Port&gt;0.344>
+2> ose:hunt(P1,"p2").
+{#Port&gt;0.344>,1}
+3> P2 = ose:open("p2").
+#Port&gt;0.355>
+4> flush().
+Shell got {mailbox_up,#Port&gt;0.344>,{#Port&gt;0.344>,1},852189}
+ok
+5> ose:listen(P1,[1234]).
+ok
+6> ose:send(P2,ose:get_id(P1),1234,&gt;&gt;"hello">>).
+ok
+7> flush().
+Shell got {message,#Port&gt;0.344>,{852189,1245316,1234,&gt;&gt;"hello">>}}
+ok</code>
+ </section>
+
+ <marker id="driver"></marker>
+ <section>
+ <title>Signals in a Linked-in driver</title>
+ <p>
+ Writing Linked-in drivers for OSE is very similar to how it is done
+ for Unix/Windows. It is only the way in which the driver subscribes
+ and consumed external events that is different. In Unix (and Windows)
+ file descriptiors (and Event Objects) are used to select on. On OSE
+ we use signals to deliver the same functionality. There are two large
+ differences between a signal and an fd.
+ </p>
+ <p>
+ In OSE it is not possible for a signal number to be a unique identifier
+ for a resource in the same way as an fd is. For example; let's say we
+ implement a driver that does an asynchronous hunt that uses signal
+ number 1234 as the hunt_sig. If we want to be able to have multiple
+ hunt ports running at the same time we have to have someway of routing
+ the signal to the correct port. This is achieved by supplying a secondary
+ id that can be retrieved through the meta-data or payload of the signal,
+ e.g:
+ <code>ErlDrvEvent event = erl_drv_ose_event_alloc(1234,port,resolver);</code>
+ The event you get back from
+ <seealso marker="ose_erl_driver#erl_drv_ose_event_alloc">
+ erl_drv_ose_event_alloc</seealso> can then be used by
+ <seealso marker="erts:erl_driver#driver_select">driver_select</seealso>
+ to subscribe to signals. The first argument is just the signal number
+ that we are interested in. The second is the id that we choose to use,
+ in this case the port id that we got in the
+ <seealso marker="erts:driver_entry#start">start</seealso> callback is
+ used. The third argument is a function pointer to a function that can
+ be used to figure out the id from a given signal. The fourth argument can
+ point to any additional data you might want to associate with the event.
+ There is a complete. You can examine the data contained in the event with
+ <seealso marker="ose_erl_driver#erl_drv_ose_event_fetch">erl_drv_ose_event_fetch</seealso>
+ , eg:
+ <code>erl_drv_ose_event_fetch(event, &amp;signal, &amp;port, (void **)&amp;extra);</code>
+ example of what this could look like in
+ <seealso marker="#example">the next section</seealso>.
+ <note>It is very important to issue the driver_select call before
+ any of the signals you are interested in are sent. If driver_select
+ is called after the signal is sent, there is a high probability that it
+ will be lost.</note>
+ </p>
+ <p>
+ The other difference from unix is that in OSE the payload of the event
+ (i.e. the signal data) is already received when the ready_output/input
+ callbacks are called. This means that you access the data of a signal
+ by calling <seealso marker="ose_erl_driver#erl_drv_ose_get_signal">
+ erl_drv_ose_get_signal</seealso>. Additionally multiple signals might be
+ associated with the event, so you should call
+ <seealso marker="ose_erl_driver#erl_drv_ose_get_signal">
+ erl_drv_ose_get_signal</seealso> until <c>NULL</c> is returned.
+ </p>
+ </section>
+
+ <marker id="example"></marker>
+ <section>
+ <title>Example Linked-in driver</title>
+<code>#include "erl_driver.h"
+#include "ose.h"
+
+struct huntsig {
+ SIGSELECT signo;
+ ErlDrvPort port;
+};
+
+union SIGNAL {
+ SIGSELECT signo;
+ struct huntsig;
+}
+
+/* Here we have to get the id from the signal. In this case we use the
+ port id since we have control over the data structure of the signal.
+ It is however possible to use anything in here. The only restriction
+ is that the same id has to be used for all signals of the same number.*/
+ErlDrvOseEventId resolver(union SIGNAL *sig) {
+ return (ErlDrvOseEventId)sig->huntsig.port;
+}
+
+static int drv_init(void) { return 0; };
+
+static ErlDrvData drv_start(ErlDrvPort port, char *command) {
+ return (ErlDrvData)port;
+}
+
+static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen) {
+ ErlDrvPort port = (ErlDrvPort)driver_data;
+
+ /* An example of extra data to associate with the event */
+ char *extra_data = driver_alloc(80);
+ snprintf("extra_data, "Event, sig_no: 1234, and port: %d", port);
+
+ /* Create a new event to select on */
+ ErlDrvOseEvent evt = erl_drv_ose_event_alloc(1234,port,resolver, extra_data);
+
+ /* Make sure to do the select call _BEFORE_ the signal arrives.
+ The signal might get lost if the hunt call is done before the
+ select. */
+ driver_select(port,evt,ERL_DRV_READ|ERL_DRV_USE,1);
+
+ union SIGNAL *sig = alloc(sizeof(union SIGNAL),1234);
+ sig->huntsig.port = port;
+ hunt("testprocess",0,NULL,&amp;sig);
+ return 0;
+}
+
+static void ready_input(ErlDrvData driver_data, ErlDrvEvent evt) {
+ char *extra_data;
+ /* Get the first signal payload from the event */
+ union SIGNAL *sig = erl_drv_ose_get_signal(evt);
+ ErlDrvPort port = (ErlDrvPort)driver_data;
+ while (sig != NULL) {
+ if (sig->signo == 1234) {
+ /* Print out the string we added as the extra parameter */
+ erl_drv_ose_event_fetch(evt, NULL, NULL, (void **)&amp;extra_data);
+ printf("We've received: %s\n", extra_data);
+
+ /* If it is our signal we send a message with the sender of the signal
+ to the controlling erlang process */
+ ErlDrvTermData reply[] = { ERL_DRV_UINT, (ErlDrvUInt)sender(&amp;sig) };
+ erl_drv_send_term(port,reply,sizeof(reply) / sizeof(reply[0]));
+ }
+
+ /* Cleanup the signal and deselect on the event.
+ Note that the event itself has to be free'd in the stop_select
+ callback. */
+ free_buf(&amp;sig);
+ driver_select(port,evt,ERL_DRV_READ|ERL_DRV_USE,0);
+
+ /* There could be more than one signal waiting in this event, so
+ we have to loop until sig == NULL */
+ sig = erl_drv_ose_get_signal(evt);
+ }
+}
+
+static void stop_select(ErlDrvEvent event, void *reserved)
+{
+ /* Free the extra_data */
+ erl_drv_ose_event_fetch(evt, NULL, NULL, (void **)&amp;extra_data);
+ driver_free(extra_data);
+
+ /* Free the event itself */
+ erl_drv_ose_event_free(event);
+}
+
+/**
+ * Setup the driver entry for the Erlang runtime
+ **/
+ErlDrvEntry ose_signal_driver_entry = {
+ .init = drv_init,
+ .start = drv_start,
+ .stop = drv_stop,
+ .ready_input = ready_input,
+ .driver_name = DRIVER_NAME,
+ .control = control,
+ .extended_marker = ERL_DRV_EXTENDED_MARKER,
+ .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION,
+ .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION,
+ .driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING,
+ .stop_select = stop_select
+};
+</code>
+ </section>
+
+</chapter>
diff --git a/lib/ose/doc/src/part.xml b/lib/ose/doc/src/part.xml
new file mode 100644
index 0000000000..250bb11f96
--- /dev/null
+++ b/lib/ose/doc/src/part.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2014</year>
+ <year>2014</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>
+
+ <title>OSE User's Guide</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2014-01-08</date>
+ <rev>1.0</rev>
+ <file>part.xml</file>
+ </header>
+ <description>
+ <p><em>OSE</em>.</p>
+ </description>
+ <xi:include href="ose_intro.xml"/>
+ <xi:include href="ose_signals_chapter.xml"/>
+</part>
diff --git a/lib/ose/doc/src/ref_man.xml b/lib/ose/doc/src/ref_man.xml
new file mode 100644
index 0000000000..54c1182fcb
--- /dev/null
+++ b/lib/ose/doc/src/ref_man.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2014</year><year>2014</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.
+
+ </legalnotice>
+
+ <title>OSE Reference Manual</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2014-01-08</date>
+ <rev>1.0</rev>
+ <file>ref_man.xml</file>
+ </header>
+ <description>
+ <p>The Standard Erlang Libraries application, <em>STDLIB</em>,
+ contains modules for manipulating lists, strings and files etc.</p>
+ <br></br>
+ </description>
+ <xi:include href="ose_app.xml"/>
+ <xi:include href="ose.xml"/>
+ <xi:include href="ose_erl_driver.xml"/>
+</application>
diff --git a/lib/ose/ebin/.gitignore b/lib/ose/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ose/ebin/.gitignore
diff --git a/lib/ose/include/.gitignore b/lib/ose/include/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ose/include/.gitignore
diff --git a/lib/ose/info b/lib/ose/info
new file mode 100644
index 0000000000..85c07dbe82
--- /dev/null
+++ b/lib/ose/info
@@ -0,0 +1,2 @@
+group: misc Miscellaneous Applications
+short: Description of Enea OSE specific functionality
diff --git a/lib/ose/src/Makefile b/lib/ose/src/Makefile
new file mode 100644
index 0000000000..88f89578fb
--- /dev/null
+++ b/lib/ose/src/Makefile
@@ -0,0 +1,106 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2013. 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
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(OSE_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/ose-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES= \
+ ose
+
+HRL_FILES=
+
+INTERNAL_HRL_FILES=
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+APP_FILE= ose.app
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_FILE= ose.appup
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ifeq ($(NATIVE_LIBS_ENABLED),yes)
+ERL_COMPILE_FLAGS += +native
+endif
+ERL_COMPILE_FLAGS += -I../include -I../../kernel/include -Werror
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+ rm -f erl_parse.erl
+
+docs:
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/include"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
+
+release_docs_spec:
+
+# ----------------------------------------------------
+# Dependencies -- alphabetically, please
+# ----------------------------------------------------
diff --git a/lib/ose/src/ose.app.src b/lib/ose/src/ose.app.src
new file mode 100644
index 0000000000..60699c369b
--- /dev/null
+++ b/lib/ose/src/ose.app.src
@@ -0,0 +1,27 @@
+%% This is an -*- erlang -*- file.
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. 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%
+%%
+{application, ose,
+ [{description, "Enea OSE specific modules"},
+ {vsn, "%VSN%"},
+ {modules, [ose]},
+ {registered,[]},
+ {applications, [stdlib,kernel]},
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","erts-6.0"]}]}.
diff --git a/lib/ose/src/ose.appup.src b/lib/ose/src/ose.appup.src
new file mode 100644
index 0000000000..6654efde16
--- /dev/null
+++ b/lib/ose/src/ose.appup.src
@@ -0,0 +1,22 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2013. 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%
+{"%VSN%",
+ [
+ ],
+ [
+ ]}.
diff --git a/lib/ose/src/ose.erl b/lib/ose/src/ose.erl
new file mode 100644
index 0000000000..77f11addf9
--- /dev/null
+++ b/lib/ose/src/ose.erl
@@ -0,0 +1,452 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. 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%
+%%
+%% @doc Interface module for OSE messaging and process monitoring from Erlang
+%%
+%% For each mailbox created through {@link open/1} a OSE phantom process with
+%% that name is started. Since phantom processes are used the memory footprint
+%% of each mailbox is quite small.
+%%
+%% To receive messages you first have to subscribe to the specific message
+%% numbers that you are interested in with {@link listen/2}. The messages
+%% will be sent to the Erlang process that created the mailbox.
+%%
+%% @end
+%%
+-module(ose).
+
+%%==============================================================================
+%% Exported API
+%%==============================================================================
+-export([open/1,
+ close/1,
+ get_id/1,
+ get_name/2,
+ hunt/2,
+ dehunt/2,
+ attach/2,
+ detach/2,
+ send/4,
+ send/5,
+ listen/2
+ ]).
+
+%%==============================================================================
+%% Types
+%%==============================================================================
+-opaque mailbox() :: port().
+%% Mailbox handle. Implemented as an erlang port.
+
+-opaque mailbox_id() :: integer().
+%% Mailbox ID, this is the same as the process id of an OSE process.
+%% An integer.
+
+-type message_number() :: 0..4294967295.
+%% OSE Signal number
+
+-opaque hunt_ref() :: {mailbox(),integer()}.
+%% Reference from a hunt request. This term will be included
+%% in a successful hunt response.
+
+-opaque attach_ref() :: {mailbox(),integer()}.
+%% Reference from an attach request. This term will be included
+%% in the term returned when the attached mailbox disappears.
+
+-export_type([mailbox_id/0,
+ message_number/0,
+ mailbox/0,
+ hunt_ref/0,
+ attach_ref/0]).
+
+%%==============================================================================
+%% Defines
+%%==============================================================================
+-define(DRIVER_NAME, "ose_signal_drv").
+-define(GET_SPID, 1).
+-define(GET_NAME, 2).
+-define(HUNT, 100).
+-define(DEHUNT, 101).
+-define(ATTACH, 102).
+-define(DETACH, 103).
+-define(SEND, 104).
+-define(SEND_W_S, 105).
+-define(LISTEN, 106).
+-define(OPEN, 200).
+
+-define(INT_32BIT(Int),(is_integer(Int) andalso (Int >= 0) andalso (Int < (1 bsl 32)))).
+
+%%==============================================================================
+%% API functions
+%%==============================================================================
+
+%%------------------------------------------------------------------------------
+%% @doc Create a mailbox with the given name and return a port that handles
+%% the mailbox.
+%%
+%% An OSE phantom process with the given name will be created that will send any
+%% messages sent through this mailbox. Any messages sent to the new OSE process
+%% will automatically be converted to an Erlang message and sent to the Erlang
+%% process that calls this function. See {@link listen/2} for details about the
+%% format of the message sent.
+%%
+%% The caller gets linked to the created mailbox.
+%%
+%% raises: `badarg' | `system_limit'
+%%
+%% @see listen/2
+%% @end
+%%------------------------------------------------------------------------------
+-spec open(Name) -> Port when
+ Name :: iodata(),
+ Port :: mailbox().
+open(Name) ->
+ try open_port({spawn_driver,?DRIVER_NAME}, [binary]) of
+ Port ->
+ try port_command(Port,[?OPEN,Name]) of
+ true ->
+ receive
+ {ose_drv_reply,Port,{error,Error}} ->
+ close(Port),
+ erlang:error(Error,[Name]);
+ {ose_drv_reply,Port,ok} ->
+ Port
+ end
+ catch
+ error:badarg -> close(Port),erlang:error(badarg,[Name])
+ end
+ catch
+ error:badarg -> erlang:error(badarg,[Name])
+ end.
+
+%%------------------------------------------------------------------------------
+%% @doc Close a mailbox
+%%
+%% This kills the OSE phantom process associated with this mailbox.
+%%
+%% Will also consume any ``{'EXIT',Port,_}'' message from the port that comes
+%% due to the port closing when the calling process traps exits.
+%%
+%% raises: `badarg'
+%% @end
+%%------------------------------------------------------------------------------
+-spec close(Port) -> ok when
+ Port :: mailbox().
+close(Port) when is_port(Port) ->
+ %% Copied from prim_inet
+ case erlang:process_info(self(), trap_exit) of
+ {trap_exit,true} ->
+ link(Port),
+ catch erlang:port_close(Port),
+ receive {'EXIT',Port,_} -> ok end;
+ {trap_exit,false} ->
+ catch erlang:port_close(Port),
+ ok
+ end;
+close(NotPort) ->
+ erlang:error(badarg,[NotPort]).
+
+%%------------------------------------------------------------------------------
+%% @doc Get the mailbox id for the given port.
+%%
+%% The mailbox id is the same as the OSE process id of the OSE phantom process
+%% that this mailbox represents.
+%%
+%% raises: `badarg'
+%% @end
+%%------------------------------------------------------------------------------
+-spec get_id(Port) -> Pid when
+ Port :: mailbox(),
+ Pid :: mailbox_id().
+get_id(Port) ->
+ try port_control(Port, ?GET_SPID, <<>>) of
+ <<Spid:32>> -> Spid
+ catch error:_Error ->
+ erlang:error(badarg,[Port])
+ end.
+
+%%------------------------------------------------------------------------------
+%% @doc Get the mailbox name for the given mailbox id.
+%%
+%% The mailbox name is the name of the OSE process with process id Pid.
+%%
+%% This call will fail with badarg if the underlying system does not support
+%% getting the name from a process id.
+%%
+%% raises: `badarg'
+%% @end
+%%------------------------------------------------------------------------------
+-spec get_name(Port, Pid) -> Name | undefined when
+ Port :: mailbox(),
+ Pid :: mailbox_id(),
+ Name :: binary().
+get_name(Port, Pid) when ?INT_32BIT(Pid) ->
+ try port_control(Port, ?GET_NAME, <<Pid:32>>) of
+ [] -> undefined;
+ Res -> Res
+ catch error:_Error ->
+ erlang:error(badarg,[Port,Pid])
+ end;
+get_name(Port, Pid) ->
+ erlang:error(badarg,[Port,Pid]).
+
+
+%%------------------------------------------------------------------------------
+%% @doc Hunt for OSE process by name.
+%%
+%% Will send `{mailbox_up, Port, Ref, MboxId}'
+%% to the calling process when the OSE process becomes available.
+%%
+%% Returns a reference term that can be used to cancel the hunt
+%% using {@link dehunt/2}.
+%%
+%% raises: `badarg'
+%%
+%% @end
+%%------------------------------------------------------------------------------
+-spec hunt(Port, HuntPath) -> Ref when
+ Port :: mailbox(),
+ HuntPath :: iodata(),
+ Ref :: hunt_ref().
+hunt(Port, HuntPath) ->
+ try port_command(Port, [?HUNT,HuntPath]) of
+ true ->
+ receive
+ {ose_drv_reply,Port,{error,Error}} ->
+ erlang:error(Error,[Port,HuntPath]);
+ {ose_drv_reply,Port,Ref} ->
+ Ref
+ end
+ catch error:_Error ->
+ erlang:error(badarg,[Port,HuntPath])
+ end.
+
+%%------------------------------------------------------------------------------
+%% @doc Stop hunting for OSE process.
+%%
+%% If a message for this hunt has been sent but not received
+%% by the calling process, it is removed from the message queue.
+%% Note that this only works if the same process that did
+%% the hunt does the dehunt.
+%%
+%% raises: `badarg'
+%%
+%% @see hunt/2
+%% @end
+%%------------------------------------------------------------------------------
+-spec dehunt(Port, Ref) -> ok when
+ Port :: mailbox(),
+ Ref :: hunt_ref().
+dehunt(Port, {Port,Ref}) when ?INT_32BIT(Ref) ->
+ try port_command(Port, <<?DEHUNT:8, Ref:32>>) of
+ true ->
+ receive
+ {ose_drv_reply,Port,{error,enoent}} ->
+ %% enoent could mean that it is in the message queue
+ receive
+ {mailbox_up, Port, {Port,Ref}, _} ->
+ ok
+ after 0 ->
+ ok
+ end;
+ {ose_drv_reply,Port,ok} ->
+ ok
+ end
+ catch error:_Error ->
+ erlang:error(badarg,[Port,{Port,Ref}])
+ end;
+dehunt(Port,Ref) ->
+ erlang:error(badarg,[Port,Ref]).
+
+%%------------------------------------------------------------------------------
+%% @doc Attach to an OSE process.
+%%
+%% Will send `{mailbox_down, Port, Ref, MboxId}'
+%% to the calling process if the OSE process exits.
+%%
+%% Returns a reference that can be used to cancel the attachment
+%% using {@link detach/2}.
+%%
+%% raises: `badarg' | `enomem'
+%%
+%% @end
+%%------------------------------------------------------------------------------
+-spec attach(Port,Pid) -> Ref when
+ Port :: mailbox(),
+ Pid :: mailbox_id(),
+ Ref :: attach_ref().
+attach(Port, Spid) when ?INT_32BIT(Spid) ->
+ try port_command(Port, <<?ATTACH:8, Spid:32>>) of
+ true ->
+ receive
+ {ose_drv_reply,Port,{error,Error}} ->
+ erlang:error(Error,[Port,Spid]);
+ {ose_drv_reply,Port,Ref} ->
+ Ref
+ end
+ catch error:_Error ->
+ erlang:error(badarg,[Port,Spid])
+ end;
+attach(Port,Spid) ->
+ erlang:error(badarg,[Port,Spid]).
+
+
+%%------------------------------------------------------------------------------
+%% @doc Remove attachment to an OSE process.
+%%
+%% If a message for this monitor has been sent but not received
+%% by the calling process, it is removed from the message queue.
+%% Note that this only works of the same process
+%% that did the attach does the detach.
+%%
+%% raises: `badarg'
+%%
+%% @see attach/2
+%% @end
+%%------------------------------------------------------------------------------
+-spec detach(Port,Ref) -> ok when
+ Port :: mailbox(),
+ Ref :: attach_ref().
+detach(Port, {Port,Ref} ) when ?INT_32BIT(Ref) ->
+ try port_command(Port, <<?DETACH:8, Ref:32>>) of
+ true ->
+ receive
+ {ose_drv_reply,Port,{error,enoent}} ->
+ %% enoent could mean that it is in the message queue
+ receive
+ {mailbox_down,Port,{Port,Ref},_} ->
+ ok
+ after 0 ->
+ ok
+ end;
+ {ose_drv_reply,Port,ok} ->
+ ok
+ end
+ catch error:_Error ->
+ erlang:error(badarg,[Port,{Port,Ref}])
+ end;
+detach(Port,Ref) ->
+ erlang:error(badarg,[Port,Ref]).
+
+%%------------------------------------------------------------------------------
+%% @doc Send an OSE message.
+%%
+%% The message is sent from the OSE process' own ID that is: `get_id(Port)'.
+%%
+%% raises: `badarg'
+%%
+%% @see send/5
+%% @end
+%%------------------------------------------------------------------------------
+-spec send(Port,Pid,SigNo,SigData) -> ok when
+ Port :: mailbox(),
+ Pid :: mailbox_id(),
+ SigNo :: message_number(),
+ SigData :: iodata().
+send(Port, Spid, SigNo, SigData) when ?INT_32BIT(Spid), ?INT_32BIT(SigNo) ->
+ try erlang:port_command(Port, [<<?SEND:8, Spid:32, SigNo:32>>, SigData]) of
+ true -> ok
+ catch error:_Error ->
+ erlang:error(badarg,[Port,Spid,SigNo,SigData])
+ end;
+send(Port,Spid,SigNo,SigData) ->
+ erlang:error(badarg,[Port,Spid,SigNo,SigData]).
+
+
+%%------------------------------------------------------------------------------
+%% @doc Send an OSE message with different sender.
+%%
+%% As {@link send/4} but the sender will be `SenderPid'.
+%%
+%% raises: `badarg'
+%%
+%% @see send/4
+%% @end
+%%------------------------------------------------------------------------------
+-spec send(Port,Pid,SenderPid,SigNo,SigData) -> ok when
+ Port :: mailbox(),
+ Pid :: mailbox_id(),
+ SenderPid :: mailbox_id(),
+ SigNo :: message_number(),
+ SigData :: iodata().
+send(Port, Spid, SenderPid, SigNo, SigData)
+ when ?INT_32BIT(Spid), ?INT_32BIT(SenderPid), ?INT_32BIT(SigNo) ->
+ try erlang:port_command(Port, [<<?SEND_W_S:8, Spid:32, SenderPid:32,
+ SigNo:32>>, SigData]) of
+ true -> ok
+ catch error:_Error ->
+ erlang:error(badarg,[Port,Spid,SenderPid,SigNo,SigData])
+ end;
+send(Port,Spid,SenderPid,SigNo,SigData) ->
+ erlang:error(badarg,[Port,Spid,SenderPid,SigNo,SigData]).
+
+%%------------------------------------------------------------------------------
+%% @doc Start listening for specified OSE signal numbers.
+%%
+%% The mailbox will send `{message,Port,{FromMboxId,ToMboxId,MsgNo,MsgData}}'
+%% to the process that created the mailbox when an OSE message with any
+%% of the specified `SigNos' arrives.
+%%
+%% Repeated calls to listen will replace the current set of signal numbers to
+%% listen to. i.e
+%%
+%% ```1>ose:listen(MsgB,[1234,12345]).
+%% ok
+%% 2> ose:listen(MsgB,[1234,123456]).
+%% ok.'''
+%%
+%% The above will first listen for signals with numbers 1234 and 12345, and then
+%% replace that with only listening to 1234 and 123456.
+%%
+%% With the current implementation it is not possible to listen to all signal
+%% numbers.
+%%
+%% raises: `badarg' | `enomem'
+%%
+%% @end
+%%------------------------------------------------------------------------------
+-spec listen(Port, SigNos) -> ok when
+ Port :: mailbox(),
+ SigNos :: list(message_number()).
+listen(Port, SigNos) when is_list(SigNos) ->
+ USSigNos = lists:usort(SigNos),
+ BinSigNos = try
+ << <<SigNo:32>> ||
+ SigNo <- USSigNos,
+ ?INT_32BIT(SigNo) orelse erlang:error(badarg)
+ >>
+ catch _:_ ->
+ erlang:error(badarg,[Port,SigNos])
+ end,
+ try port_command(Port, [?LISTEN, BinSigNos]) of
+ true ->
+ receive
+ {ose_drv_reply,Port,{error,Error}} ->
+ erlang:error(Error,[Port,SigNos]);
+ {ose_drv_reply,Port,Else} ->
+ Else
+ end
+ catch error:_Error ->
+ erlang:error(badarg,[Port,SigNos])
+ end;
+listen(Port, SigNos) ->
+ erlang:error(badarg,[Port,SigNos]).
+
+
+%%%=============================================================================
+%%% Internal functions
+%%%=============================================================================
diff --git a/lib/ose/test/Makefile b/lib/ose/test/Makefile
new file mode 100644
index 0000000000..7e2080ba38
--- /dev/null
+++ b/lib/ose/test/Makefile
@@ -0,0 +1,67 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ ose_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+INSTALL_PROGS= $(TARGET_FILES)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/ose_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \
+ -I$(ERL_TOP)/lib/kernel/include
+
+EBIN = .
+
+EMAKEFILE=Emakefile
+COVERFILE=ose.cover
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) ose.spec $(EMAKEFILE) \
+ $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+ @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+
+release_docs_spec:
diff --git a/lib/ose/test/ose.cover b/lib/ose/test/ose.cover
new file mode 100644
index 0000000000..7b846cfaf6
--- /dev/null
+++ b/lib/ose/test/ose.cover
@@ -0,0 +1,2 @@
+%% -*- erlang -*-
+{incl_app,ose,details}.
diff --git a/lib/ose/test/ose.spec b/lib/ose/test/ose.spec
new file mode 100644
index 0000000000..c897e8cd16
--- /dev/null
+++ b/lib/ose/test/ose.spec
@@ -0,0 +1 @@
+{suites,"../ose_test",all}.
diff --git a/lib/ose/test/ose_SUITE.erl b/lib/ose/test/ose_SUITE.erl
new file mode 100644
index 0000000000..7e81b19894
--- /dev/null
+++ b/lib/ose/test/ose_SUITE.erl
@@ -0,0 +1,765 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2013. 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(ose_SUITE).
+
+%-compile(export_all).
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,init_per_testcase/2,
+ end_per_testcase/2]).
+-export([
+ basic/1,stress/1,multi_msg_numbers/1,multi_mailboxes/1,
+ hunt/1,multi_hunt/1,dehunt/1,multi_dehunt/1,
+ attach/1,multi_attach/1,detach/1,multi_detach/1,
+ open_errors/1,close_errors/1,get_id_errors/1,get_name_errors/1,
+ hunt_errors/1,dehunt_errors/1,attach_errors/1,detach_errors/1,
+ send_errors/1,send_w_s_errors/1,listen_errors/1
+ ]).
+
+-define(INTERFACE,ose).
+
+
+init_per_testcase(_Func, Config) ->
+ Config.
+end_per_testcase(_Func, _Config) ->
+ ok.
+
+suite() -> [{timeout,{30,seconds}}].
+
+all() ->
+ [
+ basic,stress,multi_msg_numbers,multi_mailboxes,
+ hunt,multi_hunt,dehunt,multi_dehunt,
+ attach,multi_attach,detach,multi_detach,
+
+ open_errors,close_errors,get_id_errors,get_name_errors,
+ hunt_errors,dehunt_errors,attach_errors,detach_errors,
+ send_errors,send_w_s_errors,listen_errors
+ ].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ case os:type() of
+ {ose,_} ->
+ Config;
+ _Else ->
+ {skip,"Only run on OSE"}
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+basic(_Config) ->
+
+ [P1,P2] = multi_open(2,[42]),
+ P1Id = ?INTERFACE:get_id(P1),
+ P2Id = ?INTERFACE:get_id(P2),
+
+ ok = ?INTERFACE:send(P2,P1Id,42,<<"ping">>),
+ receive
+ {message,P1,V1} ->
+ {P2Id,P1Id,42,<<"ping">>} = V1,
+ ?INTERFACE:send(P1,P2Id,42,<<"pong">>);
+ Else1 ->
+ ct:fail({got_wrong_message,Else1})
+ end,
+
+ receive
+ {message,P2,V2} ->
+ {P1Id,P2Id,42,<<"pong">>} = V2;
+ Else2 ->
+ ct:fail({got_wrong_message,Else2})
+ end,
+
+ ?INTERFACE:close(P1),
+ ?INTERFACE:close(P2).
+
+%% Send 1000 messages and see if we can cope and that msg order is preserved
+stress(_Config) ->
+
+ Iterations = 1000,
+
+ [P1,P2] = multi_open(2,[42]),
+ P1Id = ?INTERFACE:get_id(P1),
+ P2Id = ?INTERFACE:get_id(P2),
+
+ spawn(fun() ->
+ n(fun(N) ->
+ Msg = [<<"ping">>|integer_to_list(N)],
+ ?INTERFACE:send(P2,P1Id,42,Msg)
+ end,Iterations)
+ end),
+ timer:sleep(100),
+ n(fun(N) ->
+ receive
+ {message,P1,Value} ->
+ Int = integer_to_binary(N),
+ {P2Id,P1Id,42,<<"ping",Int/binary>>} = Value,
+ ok;
+ Else ->
+ ct:fail({got_wrong_message,Else})
+ end
+ end,Iterations),
+
+ ?INTERFACE:close(P1),
+ ?INTERFACE:close(P2).
+
+%% Listen to 1000 different message numbers and send some random messages
+multi_msg_numbers(_Config) ->
+
+ Iterations = 100,
+
+ [P1,P2] = multi_open(2,lists:seq(2000,3000)),
+ P1Id = ?INTERFACE:get_id(P1),
+
+ n(fun(_) ->
+ Num = random:uniform(1000)+2000,
+ ?INTERFACE:send(P2,P1Id,Num,<<"ping",(integer_to_binary(Num))/binary>>)
+ end,Iterations),
+
+ n(fun(_) ->
+ receive
+ {message,P1,{_,_,Id,<<"ping",Num/binary>>}} when Id > 2000;
+ Id =< 3000 ->
+ Id = binary_to_integer(Num),
+ ok;
+ Else ->
+ ct:fail({got_wrong_message,Else})
+ end
+ end,Iterations),
+
+ ?INTERFACE:close(P1),
+ ?INTERFACE:close(P2).
+
+
+%% Create 100 mailboxes and send messages to them
+multi_mailboxes(_Config) ->
+
+ Mailboxes = 100,
+
+ [P1|MBs] = multi_open(Mailboxes,[42]),
+
+ [?INTERFACE:send(P1,?INTERFACE:get_id(P),42,[<<"ping">>,?INTERFACE:get_name(P,?INTERFACE:get_id(P))]) || P <- MBs],
+
+ [receive
+ {message,P,Value} ->
+ Name = ?INTERFACE:get_name(P,?INTERFACE:get_id(P)),
+ {_,_,42,<<"ping",Name/binary>>} = Value,
+ ok
+ end || P <- MBs],
+
+ [?INTERFACE:close(P) || P <- [P1|MBs]],
+ ok.
+
+hunt(_Config) ->
+ [P1,P2] = multi_open(2,[]),
+
+ Ref = ?INTERFACE:hunt(P1,"p2"),
+ receive
+ {mailbox_up,P1,Ref,Pid} ->
+ Pid = ?INTERFACE:get_id(P2),
+ ?INTERFACE:close(P1),
+ ?INTERFACE:close(P2);
+ Else ->
+ ct:fail({got_wrong_message,Else,Ref})
+ end.
+
+multi_hunt(_Config) ->
+
+ Iterations = 100,
+
+ P = ?INTERFACE:open("p"),
+
+ Refs = [?INTERFACE:hunt(P,"p"++integer_to_list(N))|| N <- lists:seq(1,Iterations)],
+
+ Pids = [begin
+ Prt = ?INTERFACE:open("p"++integer_to_list(N)),
+ Pid = ?INTERFACE:get_id(Prt),
+ ?INTERFACE:close(Prt),
+ Pid
+ end || N <- lists:seq(1,Iterations)],
+
+ [receive
+ {mailbox_up,P,Ref,Pid} ->
+ ok
+ after 10 ->
+ ct:fail({did_not_get,Pid,Ref})
+ end || {Pid,Ref} <- lists:zip(Pids,Refs)],
+ ?INTERFACE:close(P).
+
+
+dehunt(_Config) ->
+ [P1] = multi_open(1,[]),
+ Ref = ?INTERFACE:hunt(P1,"p2"),
+ receive
+ _Else -> ct:fail({got,_Else})
+ after 1000 ->
+ ok
+ end,
+ P2 = ?INTERFACE:open("p2"),
+
+ % Make sure any messages are sent
+ receive after 10 -> ok end,
+
+ ok = ?INTERFACE:dehunt(P1,Ref),
+
+ % Make sure no messages are received
+ receive
+ _Else2 -> ct:fail({got,_Else2})
+ after 1000 ->
+ ?INTERFACE:close(P1),
+ ?INTERFACE:close(P2)
+ end.
+
+%%%
+%%% This testcase basically:
+%%% spawn 10 processes that in parallel
+%%% adds some hunts for different OSE processes
+%%% maybe create hunted OSE process
+%%% dehunt half of the hunts
+%%% create more hunts
+%%% if not created create hunted OSE process
+%%% veryify that all expected hunt messages are received
+%%% verify that all processes exited correctly
+%%%
+%%% This complex test is done to make sure that the internal handling
+%%% of dehunt works as expected.
+%%%
+multi_dehunt(_Config) ->
+ [P1] = multi_open(1,[]),
+
+ Scenario =
+ fun(Iterations) ->
+
+ Hunted = "p"++integer_to_list(Iterations),
+ %% Start a couple of hunts
+ Refs = [?INTERFACE:hunt(P1,Hunted) || _ <- lists:seq(1,Iterations)],
+
+ %% We alternate if the process is opened before or after the dehunt
+ P2O = if Iterations rem 2 == 0 ->
+ ?INTERFACE:open(Hunted);
+ true ->
+ undefined
+ end,
+
+ %% Remove half of them
+ {RemRefs,_} = lists:mapfoldl(fun(Ref,Acc) when Acc rem 2 == 0 ->
+ ok = ?INTERFACE:dehunt(P1,Ref),
+ {[],Acc+1};
+ (Ref,Acc) ->
+ {Ref,Acc+1}
+ end,0,Refs),
+
+ %% Add some new ones
+ NewRefs = [?INTERFACE:hunt(P1,Hunted)
+ || _ <- lists:seq(1,Iterations div 4)]
+ ++ lists:flatten(RemRefs),
+
+ P2 = if P2O == undefined ->
+ ?INTERFACE:open(Hunted);
+ true ->
+ P2O
+ end,
+ P2Id = ?INTERFACE:get_id(P2),
+
+ %% Receive all the expected ones
+ lists:foreach(fun(Ref) ->
+ receive
+ {mailbox_up,P1,Ref,P2Id} ->
+ ok
+ after 1000 ->
+ io:format("Flush: ~p~n",[flush()]),
+ io:format("~p~n",[{Iterations,{did_not_get, Ref}}]),
+ ok = Ref
+ end
+ end,NewRefs),
+
+ %% Check that no other have arrived
+ receive
+ _Else ->
+ io:format("Flush: ~p~n",[flush()]),
+ io:format("~p~n",[{Iterations,{got, _Else}}]),
+ ok = _Else
+ after 100 ->
+ ok
+ end,
+ ?INTERFACE:close(P2)
+ end,
+
+ Self = self(),
+
+ n(fun(N) ->
+ spawn(fun() -> Self !
+ Scenario(N*25)
+ end),
+ ok
+ end,10),
+
+ n(fun(_N) ->
+ receive ok -> ok
+ after 60000 -> ct:fail(failed)
+ end
+ end,10),
+ ?INTERFACE:close(P1).
+
+attach(_Config) ->
+ [P1,P2] = multi_open(2,[]),
+
+ P2Id = ?INTERFACE:get_id(P2),
+ Ref = ?INTERFACE:attach(P1,P2Id),
+ ?INTERFACE:close(P2),
+ receive
+ {mailbox_down,P1,Ref,P2Id} ->
+ ?INTERFACE:close(P1);
+ _Else ->
+ ct:fail({got,_Else, {P1,Ref,P2Id}})
+ after 1000 ->
+ ct:fail({did_not_get,P1,Ref,P2Id})
+ end.
+
+multi_attach(_Config) ->
+
+ Iterations = 100,
+
+ [P1|Pids] = multi_open(Iterations,[]),
+
+ Refs = [{?INTERFACE:get_id(Pid),?INTERFACE:attach(P1,?INTERFACE:get_id(Pid))} || Pid <- Pids],
+
+ [?INTERFACE:close(Pid) || Pid <- Pids],
+
+ [receive
+ {mailbox_down,P1,Ref,Pid} ->
+ ok
+ after 10000 ->
+ ct:fail({did_not_get,Pid,Ref})
+ end || {Pid,Ref} <- Refs],
+ ?INTERFACE:close(P1).
+
+detach(_Config) ->
+ [P1,P2] = multi_open(2,[]),
+ P2Id = ?INTERFACE:get_id(P2),
+ Ref = ?INTERFACE:attach(P1,P2Id),
+ receive
+ _Else -> ct:fail({got,_Else})
+ after 100 ->
+ ok
+ end,
+
+ ?INTERFACE:close(P2),
+
+ % Make sure any messages are sent
+ receive after 10 -> ok end,
+
+ ?INTERFACE:detach(P1,Ref),
+
+ % Make sure no messages are received
+ receive
+ _Else2 -> ct:fail({got,_Else2})
+ after 1000 ->
+ ?INTERFACE:close(P1)
+ end.
+
+%%%
+%%% This testcase basically:
+%%% spawn 10 processes that in parallel
+%%% adds some attach for different OSE processes
+%%% maybe close OSE process
+%%% dehunt half of the hunts
+%%% create more hunts
+%%% if not closed close attached OSE process
+%%% veryify that all expected attach messages are received
+%%% verify that all processes exited correctly
+%%%
+%%% This complex test is done to make sure that the internal handling
+%%% of dehunt works as expected.
+%%%
+multi_detach(_Config) ->
+ [P1] = multi_open(1,[]),
+
+ Scenario =
+ fun(Iterations) ->
+
+ Attached = ?INTERFACE:open("p"++integer_to_list(Iterations)),
+ AttachedId = ?INTERFACE:get_id(Attached),
+ %% Start a couple of attachs
+ Refs = [?INTERFACE:attach(P1,AttachedId) || _ <- lists:seq(1,Iterations)],
+
+ %% We alternate if the process is closed before or after the detach
+ P2O = if Iterations rem 2 == 0 ->
+ ?INTERFACE:close(Attached);
+ true ->
+ undefined
+ end,
+
+ %% Remove half of them
+ {RemRefs,_} = lists:mapfoldl(fun(Ref,Acc) when Acc rem 2 == 0 ->
+ ok = ?INTERFACE:detach(P1,Ref),
+ {[],Acc+1};
+ (Ref,Acc) ->
+ {Ref,Acc+1}
+ end,0,Refs),
+
+ %% Add some new ones
+ NewRefs = [?INTERFACE:attach(P1,AttachedId)
+ || _ <- lists:seq(1,Iterations div 4)]
+ ++ lists:flatten(RemRefs),
+
+ if P2O == undefined ->
+ ?INTERFACE:close(Attached);
+ true ->
+ P2O
+ end,
+
+ %% Receive all the expected ones
+ lists:foreach(fun(Ref) ->
+ receive
+ {mailbox_down,P1,Ref,AttachedId} ->
+ ok
+ after 1000 ->
+ io:format("Flush: ~p~n",[flush()]),
+ io:format("~p~n",[{Iterations,{did_not_get, Ref}}]),
+ ok = Ref
+ end
+ end,NewRefs),
+
+ %% Check that no other have arrived
+ receive
+ _Else ->
+ io:format("Flush: ~p~n",[flush()]),
+ io:format("~p~n",[{Iterations,{got, _Else}}]),
+ ok = _Else
+ after 100 ->
+ ok
+ end
+ end,
+
+ Self = self(),
+
+ n(fun(N) ->
+ spawn(fun() -> Self !
+ Scenario(N*5)
+ end),
+ ok
+ end,10),
+
+ n(fun(_N) ->
+ receive ok -> ok
+ after 60000 -> ct:fail(failed)
+ end
+ end,10),
+ ?INTERFACE:close(P1).
+
+
+open_errors(_Config) ->
+ {'EXIT',{badarg,[{?INTERFACE,open,[inval],_}|_]}} =
+ (catch ?INTERFACE:open(inval)),
+ {'EXIT',{badarg,[{?INTERFACE,open,[["p"|1]],_}|_]}} =
+ (catch ?INTERFACE:open(["p"|1])),
+ {'EXIT',{badarg,[{?INTERFACE,open,[["p",1234]],_}|_]}} =
+ (catch ?INTERFACE:open(["p",1234])),
+
+ ok.
+
+close_errors(_Config) ->
+ {'EXIT',{badarg,[{?INTERFACE,close,[inval],_}|_]}} =
+ (catch ?INTERFACE:close(inval)),
+
+ P1 = ?INTERFACE:open("p1"),
+ ok = ?INTERFACE:close(P1),
+ ok = ?INTERFACE:close(P1).
+
+
+get_id_errors(_Config) ->
+ {'EXIT',{badarg,[{?INTERFACE,get_id,[inval],_}|_]}} =
+ (catch ?INTERFACE:get_id(inval)),
+
+ P1 = ?INTERFACE:open("p1"),
+ ok = ?INTERFACE:close(P1),
+ {'EXIT',{badarg,[{?INTERFACE,get_id,[P1],_}|_]}} =
+ (catch ?INTERFACE:get_id(P1)),
+
+ ok.
+
+get_name_errors(_Config) ->
+ P1 = ?INTERFACE:open("p1"),
+ {'EXIT',{badarg,[{?INTERFACE,get_name,[P1,inval],_}|_]}} =
+ (catch ?INTERFACE:get_name(P1,inval)),
+
+ undefined = ?INTERFACE:get_name(P1,1234),
+
+ P2 = ?INTERFACE:open("p2"),
+ P2Id = ?INTERFACE:get_id(P2),
+ ok = ?INTERFACE:close(P1),
+ {'EXIT',{badarg,[{?INTERFACE,get_name,[P1,P2Id],_}|_]}} =
+ (catch ?INTERFACE:get_name(P1,P2Id)),
+ ?INTERFACE:close(P2),
+
+ P3 = ?INTERFACE:open([255]),
+ <<255>> = ?INTERFACE:get_name(P3, ?INTERFACE:get_id(P3)),
+ ?INTERFACE:close(P3),
+
+ ok.
+
+hunt_errors(_Config) ->
+
+ {'EXIT',{badarg,[{?INTERFACE,hunt,[inval,"hello"],_}|_]}} =
+ (catch ?INTERFACE:hunt(inval,"hello")),
+
+ P1 = ?INTERFACE:open("p1"),
+ {'EXIT',{badarg,[{?INTERFACE,hunt,[P1,["hello",12345]],_}|_]}} =
+ (catch ?INTERFACE:hunt(P1,["hello",12345])),
+
+ P2 = ?INTERFACE:open(<<255>>),
+ P2Pid = ?INTERFACE:get_id(P2),
+ Ref = ?INTERFACE:hunt(P1,[255]),
+ receive
+ {mailbox_up,P1,Ref,P2Pid} ->
+ ok;
+ Else ->
+ ct:fail({got,Else,{mailbox_up,P1,Ref,P2Pid}})
+ after 150 ->
+ ct:fail({did_not_get,{mailbox_up,P1,Ref,P2Pid}})
+ end,
+
+ ok = ?INTERFACE:close(P1),
+ ok = ?INTERFACE:close(P2),
+ {'EXIT',{badarg,[{?INTERFACE,hunt,[P1,["hello"]],_}|_]}} =
+ (catch ?INTERFACE:hunt(P1,["hello"])),
+
+ ok.
+
+dehunt_errors(_Config) ->
+ P1 = ?INTERFACE:open("p1"),
+ Ref = ?INTERFACE:hunt(P1,"p2"),
+
+ {'EXIT',{badarg,[{?INTERFACE,dehunt,[inval,Ref],_}|_]}} =
+ (catch ?INTERFACE:dehunt(inval,Ref)),
+
+ {'EXIT',{badarg,[{?INTERFACE,dehunt,[P1,inval],_}|_]}} =
+ (catch ?INTERFACE:dehunt(P1,inval)),
+
+ ok = ?INTERFACE:dehunt(P1,Ref),
+ ok = ?INTERFACE:dehunt(P1,Ref),
+
+ ok = ?INTERFACE:close(P1),
+
+ {'EXIT',{badarg,[{?INTERFACE,dehunt,[P1,Ref],_}|_]}} =
+ (catch ?INTERFACE:dehunt(P1,Ref)),
+
+ case ?INTERFACE of
+ ose -> ok;
+ _ ->
+ P2 = ?INTERFACE:open("p2"),
+ ok = ?INTERFACE:close(P2)
+ end,
+
+ receive
+ Else -> ct:fail({got,Else})
+ after 100 ->
+ ok
+ end.
+
+attach_errors(_Config) ->
+ P1 = ?INTERFACE:open("p1"),
+ P2 = ?INTERFACE:open("p2"),
+ P2Id = ?INTERFACE:get_id(P2),
+
+ {'EXIT',{badarg,[{?INTERFACE,attach,[inval,P2Id],_}|_]}} =
+ (catch ?INTERFACE:attach(inval,P2Id)),
+
+ {'EXIT',{badarg,[{?INTERFACE,attach,[P1,[12345]],_}|_]}} =
+ (catch ?INTERFACE:attach(P1,[12345])),
+
+ ok = ?INTERFACE:close(P1),
+ ok = ?INTERFACE:close(P2),
+ {'EXIT',{badarg,[{?INTERFACE,attach,[P1,P2Id],_}|_]}} =
+ (catch ?INTERFACE:attach(P1,P2Id)),
+
+ ok.
+
+detach_errors(_Config) ->
+ P1 = ?INTERFACE:open("p1"),
+ P2 = ?INTERFACE:open("p2"),
+ P2Id = ?INTERFACE:get_id(P2),
+
+ Ref = ?INTERFACE:attach(P1,P2Id),
+
+ {'EXIT',{badarg,[{?INTERFACE,detach,[inval,Ref],_}|_]}} =
+ (catch ?INTERFACE:detach(inval,Ref)),
+
+ {'EXIT',{badarg,[{?INTERFACE,detach,[P1,inval],_}|_]}} =
+ (catch ?INTERFACE:detach(P1,inval)),
+
+ ok = ?INTERFACE:detach(P1,Ref),
+ ok = ?INTERFACE:detach(P1,Ref),
+
+ case ?INTERFACE of
+ ose -> ok;
+ _ ->
+ ok = ?INTERFACE:close(P1)
+ end,
+
+ ok = ?INTERFACE:close(P2),
+ ok = ?INTERFACE:close(P1),
+
+ {'EXIT',{badarg,[{?INTERFACE,detach,[P1,Ref],_}|_]}} =
+ (catch ?INTERFACE:detach(P1,Ref)),
+
+ receive
+ Else -> ct:fail({got,Else})
+ after 100 ->
+ ok
+ end.
+
+send_errors(_Config) ->
+ P1 = ?INTERFACE:open("p1"),
+ P2 = ?INTERFACE:open("p2"),
+ P2Id = ?INTERFACE:get_id(P2),
+
+ {'EXIT',{badarg,[{?INTERFACE,send,[inval,P2Id,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(inval,P2Id,42,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P1,inval,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P1,inval,42,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P1,P2Id,inval,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P1,P2Id,inval,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P1,P2Id,42,inval],_}|_]}} =
+ (catch ?INTERFACE:send(P1,P2Id,42,inval)),
+
+ ok = ?INTERFACE:close(P2),
+ ok = ?INTERFACE:send(P1,P2Id,42,"hello"),
+ ok = ?INTERFACE:close(P1),
+
+ {'EXIT',{badarg,[{?INTERFACE,send,[P1,P2Id,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P1,P2Id,42,"hello")),
+
+ receive
+ Else -> ct:fail({got,Else})
+ after 100 ->
+ ok
+ end.
+
+send_w_s_errors(_Config) ->
+ P1 = ?INTERFACE:open("p1"),
+ P1Id = ?INTERFACE:get_id(P1),
+ P2 = ?INTERFACE:open("p2"),
+ P2Id = ?INTERFACE:get_id(P2),
+ P3 = ?INTERFACE:open("p3"),
+ P3Id = ?INTERFACE:get_id(P3),
+
+ {'EXIT',{badarg,[{?INTERFACE,send,[inval,P2Id,P1Id,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(inval,P2Id,P1Id,42,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P2,-1,P1Id,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P2,-1,P1Id,42,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P2,P2Id,1 bsl 32,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P2,P2Id,1 bsl 32,42,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P2,P2Id,P1Id,inval,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P2,P2Id,P1Id,inval,"hello")),
+ {'EXIT',{badarg,[{?INTERFACE,send,[P2,P2Id,P1Id,42,inval],_}|_]}} =
+ (catch ?INTERFACE:send(P2,P2Id,P1Id,42,inval)),
+
+ ok = ?INTERFACE:close(P3),
+ ok = ?INTERFACE:send(P2,P3Id,P1Id,42,"hello"),
+
+ ok = ?INTERFACE:close(P1),
+ ok = ?INTERFACE:send(P2,P2Id,P1Id,42,"hello"),
+ ok = ?INTERFACE:close(P2),
+
+ {'EXIT',{badarg,[{?INTERFACE,send,[P1,P2Id,P1Id,42,"hello"],_}|_]}} =
+ (catch ?INTERFACE:send(P1,P2Id,P1Id,42,"hello")),
+
+ receive
+ Else -> ct:fail({got,Else})
+ after 100 ->
+ ok
+ end.
+
+listen_errors(_Config) ->
+
+ P1 = ?INTERFACE:open("p1"),
+ P1Id = ?INTERFACE:get_id(P1),
+
+ {'EXIT',{badarg,[{?INTERFACE,listen,[inval,[42]],_}|_]}} =
+ (catch ?INTERFACE:listen(inval,[42])),
+ {'EXIT',{badarg,[{?INTERFACE,listen,[P1,inval],_}|_]}} =
+ (catch ?INTERFACE:listen(P1,inval)),
+ {'EXIT',{badarg,[{?INTERFACE,listen,[P1,[1 bsl 33]],_}|_]}} =
+ (catch ?INTERFACE:listen(P1,[1 bsl 33])),
+
+ ok = ?INTERFACE:listen(P1,[42,42,42,42,42,42,42,42,42,42,42,42,42]),
+
+ case ?INTERFACE of
+ ose -> ok;
+ _ ->
+ ?INTERFACE:send(P1,P1Id,42,"hello"),
+ timer:sleep(50),
+ ?INTERFACE:listen(P1,[]),
+ ?INTERFACE:send(P1,P1Id,42,"hello2"),
+
+ receive
+ {message,P1,42,"hello"} -> ok
+ end,
+
+ receive
+ Else -> ct:fail({got,Else})
+ after 100 ->
+ ok
+ end
+ end,
+
+ ok = ?INTERFACE:close(P1),
+ {'EXIT',{badarg,[{?INTERFACE,listen,[P1,[42]],_}|_]}} =
+ (catch ?INTERFACE:listen(P1,[42])),
+
+ ok.
+
+%%
+%% Internal functions
+%%
+multi_open(N,ListenNums) ->
+ multi_open(N,ListenNums,[]).
+
+multi_open(0,_,Acc) ->
+ Acc;
+multi_open(N,ListenNums,Acc) ->
+ P = ?INTERFACE:open("p"++integer_to_list(N)),
+ ok = ?INTERFACE:listen(P,ListenNums),
+ multi_open(N-1,ListenNums,[P|Acc]).
+
+n(_F,0) ->
+ ok;
+n(F,N) ->
+ ok = F(N),
+ n(F,N-1).
+
+
+flush() ->
+ receive
+ Msg ->
+ [Msg|flush()]
+ after 0 ->
+ []
+ end.
diff --git a/lib/ose/vsn.mk b/lib/ose/vsn.mk
new file mode 100644
index 0000000000..78ffa4d496
--- /dev/null
+++ b/lib/ose/vsn.mk
@@ -0,0 +1 @@
+OSE_VSN = 1.0
diff --git a/lib/otp_mibs/src/otp_mibs.app.src b/lib/otp_mibs/src/otp_mibs.app.src
index b177af0709..ebc656b0b2 100644
--- a/lib/otp_mibs/src/otp_mibs.app.src
+++ b/lib/otp_mibs/src/otp_mibs.app.src
@@ -23,5 +23,7 @@
{modules, [otp_mib]},
{registered, []},
{applications, [kernel, stdlib, snmp]},
- {env,[]}]}.
+ {env,[]},
+ {runtime_dependencies, ["stdlib-2.0","snmp-4.25.1","mnesia-4.12",
+ "kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/otp_mibs/src/otp_mibs.appup.src b/lib/otp_mibs/src/otp_mibs.appup.src
index 5e99dfe325..fd5ce1e391 100644
--- a/lib/otp_mibs/src/otp_mibs.appup.src
+++ b/lib/otp_mibs/src/otp_mibs.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -15,6 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-
-{"%VSN%",[],[]}.
+{"%VSN%",
+ [{<<".*">>,[{restart_application, otp_mibs}]}],
+ [{<<".*">>,[{restart_application, otp_mibs}]}]
+}.
diff --git a/lib/otp_mibs/test/otp_mibs_SUITE.erl b/lib/otp_mibs/test/otp_mibs_SUITE.erl
index 5fd52ac2ac..5376c54210 100644
--- a/lib/otp_mibs/test/otp_mibs_SUITE.erl
+++ b/lib/otp_mibs/test/otp_mibs_SUITE.erl
@@ -45,7 +45,7 @@
end_per_testcase/2]).
% Test cases must be exported.
--export([nt_basic_types/1, nt_high_reduction_count/1]).
+-export([app/1, appup/1, nt_basic_types/1, nt_high_reduction_count/1]).
-define(TRAP_UDP, 5000).
-define(AGENT_UDP, 4000).
@@ -75,9 +75,10 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}, {require, snmp_mgr_agent, snmp}].
-all() -> [{group, node_table}].
+all() -> [{group, app}, {group, node_table}].
-groups() -> [{node_table, [], [nt_basic_types, nt_high_reduction_count]}].
+groups() -> [{app, [], [app, appup]},
+ {node_table, [], [nt_basic_types, nt_high_reduction_count]}].
init_per_group(_GroupName, Config) -> Config.
@@ -118,6 +119,14 @@ end_per_suite(Config) ->
%% Test cases
%%---------------------------------------------------------------------
+%% Test that the otp_mibs app file is ok
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(otp_mibs).
+
+%% Test that the otp_mibs appup file is ok
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(otp_mibs).
+
nt_basic_types(suite) ->
[];
nt_basic_types(doc) ->
diff --git a/lib/otp_mibs/vsn.mk b/lib/otp_mibs/vsn.mk
index 96d3088224..98db21c132 100644
--- a/lib/otp_mibs/vsn.mk
+++ b/lib/otp_mibs/vsn.mk
@@ -1,4 +1,4 @@
-OTP_MIBS_VSN = 1.0.8
+OTP_MIBS_VSN = 1.0.9
# Note: The branch 'otp_mibs' is defunct as of otp_mibs-1.0.4 and
# should NOT be used again.
diff --git a/lib/parsetools/doc/src/yecc.xml b/lib/parsetools/doc/src/yecc.xml
index 380cac967a..7298e09c2c 100644
--- a/lib/parsetools/doc/src/yecc.xml
+++ b/lib/parsetools/doc/src/yecc.xml
@@ -425,9 +425,9 @@ myparser:parse_and_scan({Mod, Tokenizer, Args}) </code>
Nonterminals E T F.
Terminals '+' '*' '(' ')' number.
Rootsymbol E.
-E -> E '+' T: ['$2', '$1', '$3'].
+E -> E '+' T: {'$2', '$1', '$3'}.
E -> T : '$1'.
-T -> T '*' F: ['$2', '$1', '$3'].
+T -> T '*' F: {'$2', '$1', '$3'}.
T -> F : '$1'.
F -> '(' E ')' : '$2'.
F -> number : '$1'. </code>
@@ -438,8 +438,8 @@ Terminals '+' '*' '(' ')' number.
Rootsymbol E.
Left 100 '+'.
Left 200 '*'.
-E -> E '+' E : ['$2', '$1', '$3'].
-E -> E '*' E : ['$2', '$1', '$3'].
+E -> E '+' E : {'$2', '$1', '$3'}.
+E -> E '*' E : {'$2', '$1', '$3'}.
E -> '(' E ')' : '$2'.
E -> number : '$1'. </code>
<p>3. An overloaded minus operator:</p>
diff --git a/lib/parsetools/src/parsetools.app.src b/lib/parsetools/src/parsetools.app.src
index af62fc4f6b..9eeb8fcc05 100644
--- a/lib/parsetools/src/parsetools.app.src
+++ b/lib/parsetools/src/parsetools.app.src
@@ -11,7 +11,8 @@
{applications, [kernel,stdlib]},
{env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]}
]
- }
+ },
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]
}.
diff --git a/lib/parsetools/src/parsetools.appup.src b/lib/parsetools/src/parsetools.appup.src
index 54a63833e6..0e02099893 100644
--- a/lib/parsetools/src/parsetools.appup.src
+++ b/lib/parsetools/src/parsetools.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, parsetools}]}],
+ [{<<".*">>,[{restart_application, parsetools}]}]
+}.
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index b698beb558..f4657663e6 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -423,7 +423,7 @@ infile(Parent, Infilex, Options) ->
end,
case {St#yecc.errors, werror(St)} of
{[], false} -> ok;
- _ -> _ = file:delete(St#yecc.outfile)
+ _ -> _ = file:delete(St#yecc.outfile), ok
end,
Parent ! {self(), yecc_ret(St)}.
diff --git a/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile
index 6455f6ade7..7c7cc13965 100644
--- a/lib/parsetools/test/Makefile
+++ b/lib/parsetools/test/Makefile
@@ -20,6 +20,7 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
+ app_SUITE \
leex_SUITE \
yecc_SUITE
diff --git a/lib/parsetools/test/app_SUITE.erl b/lib/parsetools/test/app_SUITE.erl
new file mode 100644
index 0000000000..88ac95e311
--- /dev/null
+++ b/lib/parsetools/test/app_SUITE.erl
@@ -0,0 +1,50 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(app_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [app, appup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the parsetools app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(parsetools).
+
+appup() ->
+ [{doc, "Test that the parsetools appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(parsetools).
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index d62962c54a..8fd7422c1c 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.0.10
+PARSETOOLS_VSN = 2.0.11
diff --git a/lib/percept/src/percept.app.src b/lib/percept/src/percept.app.src
index cf4a9fc438..f8991ee577 100644
--- a/lib/percept/src/percept.app.src
+++ b/lib/percept/src/percept.app.src
@@ -35,7 +35,9 @@
]},
{registered, [percept_db,percept_port]},
{applications, [kernel,stdlib]},
- {env,[]}
+ {env,[]},
+ {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","kernel-3.0",
+ "inets-5.10","erts-6.0"]}
]}.
diff --git a/lib/percept/src/percept.appup.src b/lib/percept/src/percept.appup.src
index 4fc2852878..23e67f772f 100644
--- a/lib/percept/src/percept.appup.src
+++ b/lib/percept/src/percept.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -15,7 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-
-{"%VSN%",[],[]}.
-
+{"%VSN%",
+ [{<<".*">>,[{restart_application, percept}]}],
+ [{<<".*">>,[{restart_application, percept}]}]
+}.
diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl
index e415d92a04..aea2462b2e 100644
--- a/lib/percept/test/percept_SUITE.erl
+++ b/lib/percept/test/percept_SUITE.erl
@@ -27,6 +27,8 @@
%% Test cases
-export([
+ app/1,
+ appup/1,
profile/1,
analyze/1,
analyze_dist/1,
@@ -54,7 +56,7 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [webserver, profile, analyze, analyze_dist].
+ [app, appup, webserver, profile, analyze, analyze_dist].
groups() ->
[].
@@ -70,6 +72,14 @@ end_per_group(_GroupName, Config) ->
%% Tests
%%----------------------------------------------------------------------
+%% Test that the percept app file is ok
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(percept).
+
+%% Test that the percept appup file is ok
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(percept).
+
webserver(suite) ->
[];
webserver(doc) ->
diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk
index 99729c11e2..935a9d1336 100644
--- a/lib/percept/vsn.mk
+++ b/lib/percept/vsn.mk
@@ -1 +1 @@
-PERCEPT_VSN = 0.8.8.2
+PERCEPT_VSN = 0.8.9
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index bd19d0e434..fc3479cb64 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -95,7 +95,7 @@
crypto:rand_bytes(8)} | 'PBES2-params'}</code></p>
<p><code>public_key() = rsa_public_key() | dsa_public_key() | ec_public_key()</code></p>
- <p><code>private_key() = rsa_public_key() | dsa_public_key() | ec_public_key()</code></p>
+ <p><code>private_key() = rsa_private_key() | dsa_private_key() | ec_private_key()</code></p>
<p><code>rsa_public_key() = #'RSAPublicKey'{}</code></p>
<p><code>rsa_private_key() = #'RSAPrivateKey'{}</code></p>
diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl
index 8afc841fa6..c6394115e3 100644
--- a/lib/public_key/include/public_key.hrl
+++ b/lib/public_key/include/public_key.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -76,7 +76,6 @@
point
}).
-
-define(unspecified, 0).
-define(keyCompromise, 1).
-define(cACompromise, 2).
@@ -88,23 +87,4 @@
-define(privilegeWithdrawn, 9).
-define(aACompromise, 10).
--type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key().
--type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key().
--type rsa_public_key() :: #'RSAPublicKey'{}.
--type rsa_private_key() :: #'RSAPrivateKey'{}.
--type dsa_private_key() :: #'DSAPrivateKey'{}.
--type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
--type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}}.
--type ec_private_key() :: #'ECPrivateKey'{}.
--type der_encoded() :: binary().
--type decrypt_der() :: binary().
--type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
- | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
- | 'SubjectPublicKeyInfo' | 'CertificationRequest' | 'CertificateList'.
--type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
- not_encrypted | {Cipher :: string(), Salt :: binary()}}.
--type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
--type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
- auth_keys.
-
-endif. % -ifdef(public_key).
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index dc8d68c78f..ae517ca642 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -319,6 +319,8 @@ verify_fun(Otpcert, Result, UserState0, VerifyFun) ->
%%
%% Description: Extracts a specific extension from a list of extensions.
%%--------------------------------------------------------------------
+select_extension(_, asn1_NOVALUE) ->
+ undefined;
select_extension(_, []) ->
undefined;
select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
@@ -342,8 +344,11 @@ match_name(uniformResourceIdentifier, URI, [PermittedName | Rest]) ->
incomplete ->
false;
{_, _, Host, _, _} ->
- match_name(fun is_valid_host_or_domain/2, Host,
- PermittedName, Rest)
+ PN = case split_uri(PermittedName) of
+ {_, _, PNhost, _, _} -> PNhost;
+ _X -> PermittedName
+ end,
+ match_name(fun is_valid_host_or_domain/2, Host, PN, Rest)
end;
match_name(emailAddress, Name, [PermittedName | Rest]) ->
@@ -511,10 +516,10 @@ is_dir_name2(Value, Value) -> true;
is_dir_name2({printableString, Value1}, {printableString, Value2}) ->
string:to_lower(strip_spaces(Value1)) =:=
string:to_lower(strip_spaces(Value2));
-is_dir_name2({utf8String, Value1}, String) -> %% BUGBUG FIX UTF8 conv
- is_dir_name2({printableString, binary_to_list(Value1)}, String);
-is_dir_name2(String, {utf8String, Value1}) -> %% BUGBUG FIX UTF8 conv
- is_dir_name2(String, {printableString, binary_to_list(Value1)});
+is_dir_name2({utf8String, Value1}, String) ->
+ is_dir_name2({printableString, unicode:characters_to_list(Value1)}, String);
+is_dir_name2(String, {utf8String, Value1}) ->
+ is_dir_name2(String, {printableString, unicode:characters_to_list(Value1)});
is_dir_name2(_, _) ->
false.
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index f7a361d5a8..9a8e49f265 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -99,7 +99,7 @@ transform(Other,_) ->
Other.
%%--------------------------------------------------------------------
--spec supportedPublicKeyAlgorithms(Oid::tuple()) -> asn1_type().
+-spec supportedPublicKeyAlgorithms(Oid::tuple()) -> public_key:asn1_type().
%%
%% Description: Returns the public key type for an algorithm
%% identifier tuple as found in SubjectPublicKeyInfo.
diff --git a/lib/public_key/src/pubkey_crl.erl b/lib/public_key/src/pubkey_crl.erl
index eaba5bfa1b..f0df4bc3f2 100644
--- a/lib/public_key/src/pubkey_crl.erl
+++ b/lib/public_key/src/pubkey_crl.erl
@@ -39,7 +39,13 @@ validate(OtpCert, OtherDPCRLs, DP, {DerCRL, CRL}, {DerDeltaCRL, DeltaCRL},
CertIssuer = TBSCert#'OTPTBSCertificate'.issuer,
TBSCRL = CRL#'CertificateList'.tbsCertList,
CRLIssuer = TBSCRL#'TBSCertList'.issuer,
- AltNames = subject_alt_names(TBSCert#'OTPTBSCertificate'.extensions),
+ AltNames = case pubkey_cert:select_extension(?'id-ce-subjectAltName',
+ TBSCert#'OTPTBSCertificate'.extensions) of
+ undefined ->
+ [];
+ Ext ->
+ Ext#'Extension'.extnValue
+ end,
revoked_status(DP, IDP, {directoryName, CRLIssuer},
[ {directoryName, CertIssuer} | AltNames], SerialNumber, Revoked,
DeltaRevoked, RevokedState1);
@@ -397,16 +403,18 @@ verify_dp_name(IDPNames, DPorIssuerNames) ->
match_one([], _) ->
false;
match_one([{Type, Name} | Names], CandidateNames) ->
- Candidates = [NameName || {NameType, NameName} <- CandidateNames, NameType == Type],
+ Candidates = [NameName || {NameType, NameName} <- CandidateNames,
+ NameType == Type],
case Candidates of
[] ->
false;
- [_|_] -> case pubkey_cert:match_name(Type, Name, Candidates) of
- true ->
- true;
- false ->
- match_one(Names, CandidateNames)
- end
+ [_|_] ->
+ case pubkey_cert:match_name(Type, Name, Candidates) of
+ true ->
+ true;
+ false ->
+ match_one(Names, CandidateNames)
+ end
end.
verify_dp_bools(TBSCert, IDP) ->
@@ -664,6 +672,8 @@ verify_extensions([#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions =
verify_extensions(pubkey_cert:extensions_list(Ext)) and verify_extensions(Rest);
verify_extensions([]) ->
true;
+verify_extensions(asn1_NOVALUE) ->
+ true;
verify_extensions([#'Extension'{critical = true, extnID = Id} | Rest]) ->
case lists:member(Id, [?'id-ce-authorityKeyIdentifier',
?'id-ce-issuerAltName',
@@ -689,13 +699,3 @@ authority_key_identifier(Extensions) ->
Enc = extension_value(?'id-ce-authorityKeyIdentifier',
'AuthorityKeyIdentifier', Extensions),
pubkey_cert_records:transform(Enc, decode).
-
-subject_alt_names(Extensions) ->
- Enc = extension_value(?'id-ce-subjectAltName',
- 'GeneralNames', Extensions),
- case Enc of
- undefined ->
- [];
- _ ->
- pubkey_cert_records:transform(Enc, decode)
- end.
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index c0a0de815b..3a1653d989 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -51,7 +51,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec decode(binary()) -> [pem_entry()].
+-spec decode(binary()) -> [public_key:pem_entry()].
%%
%% Description: Decodes a PEM binary.
%%--------------------------------------------------------------------
@@ -59,7 +59,7 @@ decode(Bin) ->
decode_pem_entries(split_bin(Bin), []).
%%--------------------------------------------------------------------
--spec encode([pem_entry()]) -> iolist().
+-spec encode([public_key:pem_entry()]) -> iolist().
%%
%% Description: Encodes a list of PEM entries.
%%--------------------------------------------------------------------
@@ -67,7 +67,7 @@ encode(PemEntries) ->
encode_pem_entries(PemEntries).
%%--------------------------------------------------------------------
--spec decipher({pki_asn1_type(), DerEncrypted::binary(),
+-spec decipher({public_key:pki_asn1_type(), DerEncrypted::binary(),
{Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}}},
string()) -> Der::binary().
%%
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index 41280b9e14..0522ea6ac3 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -35,7 +35,8 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
+-spec decode(binary(), public_key | public_key:ssh_file()) ->
+ [{public_key:public_key(), Attributes::list()}].
%%
%% Description: Decodes a ssh file-binary.
%%--------------------------------------------------------------------
@@ -52,7 +53,7 @@ decode(Bin, Type) ->
openssh_decode(Bin, Type).
%%--------------------------------------------------------------------
--spec encode([{public_key(), Attributes::list()}], ssh_file()) ->
+-spec encode([{public_key:public_key(), Attributes::list()}], public_key:ssh_file()) ->
binary().
%%
%% Description: Encodes a list of ssh file entries.
diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src
index 736a778a4b..88ef07c5a6 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -13,7 +13,9 @@
]},
{applications, [asn1, crypto, kernel, stdlib]},
{registered, []},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0","crypto-3.3",
+ "asn1-3.0"]}
]
}.
diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src
index aacd3b866d..5278732c87 100644
--- a/lib/public_key/src/public_key.appup.src
+++ b/lib/public_key/src/public_key.appup.src
@@ -1,8 +1,21 @@
%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
{"%VSN%",
- [
- {<<"0\\.*">>, [{restart_application, public_key}]}
- ],
- [
- {<<"0\\.*">>, [{restart_application, public_key}]}
- ]}.
+ [{<<".*">>,[{restart_application, public_key}]}],
+ [{<<".*">>,[{restart_application, public_key}]}]
+}.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index ceecbcc7f2..a732455aa7 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -49,6 +49,27 @@
pkix_crls_validate/3
]).
+-export_type([public_key/0, private_key/0, pem_entry/0,
+ pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0]).
+
+-type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key().
+-type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key().
+
+-type rsa_public_key() :: #'RSAPublicKey'{}.
+-type rsa_private_key() :: #'RSAPrivateKey'{}.
+-type dsa_private_key() :: #'DSAPrivateKey'{}.
+-type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
+-type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}}.
+-type ec_private_key() :: #'ECPrivateKey'{}.
+-type der_encoded() :: binary().
+-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
+ | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
+ | 'SubjectPublicKeyInfo' | 'CertificationRequest' | 'CertificateList'.
+-type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
+ not_encrypted | {Cipher :: string(), Salt :: binary()}}.
+-type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
+-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
+ auth_keys.
-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
| 'rsa_no_padding'.
-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index d3e9bf7cf6..163f5f4413 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -36,7 +36,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app,
+ [app, appup,
{group, pem_decode_encode},
{group, ssh_public_key_decode_encode},
encrypt_decrypt,
@@ -95,6 +95,13 @@ app(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the public_key appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(public_key).
+
+%%--------------------------------------------------------------------
+
dsa_pem() ->
[{doc, "DSA PEM-file decode/encode"}].
dsa_pem(Config) when is_list(Config) ->
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index 3473757c5f..f0450918aa 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.21
+PUBLIC_KEY_VSN = 0.22
diff --git a/lib/reltool/src/reltool.app.src b/lib/reltool/src/reltool.app.src
index 4188f341f1..65fcf4aae5 100644
--- a/lib/reltool/src/reltool.app.src
+++ b/lib/reltool/src/reltool.app.src
@@ -34,5 +34,7 @@
]},
{registered, []},
{applications, [stdlib, kernel]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","tools-2.6.14","stdlib-2.0","sasl-2.4",
+ "kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/reltool/src/reltool.appup.src b/lib/reltool/src/reltool.appup.src
index c02edd2afb..79ecdbd392 100644
--- a/lib/reltool/src/reltool.appup.src
+++ b/lib/reltool/src/reltool.appup.src
@@ -1,8 +1,7 @@
-%% This is an -*- erlang -*- file.
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -16,7 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-
{"%VSN%",
- [ ]
+ [{<<".*">>,[{restart_application, reltool}]}],
+ [{<<".*">>,[{restart_application, reltool}]}]
}.
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index f0d8b38519..56161a152a 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -164,7 +164,8 @@
applications = [] :: [app_name()],
env = [] :: [{atom(), term()}],
mod = undefined :: {mod_name(), [term()]} | undefined,
- start_phases = undefined :: [{atom(), term()}] | undefined
+ start_phases = undefined :: [{atom(), term()}] | undefined,
+ runtime_dependencies = [] :: [string()]
}).
-record(regexp, {source, compiled}).
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 97785ca7f8..98eeed5c27 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1125,6 +1125,9 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) ->
start_phases ->
parse_app_info(File, KeyVals, AI#app_info{start_phases = Val},
Status);
+ runtime_dependencies ->
+ parse_app_info(File, KeyVals, AI#app_info{runtime_dependencies = Val},
+ Status);
_ ->
Status2 =
reltool_utils:add_warning("Unexpected item ~p in app file ~tp.",
diff --git a/lib/reltool/test/reltool_app_SUITE.erl b/lib/reltool/test/reltool_app_SUITE.erl
index a6e00cde08..9abc7fea41 100644
--- a/lib/reltool/test/reltool_app_SUITE.erl
+++ b/lib/reltool/test/reltool_app_SUITE.erl
@@ -26,6 +26,7 @@
-compile(export_all).
-include("reltool_test_lib.hrl").
+-include_lib("common_test/include/ct.hrl").
t() -> reltool_test_lib:t(?MODULE).
@@ -64,7 +65,7 @@ end_per_testcase(Func,Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [fields, modules, export_all, app_depend, undef_funcs].
+ [fields, modules, export_all, app_depend, undef_funcs, appup].
groups() ->
[].
@@ -290,3 +291,9 @@ key1search(Key, L) ->
{value, {Key, Value}} ->
Value
end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Test that the reltool appup file is ok
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(reltool).
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 16ec570d22..163b77dfa0 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.6.4.1
+RELTOOL_VSN = 0.6.5
diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in
index 2bcb93b4dd..d46b4997f7 100644
--- a/lib/runtime_tools/c_src/Makefile.in
+++ b/lib/runtime_tools/c_src/Makefile.in
@@ -101,7 +101,12 @@ endif
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
+ifneq ($(findstring ose,$(TARGET)),ose)
debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(NIF_LIB)
+else
+# We do not build this on OSE
+debug opt valgrind:
+endif
DYNTRACE_OBJS = $(before_DTrace_OBJS)
@@ -153,8 +158,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/obj"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/lib"
+ifneq ($(findstring ose,$(TARGET)),ose)
$(INSTALL_PROGRAM) $(DYNTRACE_OBJS) "$(RELSYSDIR)/priv/obj"
$(INSTALL_PROGRAM) $(NIF_LIB) $(SOLIBS) "$(RELSYSDIR)/priv/lib"
+endif
release_docs_spec:
diff --git a/lib/runtime_tools/doc/specs/.gitignore b/lib/runtime_tools/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/runtime_tools/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index 51d93df418..07c63197e9 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -40,7 +40,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml
+XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml system_information.xml
XML_REF6_FILES = runtime_tools_app.xml
XML_PART_FILES = part_notes.xml part_notes_history.xml part.xml
@@ -71,12 +71,20 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
DVIPS_FLAGS +=
+SPECS_ESRC = ../../src
+
+SPECS_FLAGS = -I../../include -I../../../kernel/src
+
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
diff --git a/lib/runtime_tools/doc/src/ref_man.xml b/lib/runtime_tools/doc/src/ref_man.xml
index 6017f3cdaa..25fa97896b 100644
--- a/lib/runtime_tools/doc/src/ref_man.xml
+++ b/lib/runtime_tools/doc/src/ref_man.xml
@@ -35,5 +35,6 @@
<xi:include href="dbg.xml"/>
<xi:include href="dyntrace.xml"/>
<xi:include href="erts_alloc_config.xml"/>
+ <xi:include href="system_information.xml"/>
</application>
diff --git a/lib/runtime_tools/doc/src/specs.xml b/lib/runtime_tools/doc/src/specs.xml
new file mode 100644
index 0000000000..d4c3c9dfe6
--- /dev/null
+++ b/lib/runtime_tools/doc/src/specs.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_system_information.xml"/>
+</specs>
diff --git a/lib/runtime_tools/doc/src/system_information.xml b/lib/runtime_tools/doc/src/system_information.xml
new file mode 100644
index 0000000000..b586334ae7
--- /dev/null
+++ b/lib/runtime_tools/doc/src/system_information.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2014</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.
+
+ </legalnotice>
+
+ <title></title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>system_information.xml</file>
+ </header>
+ <module>system_information</module>
+ <modulesummary>System Information</modulesummary>
+ <description>
+ <p></p>
+ </description>
+ <funcs>
+ <func>
+ <name name="sanity_check" arity="0"/>
+ <fsummary>Perform a sanity check</fsummary>
+ <desc>
+ <p>Performs a sanity check on the system. If no issues
+ were found, <c>ok</c> is returned. If issues were
+ found, <c>{failed, <anno>Failures</anno>}</c> is
+ returned. All failures found will be part of the
+ <c><anno>Failures</anno></c> list. Currently defined
+ <c><anno>Failure</anno></c> elements in the
+ <c><anno>Failures</anno></c> list:</p>
+ <taglist>
+ <tag><c><anno>InvalidAppFile</anno></c></tag>
+ <item><p>An application has an invalid <c>.app</c> file. The
+ second element identifies the application which has the
+ invalid <c>.app</c> file.</p></item>
+ <tag><c><anno>InvalidApplicationVersion</anno></c></tag>
+ <item><p>An application has an invalid application version.
+ The second element identifies the application version that
+ is invalid.</p></item>
+ <tag><c><anno>MissingRuntimeDependencies</anno></c></tag>
+ <item><p>An application is missing
+ <seealso marker="kernel:app#runtime_dependencies">runtime
+ dependencies</seealso>. The second element identifies the
+ application (with version) that has missing dependencies.
+ The third element contains the missing dependencies.</p>
+ <p>Note that this check use application versions that
+ are loaded, or will be loaded when used. You might have
+ application versions that satisfies all dependencies
+ installed in the system, but if those are not loaded this
+ check will fail. The system will of course also fail when
+ used like this. This may happen when you have multiple
+ <seealso marker="doc/system_principles:versions">branched
+ versions</seealso> of the same application installed in the
+ system, but you do not use a
+ <seealso marker="doc/system_principles:system_principles#BOOTSCRIPT">boot
+ script</seealso> identifing the correct application version.</p>
+ </item>
+ </taglist>
+ <p>Currently the sanity check is limited to verifying
+ runtime dependencies found in the <c>.app</c> files of
+ all applications. More checks will be introduced in the
+ future. This implies that the return type <em>will</em>
+ change in the future.</p>
+ <note><p>An <c>ok</c> return value only means that
+ <c>sanity_check/0</c> did not find any issues, <em>not</em>
+ that no issues exist.</p></note>
+ </desc>
+ </func>
+ <func>
+ <name name="to_file" arity="1"/>
+ <fsummary>Write miscellaneous system information to file</fsummary>
+ <desc><p>Writes miscellaneous system information to file. This
+ information will typically be requested by the Erlang/OTP team
+ at Ericsson AB when reporting an issue.</p></desc>
+ </func>
+ </funcs>
+ </erlref>
+
diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl
index 284e88d4a7..b9a26dc0dc 100644
--- a/lib/runtime_tools/src/erts_alloc_config.erl
+++ b/lib/runtime_tools/src/erts_alloc_config.erl
@@ -39,6 +39,8 @@
need_config_change,
alloc_util,
instances,
+ strategy,
+ acul,
low_mbc_blocks_size,
high_mbc_blocks_size,
sbct,
@@ -54,8 +56,6 @@
-define(SERVER, '__erts_alloc_config__').
--define(MAX_ALLOCATOR_INSTANCES, 16).
-
-define(KB, 1024).
-define(MB, 1048576).
@@ -99,23 +99,11 @@
{ets_alloc, 131072},
{fix_alloc, 131072},
{eheap_alloc, 524288},
- {ll_alloc, 2097152},
+ {ll_alloc, 131072},
{sl_alloc, 131072},
{temp_alloc, 131072},
{driver_alloc, 131072}]).
--define(MMMBC_DEFAULTS,
- [{binary_alloc, 10},
- {std_alloc, 10},
- {ets_alloc, 10},
- {fix_alloc, 10},
- {eheap_alloc, 10},
- {ll_alloc, 0},
- {sl_alloc, 10},
- {temp_alloc, 10},
- {driver_alloc, 10}]).
-
-
%%%
%%% Exported interface
%%%
@@ -230,20 +218,72 @@ server_loop(State) ->
end,
server_loop(NewState).
-allocator_instances(temp_alloc) ->
- erlang:system_info(schedulers) + 1;
-allocator_instances(ll_alloc) ->
+carrier_migration_support(aoff) ->
+ true;
+carrier_migration_support(aoffcbf) ->
+ true;
+carrier_migration_support(aoffcaobf) ->
+ true;
+carrier_migration_support(_) ->
+ false.
+
+allocator_instances(ll_alloc, Strategy) ->
+ case carrier_migration_support(Strategy) of
+ true -> erlang:system_info(schedulers);
+ false -> 1
+ end;
+allocator_instances(_A, undefined) ->
1;
-allocator_instances(_Allocator) ->
- case erlang:system_info(schedulers) of
- Schdlrs when Schdlrs =< ?MAX_ALLOCATOR_INSTANCES -> Schdlrs;
- _Schdlrs -> ?MAX_ALLOCATOR_INSTANCES
+allocator_instances(_A, _Strategy) ->
+ erlang:system_info(schedulers).
+
+strategy(temp_alloc, _AI) ->
+ af;
+strategy(A, AI) ->
+ try
+ {A, OptList} = lists:keyfind(A, 1, AI),
+ {as, S} = lists:keyfind(as, 1, OptList),
+ S
+ catch
+ _ : _ ->
+ undefined
+ end.
+
+strategy_str(af) ->
+ "A fit";
+strategy_str(gf) ->
+ "Good fit";
+strategy_str(bf) ->
+ "Best fit";
+strategy_str(aobf) ->
+ "Address order best fit";
+strategy_str(aoff) ->
+ "Address order first fit";
+strategy_str(aoffcbf) ->
+ "Address order first fit carrier best fit";
+strategy_str(aoffcaobf) ->
+ "Address order first fit carrier adress order best fit".
+
+default_acul(A, S) ->
+ case carrier_migration_support(S) of
+ false ->
+ 0;
+ true ->
+ case A of
+ ll_alloc -> 85;
+ eheap_alloc -> 45;
+ _ -> 60
+ end
end.
-
+
make_state() ->
+ {_, _, _, AI} = erlang:system_info(allocator),
#state{alloc = lists:map(fun (A) ->
+ S = strategy(A, AI),
#alloc{name = A,
- instances = allocator_instances(A)}
+ strategy = S,
+ acul = default_acul(A, S),
+ instances = allocator_instances(A, S)}
end,
?ALLOCATORS)}.
@@ -345,7 +385,7 @@ do_save_scenario(AlcList) ->
conf_size(Bytes) when is_integer(Bytes), Bytes < 0 ->
exit({bad_value, Bytes});
conf_size(Bytes) when is_integer(Bytes), Bytes < 1*?MB ->
- ?ROUNDUP(?B2KB(Bytes), 128);
+ ?ROUNDUP(?B2KB(Bytes), 256);
conf_size(Bytes) when is_integer(Bytes), Bytes < 10*?MB ->
?ROUNDUP(?B2KB(Bytes), ?B2KB(1*?MB));
conf_size(Bytes) when is_integer(Bytes), Bytes < 100*?MB ->
@@ -376,28 +416,25 @@ mmbcs(#conf{format_to = FTO},
temp_alloc -> BlocksSize;
_ -> BlocksSize div Insts
end,
- case BS > default_mmbcs(A, Insts) of
- true ->
+ DefMMBCS = default_mmbcs(A, Insts),
+ case {Insts, BS > DefMMBCS} of
+ {1, true} ->
MMBCS = conf_size(BS),
fc(FTO, "Main mbc size of ~p kilobytes.", [MMBCS]),
format(FTO, " +M~cmmbcs ~p~n", [alloc_char(A), MMBCS]);
- false ->
+ _ ->
+ MMBCS = ?B2KB(DefMMBCS),
+ fc(FTO, "Main mbc size of ~p kilobytes.", [MMBCS]),
+ format(FTO, " +M~cmmbcs ~p~n", [alloc_char(A), MMBCS]),
ok
end.
-smbcs_lmbcs_mmmbc(#conf{format_to = FTO},
- #alloc{name = A, instances = Insts, segments = Segments}) ->
- MMMBC = case {A, Insts} of
- {_, 1} -> Segments#segment.number;
- {temp_alloc, _} -> Segments#segment.number;
- _ -> (Segments#segment.number div Insts) + 1
- end,
+smbcs_lmbcs(#conf{format_to = FTO},
+ #alloc{name = A, segments = Segments}) ->
MBCS = Segments#segment.size,
AC = alloc_char(A),
fc(FTO, "Mseg mbc size of ~p kilobytes.", [MBCS]),
format(FTO, " +M~csmbcs ~p +M~clmbcs ~p~n", [AC, MBCS, AC, MBCS]),
- fc(FTO, "Max ~p mseg mbcs.", [MMMBC]),
- format(FTO, " +M~cmmmbc ~p~n", [AC, MMMBC]),
ok.
alloc_char(binary_alloc) -> $B;
@@ -462,6 +499,8 @@ au_conf_alloc(#conf{format_to = FTO} = Conf,
#alloc{name = A,
alloc_util = true,
instances = Insts,
+ acul = Acul,
+ strategy = Strategy,
low_mbc_blocks_size = Low,
high_mbc_blocks_size = High} = Alc) ->
fcp(FTO, "Usage of mbcs: ~p - ~p kilobytes", [?B2KB(Low), ?B2KB(High)]),
@@ -470,31 +509,49 @@ au_conf_alloc(#conf{format_to = FTO} = Conf,
fc(FTO, "One instance used."),
format(FTO, " +M~ct false~n", [alloc_char(A)]);
_ ->
- fc(FTO, "~p instances used.",
+ fc(FTO, "~p + 1 instances used.",
[Insts]),
- format(FTO, " +M~ct true~n", [alloc_char(A)])
- end,
+ format(FTO, " +M~ct true~n", [alloc_char(A)]),
+ case Strategy of
+ undefined ->
+ ok;
+ _ ->
+ fc(FTO, "Allocation strategy: ~s.",
+ [strategy_str(Strategy)]),
+ format(FTO, " +M~cas ~s~n", [alloc_char(A),
+ atom_to_list(Strategy)])
+ end,
+ case carrier_migration_support(Strategy) of
+ false ->
+ ok;
+ true ->
+ fc(FTO, "Abandon carrier utilization limit of ~p%.", [Acul]),
+ format(FTO, " +M~cacul ~p~n", [alloc_char(A), Acul])
+ end
+ end,
mmbcs(Conf, Alc),
- smbcs_lmbcs_mmmbc(Conf, Alc),
+ smbcs_lmbcs(Conf, Alc),
sbct(Conf, Alc).
-large_growth(Low, High) ->
- High - Low >= ?LARGE_GROWTH_ABS_LIMIT.
-
calc_seg_size(Growth, Segs) ->
conf_size(round(Growth*?FRAG_FACT*?GROWTH_SEG_FACT) div Segs).
calc_growth_segments(Conf, AlcList0) ->
- CalcSmall = fun (#alloc{name = ll_alloc} = Alc, Acc) ->
- {Alc#alloc{segments = #segment{size = 0,
+ CalcSmall = fun (#alloc{name = ll_alloc, instances = 1} = Alc, Acc) ->
+ {Alc#alloc{segments = #segment{size = conf_size(0),
number = 0}},
Acc};
(#alloc{alloc_util = true,
- low_mbc_blocks_size = Low,
+ instances = Insts,
+ low_mbc_blocks_size = LowMBC,
high_mbc_blocks_size = High} = Alc,
{SL, AL}) ->
+ Low = case Insts of
+ 1 -> LowMBC;
+ _ -> 0
+ end,
Growth = High - Low,
- case large_growth(Low, High) of
+ case Growth >= ?LARGE_GROWTH_ABS_LIMIT of
true ->
{Alc, {SL, AL+1}};
false ->
@@ -522,8 +579,13 @@ calc_growth_segments(Conf, AlcList0) ->
end,
CalcLarge = fun (#alloc{alloc_util = true,
segments = undefined,
- low_mbc_blocks_size = Low,
+ instances = Insts,
+ low_mbc_blocks_size = LowMBC,
high_mbc_blocks_size = High} = Alc) ->
+ Low = case Insts of
+ 1 -> LowMBC;
+ _ -> 0
+ end,
Growth = High - Low,
SegSize = calc_seg_size(Growth,
SegsPerAlloc),
@@ -560,15 +622,10 @@ format_header(FTO) ->
case erlang:system_info(schedulers) of
1 -> ok;
Schdlrs ->
- MinSchdlrs = case Schdlrs > ?MAX_ALLOCATOR_INSTANCES of
- true -> ?MAX_ALLOCATOR_INSTANCES;
- false -> Schdlrs
- end,
fcp(FTO,
"NOTE: This configuration was made for ~p schedulers. "
- "It is very important that at least ~p schedulers "
- "are used.",
- [Schdlrs, MinSchdlrs])
+ "It is very important that ~p schedulers are used.",
+ [Schdlrs, Schdlrs])
end,
fcp(FTO,
"This configuration is intended as a suggestion and "
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 68ef04f20c..fea0854042 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. 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
@@ -227,7 +227,9 @@ fetch_stats(Parent, Time) ->
fetch_stats_loop(Parent, Time) ->
erlang:system_flag(scheduler_wall_time, true),
receive
- _Msg -> erlang:system_flag(scheduler_wall_time, false)
+ _Msg ->
+ %% erlang:system_flag(scheduler_wall_time, false)
+ ok
after Time ->
_M = Parent ! {stats, 1,
erlang:statistics(scheduler_wall_time),
@@ -244,17 +246,6 @@ etop_collect(Collector) ->
%% utilization in etop). Next time the flag will be true and then
%% there will be a measurement.
SchedulerWallTime = erlang:statistics(scheduler_wall_time),
-
- %% Turn off the flag while collecting data per process etc.
- case erlang:system_flag(scheduler_wall_time,false) of
- false ->
- %% First time and the flag was false - start a monitoring
- %% process to set the flag back to false when etop is stopped.
- spawn(fun() -> flag_holder_proc(Collector) end);
- _ ->
- ok
- end,
-
ProcInfo = etop_collect(processes(), []),
Collector ! {self(),#etop_info{now = now(),
@@ -264,13 +255,22 @@ etop_collect(Collector) ->
memi = etop_memi(),
procinfo = ProcInfo
}},
+
+ case SchedulerWallTime of
+ undefined ->
+ spawn(fun() -> flag_holder_proc(Collector) end);
+ _ ->
+ ok
+ end,
+
erlang:system_flag(scheduler_wall_time,true).
flag_holder_proc(Collector) ->
Ref = erlang:monitor(process,Collector),
receive
{'DOWN',Ref,_,_,_} ->
- erlang:system_flag(scheduler_wall_time,false)
+ %% erlang:system_flag(scheduler_wall_time,false)
+ ok
end.
etop_memi() ->
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index d46cfe1f32..0a70802c08 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -25,6 +25,8 @@
{registered, [runtime_tools_sup]},
{applications, [kernel, stdlib]},
{env, []},
- {mod, {runtime_tools, []}}]}.
+ {mod, {runtime_tools, []}},
+ {runtime_dependencies, ["stdlib-2.0","mnesia-4.12","kernel-3.0",
+ "erts-6.0"]}]}.
diff --git a/lib/runtime_tools/src/runtime_tools.appup.src b/lib/runtime_tools/src/runtime_tools.appup.src
index 7a435e9b22..0c2bab316f 100644
--- a/lib/runtime_tools/src/runtime_tools.appup.src
+++ b/lib/runtime_tools/src/runtime_tools.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -15,5 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-{"%VSN%",[],[]}.
+{"%VSN%",
+ [{<<".*">>,[{restart_application, runtime_tools}]}],
+ [{<<".*">>,[{restart_application, runtime_tools}]}]
+}.
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index 603b698d5e..f541d6e449 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -39,7 +39,8 @@
application/1, application/2,
environment/0, environment/1,
module/1, module/2,
- modules/1
+ modules/1,
+ sanity_check/0
]).
%% gen_server callbacks
@@ -85,9 +86,14 @@ report() -> [
{erts_compile_info, erlang:system_info(compile_info)},
{beam_dynamic_libraries, get_dynamic_libraries()},
{environment_erts, os_getenv_erts_specific()},
- {environment, [split_env(Env) || Env <- os:getenv()]}
+ {environment, [split_env(Env) || Env <- os:getenv()]},
+ {sanity_check, sanity_check()}
].
+-spec to_file(FileName) -> ok | {error, Reason} when
+ FileName :: file:name_all(),
+ Reason :: file:posix() | badarg | terminated | system_limit.
+
to_file(File) ->
file:write_file(File, iolist_to_binary([
io_lib:format("{system_information_version, ~p}.~n", [
@@ -130,6 +136,27 @@ module(M, Opts) when is_atom(M), is_list(Opts) ->
modules(Opt) when is_atom(Opt) ->
gen_server:call(?SERVER, {modules, Opt}).
+
+-spec sanity_check() -> ok | {failed, Failures} when
+ Application :: atom(),
+ ApplicationVersion :: string(),
+ MissingRuntimeDependencies :: {missing_runtime_dependencies,
+ ApplicationVersion,
+ [ApplicationVersion]},
+ InvalidApplicationVersion :: {invalid_application_version,
+ ApplicationVersion},
+ InvalidAppFile :: {invalid_app_file, Application},
+ Failure :: MissingRuntimeDependencies
+ | InvalidApplicationVersion
+ | InvalidAppFile,
+ Failures :: [Failure].
+
+sanity_check() ->
+ case check_runtime_dependencies() of
+ [] -> ok;
+ Issues -> {failed, Issues}
+ end.
+
%%===================================================================
%% gen_server callbacks
%%===================================================================
@@ -457,6 +484,8 @@ get_application_from_path(Path) ->
{description, proplists:get_value(description, Info, [])},
{vsn, proplists:get_value(vsn, Info, [])},
{path, Path},
+ {runtime_dependencies,
+ proplists:get_value(runtime_dependencies, Info, [])},
{modules, get_modules_from_path(Path)}
]}
end.
@@ -552,3 +581,252 @@ get_beam_name() ->
Value -> Value
end,
Beam ++ Type ++ Flavor.
+
+%% Check runtime dependencies...
+
+vsnstr2vsn(VsnStr) ->
+ list_to_tuple(lists:map(fun (Part) ->
+ list_to_integer(Part)
+ end,
+ string:tokens(VsnStr, "."))).
+
+rtdepstrs2rtdeps([]) ->
+ [];
+rtdepstrs2rtdeps([RTDep | RTDeps]) ->
+ [AppStr, VsnStr] = string:tokens(RTDep, "-"),
+ [{list_to_atom(AppStr), vsnstr2vsn(VsnStr)} | rtdepstrs2rtdeps(RTDeps)].
+
+build_app_table([], AppTab) ->
+ AppTab;
+build_app_table([App | Apps], AppTab0) ->
+ AppTab1 = try
+ %% We may have multiple application versions installed
+ %% of the same application! It is therefore important
+ %% to look up the application version that actually will
+ %% be used via code server.
+ AppFile = code:where_is_file(atom_to_list(App) ++ ".app"),
+ {ok, [{application, App, Info}]} = file:consult(AppFile),
+ VsnStr = proplists:get_value(vsn, Info),
+ Vsn = vsnstr2vsn(VsnStr),
+ RTDepStrs = proplists:get_value(runtime_dependencies,
+ Info, []),
+ RTDeps = rtdepstrs2rtdeps(RTDepStrs),
+ gb_trees:insert(App, {Vsn, RTDeps}, AppTab0)
+ catch
+ _ : _ ->
+ AppTab0
+ end,
+ build_app_table(Apps, AppTab1).
+
+meets_min_req(Vsn, Vsn) ->
+ true;
+meets_min_req({X}, VsnReq) ->
+ meets_min_req({X, 0, 0}, VsnReq);
+meets_min_req({X, Y}, VsnReq) ->
+ meets_min_req({X, Y, 0}, VsnReq);
+meets_min_req(Vsn, {X}) ->
+ meets_min_req(Vsn, {X, 0, 0});
+meets_min_req(Vsn, {X, Y}) ->
+ meets_min_req(Vsn, {X, Y, 0});
+meets_min_req({X, _Y, _Z}, {XReq, _YReq, _ZReq}) when X > XReq ->
+ true;
+meets_min_req({X, Y, _Z}, {X, YReq, _ZReq}) when Y > YReq ->
+ true;
+meets_min_req({X, Y, Z}, {X, Y, ZReq}) when Z > ZReq ->
+ true;
+meets_min_req({_X, _Y, _Z}, {_XReq, _YReq, _ZReq}) ->
+ false;
+meets_min_req(Vsn, VsnReq) ->
+ gp_meets_min_req(mk_gp_vsn_list(Vsn), mk_gp_vsn_list(VsnReq)).
+
+gp_meets_min_req([X, Y, Z | _Vs], [X, Y, Z]) ->
+ true;
+gp_meets_min_req([X, Y, Z | _Vs], [XReq, YReq, ZReq]) ->
+ meets_min_req({X, Y, Z}, {XReq, YReq, ZReq});
+gp_meets_min_req([X, Y, Z | Vs], [X, Y, Z | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req(_Vsn, _VReq) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+gp_meets_min_req_tail([V | Vs], [V | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req_tail([], []) ->
+ true;
+gp_meets_min_req_tail([_V | _Vs], []) ->
+ true;
+gp_meets_min_req_tail([V | _Vs], [VReq]) when V > VReq ->
+ true;
+gp_meets_min_req_tail(_Vs, _VReqs) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+mk_gp_vsn_list(Vsn) ->
+ [X, Y, Z | Tail] = tuple_to_list(Vsn),
+ [X, Y, Z | remove_trailing_zeroes(Tail)].
+
+remove_trailing_zeroes([]) ->
+ [];
+remove_trailing_zeroes([0 | Vs]) ->
+ case remove_trailing_zeroes(Vs) of
+ [] -> [];
+ NewVs -> [0 | NewVs]
+ end;
+remove_trailing_zeroes([V | Vs]) ->
+ [V | remove_trailing_zeroes(Vs)].
+
+mk_app_vsn_str({App, Vsn}) ->
+ mk_app_vsn_str(App, Vsn).
+
+mk_app_vsn_str(App, Vsn) ->
+ VsnList = tuple_to_list(Vsn),
+ lists:flatten([atom_to_list(App),
+ $-,
+ integer_to_list(hd(VsnList)),
+ lists:map(fun (Part) ->
+ [$., integer_to_list(Part)]
+ end, tl(VsnList))]).
+
+otp_17_0_vsns_orddict() ->
+ [{asn1,{3,0}},
+ {common_test,{1,8}},
+ {compiler,{5,0}},
+ {cosEvent,{2,1,15}},
+ {cosEventDomain,{1,1,14}},
+ {cosFileTransfer,{1,1,16}},
+ {cosNotification,{1,1,21}},
+ {cosProperty,{1,1,17}},
+ {cosTime,{1,1,14}},
+ {cosTransactions,{1,2,14}},
+ {crypto,{3,3}},
+ {debugger,{4,0}},
+ {dialyzer,{2,7}},
+ {diameter,{1,6}},
+ {edoc,{0,7,13}},
+ {eldap,{1,0,3}},
+ {erl_docgen,{0,3,5}},
+ {erl_interface,{3,7,16}},
+ {erts,{6,0}},
+ {et,{1,5}},
+ {eunit,{2,2,7}},
+ {gs,{1,5,16}},
+ {hipe,{3,10,3}},
+ {ic,{4,3,5}},
+ {inets,{5,10}},
+ {jinterface,{1,5,9}},
+ {kernel,{3,0}},
+ {megaco,{3,17,1}},
+ {mnesia,{4,12}},
+ {observer,{2,0}},
+ {odbc,{2,10,20}},
+ {orber,{3,6,27}},
+ {os_mon,{2,2,15}},
+ {ose,{1,0}},
+ {otp_mibs,{1,0,9}},
+ {parsetools,{2,0,11}},
+ {percept,{0,8,9}},
+ {public_key,{0,22}},
+ {reltool,{0,6,5}},
+ {runtime_tools,{1,8,14}},
+ {sasl,{2,4}},
+ {snmp,{4,25,1}},
+ {ssh,{3,0,1}},
+ {ssl,{5,3,4}},
+ {stdlib,{2,0}},
+ {syntax_tools,{1,6,14}},
+ {test_server,{3,7}},
+ {tools,{2,6,14}},
+ {typer,{0,9,6}},
+ {webtool,{0,8,10}},
+ {wx,{1,2}},
+ {xmerl,{1,3,7}}].
+
+otp_17_0_vsns_tab() ->
+ gb_trees:from_orddict(otp_17_0_vsns_orddict()).
+
+check_runtime_dependency({App, DepVsn}, AppTab) ->
+ case gb_trees:lookup(App, AppTab) of
+ none ->
+ false;
+ {value, {Vsn, _}} ->
+ meets_min_req(Vsn, DepVsn)
+ end.
+
+check_runtime_dependencies(App, AppTab, OtpMinVsnTab) ->
+ case gb_trees:lookup(App, AppTab) of
+ none ->
+ [{invalid_app_file, App}];
+ {value, {Vsn, RTDeps}} ->
+ RTD = case lists:foldl(
+ fun (RTDep, Acc) ->
+ case check_runtime_dependency(RTDep, AppTab) of
+ true ->
+ Acc;
+ false ->
+ [mk_app_vsn_str(RTDep) | Acc]
+ end
+ end,
+ [],
+ RTDeps) of
+ [] ->
+ [];
+ MissingDeps ->
+ [{missing_runtime_dependencies,
+ mk_app_vsn_str(App, Vsn),
+ MissingDeps}]
+ end,
+ case gb_trees:lookup(App, OtpMinVsnTab) of
+ none ->
+ RTD;
+ {value, MinVsn} ->
+ case meets_min_req(Vsn, MinVsn) of
+ true ->
+ RTD;
+ false ->
+ [{invalid_application_version,
+ mk_app_vsn_str(App, Vsn)} | RTD]
+ end
+ end
+ end.
+
+app_file_to_app(AF) ->
+ list_to_atom(filename:basename(AF, ".app")).
+
+get_apps() ->
+ get_apps(code:get_path(), []).
+
+get_apps([], Apps) ->
+ lists:usort(Apps);
+get_apps([Path|Paths], Apps) ->
+ case filelib:wildcard(filename:join(Path, "*.app")) of
+ [] ->
+ %% Not app or invalid app
+ get_apps(Paths, Apps);
+ [AppFile] ->
+ get_apps(Paths, [app_file_to_app(AppFile) | Apps]);
+ [_AppFile| _] = AppFiles ->
+ %% Strange with multple .app files... Lets put them
+ %% all in the list and see what we get...
+ lists:map(fun (AF) ->
+ app_file_to_app(AF)
+ end, AppFiles) ++ Apps
+ end.
+
+check_runtime_dependencies() ->
+ OtpMinVsnTab = otp_17_0_vsns_tab(),
+ Apps = get_apps(),
+ AppTab = build_app_table(Apps, gb_trees:empty()),
+ lists:foldl(fun (App, Acc) ->
+ case check_runtime_dependencies(App,
+ AppTab,
+ OtpMinVsnTab) of
+ [] -> Acc;
+ Issues -> Issues ++ Acc
+ end
+ end,
+ [],
+ Apps).
+
+%% End of runtime dependency checks
diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl
index 62497ab527..48ed810918 100644
--- a/lib/runtime_tools/test/runtime_tools_SUITE.erl
+++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl
@@ -25,7 +25,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases
--export([app_file/1, start_stop_app/1]).
+-export([app_file/1, appup_file/1, start_stop_app/1]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
@@ -43,6 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[app_file,
+ appup_file,
start_stop_app].
groups() ->
@@ -65,6 +66,9 @@ app_file(_Config) ->
?line ok = ?t:app_test(runtime_tools),
ok.
+appup_file(_Config) ->
+ ok = ?t:appup_test(runtime_tools).
+
start_stop_app(_Config) ->
ok = application:start(runtime_tools),
Sup = whereis(runtime_tools_sup),
diff --git a/lib/runtime_tools/test/system_information_SUITE.erl b/lib/runtime_tools/test/system_information_SUITE.erl
index fb9455a30f..53d20060e7 100644
--- a/lib/runtime_tools/test/system_information_SUITE.erl
+++ b/lib/runtime_tools/test/system_information_SUITE.erl
@@ -33,6 +33,7 @@
api_report/1,
api_to_file/1,
api_from_file/1,
+ sanity_check/1,
%% server
api_start_stop/1,
validate_server_interface/1
@@ -84,7 +85,8 @@ all() -> [
api_to_file,
api_from_file,
api_start_stop,
- validate_server_interface
+ validate_server_interface,
+ sanity_check
].
@@ -262,6 +264,9 @@ validate_server_interface(Config) ->
ok = system_information:stop(),
ok.
+sanity_check(Config) when is_list(Config) ->
+ ok = system_information:sanity_check().
+
%% aux
@@ -288,7 +293,8 @@ validate_report(Report) ->
erts_compile_info,
beam_dynamic_libraries,
environment_erts,
- environment
+ environment,
+ sanity_check
], Report).
ensure_report_keys([], _) -> ok;
diff --git a/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat b/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat
index 0900eadd4a..18938372a3 100644
--- a/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat
+++ b/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat
@@ -9870,4 +9870,5 @@
{"MANPATH",
"/usr/local/man:/usr/share/man:/usr/X11R6/man:/opt/gnome/share/man"},
{"LESSKEY","/etc/lesskey.bin"},
- {"LC_PAPER","sv_SE.UTF-8"}]}]}.
+ {"LC_PAPER","sv_SE.UTF-8"}]},
+ {sanity_check,ok}]}.
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index c282661a61..32953dfc5a 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.8.13
+RUNTIME_TOOLS_VSN = 1.8.14
diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src
index 8c814cfaf5..8e95197a2a 100644
--- a/lib/sasl/src/sasl.app.src
+++ b/lib/sasl/src/sasl.app.src
@@ -44,5 +44,7 @@
{applications, [kernel, stdlib]},
{env, [{sasl_error_logger, tty},
{errlog_type, all}]},
- {mod, {sasl, []}}]}.
+ {mod, {sasl, []}},
+ {runtime_dependencies, ["tools-2.6.14","stdlib-2.0","kernel-3.0",
+ "erts-6.0"]}]}.
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index 0c4d80a74f..e789853eea 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -16,12 +16,10 @@
%%
%% %CopyrightEnd%
{"%VSN%",
- %% Up from - max two major revisions back
+ %% Up from - max one major revision back
[{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
- {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
- {<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}], %% R15
- %% Down to - max two major revisions back
+ {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}], %% R16
+ %% Down to - max one major revision back
[{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
- {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
- {<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15
+ {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16
}.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 3d370a93a5..e5da797efb 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -2233,7 +2233,7 @@ format_error({undefined_applications,Apps}) ->
io_lib:format("Undefined applications: ~p~n",[Apps]);
format_error({duplicate_modules,Dups}) ->
io_lib:format("Duplicated modules: ~n~ts",
- [map(fun({{Mod,_,App1,_,_},{Mod,_,App2,_,_}}) ->
+ [map(fun({{Mod,App1,_},{Mod,App2,_}}) ->
io_lib:format("\t~w specified in ~w and ~w~n",
[Mod,App1,App2])
end, Dups)]);
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index 54c327410d..76f753c3d0 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -365,14 +365,22 @@ translate_application_instrs(Script, Appls, PreAppls) ->
case lists:keysearch(Appl, #application.name, Appls) of
{value, PostApplication} ->
PostMods = PostApplication#application.modules,
+ Type = PostApplication#application.type,
+ Apply =
+ case Type of
+ none -> [];
+ load -> [{apply, {application, load,
+ [Appl]}}];
+ _ -> [{apply, {application, start,
+ [Appl, Type]}}]
+ end,
[{apply, {application, stop, [Appl]}}] ++
[{remove, {M, brutal_purge, brutal_purge}}
|| M <- PreMods] ++
[{purge, PreMods}] ++
[{add_module, M, []} || M <- PostMods] ++
- [{apply, {application, start,
- [Appl, permanent]}}];
+ Apply;
false ->
throw({error, {no_such_application, Appl}})
end;
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 7e9d7c984a..ad2a8005b9 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -1781,37 +1781,46 @@ otp_10463_upgrade_script_regexp(_Config) ->
ok.
no_dot_erlang(Conf) ->
- PrivDir = priv_dir(Conf),
+ PrivDir = ?config(data_dir,Conf),
{ok, OrigWd} = file:get_cwd(),
try
ok = file:set_cwd(PrivDir),
- Erl = "\"" ++ filename:join([code:root_dir(),"bin","erl"]) ++ "\"",
- Args = " -noinput -run io put_chars \"TESTOK\" -run erlang halt",
+ {ok, Wd} = file:get_cwd(),
+ io:format("Dir ~ts~n", [Wd]),
+
+ Erl0 = filename:join([code:root_dir(),"bin","erl"]),
+ Erl = filename:nativename(Erl0),
+ Quote = "\"",
+ Args = " -noinput -run c pwd -run erlang halt",
ok = file:write_file(".erlang", <<"io:put_chars(\"DOT_ERLANG_READ\\n\").\n">>),
- case os:cmd(Erl ++ Args) of
+ CMD1 = Quote ++ Erl ++ Quote ++ Args ,
+ case os:cmd(CMD1) of
"DOT_ERLANG_READ" ++ _ -> ok;
Other1 ->
- io:format("Failed: ~ts~n",[Erl ++ Args]),
+ io:format("Failed: ~ts~n",[CMD1]),
io:format("Expected: ~s ++ _~n",["DOT_ERLANG_READ "]),
io:format("Got: ~ts~n",[Other1]),
- exit(failed_to_start, test_error)
+ exit({failed_to_start, test_error})
end,
NO_DOT_ERL = " -boot no_dot_erlang",
- case os:cmd(Erl ++ NO_DOT_ERL ++ Args) of
- "TESTOK" ++ _ -> ok;
- Other2 ->
- io:format("Failed: ~ts~n",[Erl ++ Args]),
+ CMD2 = Quote ++ Erl ++ Quote ++ NO_DOT_ERL ++ Args,
+ case lists:prefix(Wd, Other2 = os:cmd(CMD2)) of
+ true -> ok;
+ false ->
+ io:format("Failed: ~ts~n",[CMD2]),
io:format("Expected: ~s~n",["TESTOK"]),
io:format("Got: ~ts~n",[Other2]),
- exit(failed_to_start, no_dot_erlang)
+ exit({failed_to_start, no_dot_erlang})
end
after
_ = file:delete(".erlang"),
- ok = file:set_cwd(OrigWd)
+ ok = file:set_cwd(OrigWd),
+ ok
end.
+
%%%=================================================================
%%% Misceleaneous functions
%%%=================================================================
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index e799230338..f4455f7e9b 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -19,11 +19,6 @@
-module(sasl_SUITE).
-include_lib("common_test/include/ct.hrl").
-
-%% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(1)).
--define(application, sasl).
-
%% Test server specific exports
-export([all/0,groups/0,init_per_group/2,end_per_group/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -47,28 +42,26 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_Case, Config) ->
- Dog=test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+ Config.
+end_per_testcase(_Case, _Config) ->
ok.
app_test(Config) when is_list(Config) ->
?t:app_test(sasl, allow),
ok.
-%% Test that appup allows upgrade from/downgrade to a maximum of two
-%% major releases back.
+%% Test that appup allows upgrade from/downgrade to a maximum of one
+%% major release back.
appup_test(_Config) ->
- do_appup_tests(create_test_vsns()).
+ appup_tests(sasl,create_test_vsns(sasl)).
-do_appup_tests({[],[]}) ->
+appup_tests(_App,{[],[]}) ->
{skip,"no previous releases available"};
-do_appup_tests({OkVsns,NokVsns}) ->
- application:load(sasl),
- {_,_,Vsn} = lists:keyfind(sasl,1,application:loaded_applications()),
- AppupFile = filename:join([code:lib_dir(sasl),ebin,"sasl.appup"]),
+appup_tests(App,{OkVsns,NokVsns}) ->
+ application:load(App),
+ {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),
+ AppupFileName = atom_to_list(App) ++ ".appup",
+ AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),
{ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
ct:log("~p~n",[AppupScript]),
ct:log("Testing ok versions: ~p~n",[OkVsns]),
@@ -79,13 +72,12 @@ do_appup_tests({OkVsns,NokVsns}) ->
check_appup(NokVsns,DownTo,error),
ok.
-create_test_vsns() ->
+create_test_vsns(App) ->
This = erlang:system_info(otp_release),
FirstMajor = previous_major(This),
SecondMajor = previous_major(FirstMajor),
- ThirdMajor = previous_major(SecondMajor),
- Ok = sasl_vsn([FirstMajor,SecondMajor]),
- Nok0 = sasl_vsn([ThirdMajor]),
+ Ok = app_vsn(App,[FirstMajor]),
+ Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
[Ok1|_] ->
[Ok1 ++ ",1" | Nok0]; % illegal
@@ -101,19 +93,36 @@ previous_major("r"++Rel) ->
previous_major(Rel) ->
integer_to_list(list_to_integer(Rel)-1).
-sasl_vsn([R|Rs]) ->
- case test_server:is_release_available(R) of
- true ->
- {ok,N} = test_server:start_node(prevrel,peer,[{erl,[{release,R}]}]),
- _ = rpc:call(N,application,load,[sasl]),
+app_vsn(App,[R|Rs]) ->
+ OldRel =
+ case test_server:is_release_available(R) of
+ true ->
+ {release,R};
+ false ->
+ case ct:get_config({otp_releases,list_to_atom(R)}) of
+ undefined ->
+ false;
+ Prog0 ->
+ case os:find_executable(Prog0) of
+ false ->
+ false;
+ Prog ->
+ {prog,Prog}
+ end
+ end
+ end,
+ case OldRel of
+ false ->
+ app_vsn(App,Rs);
+ _ ->
+ {ok,N} = test_server:start_node(prevrel,peer,[{erl,[OldRel]}]),
+ _ = rpc:call(N,application,load,[App]),
As = rpc:call(N,application,loaded_applications,[]),
- {_,_,V} = lists:keyfind(sasl,1,As),
+ {_,_,V} = lists:keyfind(App,1,As),
test_server:stop_node(N),
- [V|sasl_vsn(Rs)];
- false ->
- sasl_vsn(Rs)
+ [V|app_vsn(App,Rs)]
end;
-sasl_vsn([]) ->
+app_vsn(_App,[]) ->
[].
check_appup([Vsn|Vsns],Instrs,Expected) ->
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index e3f6933476..1d3a71e94e 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -47,6 +47,7 @@
abnormal_script/1, src_tests_script/1, crazy_script/1,
included_script/1, included_override_script/1,
included_fail_script/1, included_bug_script/1, exref_script/1,
+ duplicate_modules_script/1,
otp_3065_circular_dependenies/1, included_and_used_sort_script/1]).
-export([tar_options/1, normal_tar/1, no_mod_vsn_tar/1, system_files_tar/1,
system_files_tar/2, invalid_system_files_tar/1,
@@ -84,6 +85,7 @@ groups() ->
src_tests_script, crazy_script,
included_script, included_override_script,
included_fail_script, included_bug_script, exref_script,
+ duplicate_modules_script,
otp_3065_circular_dependenies, included_and_used_sort_script]},
{tar, [],
[tar_options, normal_tar, no_mod_vsn_tar, system_files_tar,
@@ -822,6 +824,33 @@ no_hipe({ok, Value}) ->
{ok, Value}
end.
+%% duplicate_modules_script: Check that make_script rejects two
+%% applications providing the same module.
+duplicate_modules_script(Config) when is_list(Config) ->
+ {ok, OldDir} = file:get_cwd(),
+
+ {LatestDir, LatestName} = create_script(duplicate_modules,Config),
+
+ DataDir = filename:absname(?copydir),
+
+ ok = file:set_cwd(LatestDir),
+ LibDir = fname([DataDir, d_duplicate_modules, lib]),
+ P = [fname([LibDir, 'app1-1.0', ebin]),
+ fname([LibDir, 'app2-1.0', ebin])],
+
+ %% Check wrong app vsn
+ error = systools:make_script(LatestName, [{path, P}]),
+ {error,
+ systools_make,
+ {duplicate_modules, [
+ {{myapp,app1,_}, {myapp,app2,_}}
+ ]
+ }
+ } = systools:make_script(LatestName, [silent, {path, P}]),
+
+ ok = file:set_cwd(OldDir),
+ ok.
+
%% tar_options: Check illegal tar options.
tar_options(Config) when is_list(Config) ->
{'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} =
@@ -2186,7 +2215,10 @@ create_script(current_all_future_sasl,Config) ->
do_create_script(current_all_future_sasl,Config,current,Apps);
create_script({unicode,RelVsn},Config) ->
Apps = core_apps(current) ++ [{ua,"1.0"}],
- do_create_script(unicode,RelVsn,Config,current,Apps).
+ do_create_script(unicode,RelVsn,Config,current,Apps);
+create_script(duplicate_modules,Config) ->
+ Apps = core_apps(current) ++ [{app1,"1.0"},{app2,"1.0"}],
+ do_create_script(duplicate_modules,Config,current,Apps).
do_create_script(Id,Config,ErtsVsn,AppVsns) ->
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app
new file mode 100644
index 0000000000..dea9257f2f
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app
@@ -0,0 +1,7 @@
+{application, app1,
+ [{description, "Application 1"},
+ {vsn, "1.0"},
+ {modules, [myapp]},
+ {registered, []},
+ {applications, []},
+ {env, []}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl
new file mode 100644
index 0000000000..bf2ab7c79c
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl
@@ -0,0 +1,2 @@
+-module(myapp).
+-vsn("1.0").
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app
new file mode 100644
index 0000000000..476750d8b2
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app
@@ -0,0 +1,7 @@
+{application, app2,
+ [{description, "Application 2"},
+ {vsn, "1.0"},
+ {modules, [myapp]},
+ {registered, []},
+ {applications, []},
+ {env, []}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl
new file mode 100644
index 0000000000..bf2ab7c79c
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl
@@ -0,0 +1,2 @@
+-module(myapp).
+-vsn("1.0").
diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl
index 0cb6e63cf3..5efab7c028 100644
--- a/lib/sasl/test/systools_rc_SUITE.erl
+++ b/lib/sasl/test/systools_rc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -436,25 +436,19 @@ translate(Config) when is_list(Config) ->
translate_app(Config) when is_list(Config) ->
PreApps =
- [#application{name = test,
- description = "TEST",
- vsn = "1.0",
- modules = [foo,bar,baz],
- regs = [],
- mod = {sasl, []}},
+ [Test = #application{name = test,
+ description = "TEST",
+ vsn = "1.0",
+ modules = [foo,bar,baz],
+ regs = [],
+ mod = {sasl, []}},
#application{name = pelle,
description = "PELLE",
vsn = "1.0",
modules = [pelle, kalle],
regs = [],
mod = {pelle, []}}],
- Apps =
- [#application{name = test,
- description = "TEST",
- vsn = "1.0",
- modules = [foo,bar,baz],
- regs = [],
- mod = {sasl, []}}],
+ Apps = [Test],
%% Simple translation (1)
Up1 = [{add_module, foo},
{add_module, bar}],
@@ -475,6 +469,56 @@ translate_app(Config) when is_list(Config) ->
{load,{baz,brutal_purge,brutal_purge}},
{apply,{application,start,[test,permanent]}}] = X2,
+ %% Translate add_application with different restart types
+ %% permanent
+ Up2_1 = [{add_application, test, permanent}],
+ {ok, X2_1} = systools_rc:translate_scripts([Up2_1], Apps, []),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,permanent]}}] = X2_1,
+
+ %% transient
+ Up2_2 = [{add_application, test, transient}],
+ {ok, X2_2} = systools_rc:translate_scripts([Up2_2], Apps, []),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,transient]}}] = X2_2,
+
+ %% temporary
+ Up2_3 = [{add_application, test, temporary}],
+ {ok, X2_3} = systools_rc:translate_scripts([Up2_3], Apps, []),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,temporary]}}] = X2_3,
+
+ %% load
+ Up2_4 = [{add_application, test, load}],
+ {ok, X2_4} = systools_rc:translate_scripts([Up2_4], Apps, []),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,load,[test]}}] = X2_4,
+
+ %% none
+ Up2_5 = [{add_application, test, none}],
+ {ok, X2_5} = systools_rc:translate_scripts([Up2_5], Apps, []),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}}] = X2_5,
+
%% Simple translation (3)
Up3 = [{remove_application, pelle}],
{ok, X3} = systools_rc:translate_scripts([Up3], Apps, PreApps),
@@ -484,6 +528,102 @@ translate_app(Config) when is_list(Config) ->
{remove,{kalle,brutal_purge,brutal_purge}},
{purge,[pelle,kalle]},
{apply,{application,unload,[pelle]}}] = X3,
+
+ %% Simple translation (4)
+ Up4 = [{restart_application, test}],
+ {ok, X4} = systools_rc:translate_scripts([Up4], Apps, PreApps),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {apply,{application,stop,[test]}},
+ {remove,{foo,brutal_purge,brutal_purge}},
+ {remove,{bar,brutal_purge,brutal_purge}},
+ {remove,{baz,brutal_purge,brutal_purge}},
+ {purge,[foo,bar,baz]},
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,permanent]}}] = X4,
+
+ %% Translate restart_application with different restart types
+ %% permanent
+ {ok, X4_1} = systools_rc:translate_scripts([Up4],
+ [Test#application{type=permanent}],
+ [Test]),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {apply,{application,stop,[test]}},
+ {remove,{foo,brutal_purge,brutal_purge}},
+ {remove,{bar,brutal_purge,brutal_purge}},
+ {remove,{baz,brutal_purge,brutal_purge}},
+ {purge,[foo,bar,baz]},
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,permanent]}}] = X4_1,
+
+ %% transient
+ {ok, X4_2} = systools_rc:translate_scripts([Up4],
+ [Test#application{type=transient}],
+ [Test]),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {apply,{application,stop,[test]}},
+ {remove,{foo,brutal_purge,brutal_purge}},
+ {remove,{bar,brutal_purge,brutal_purge}},
+ {remove,{baz,brutal_purge,brutal_purge}},
+ {purge,[foo,bar,baz]},
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,transient]}}] = X4_2,
+
+ %% temporary
+ {ok, X4_3} = systools_rc:translate_scripts([Up4],
+ [Test#application{type=temporary}],
+ [Test]),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {apply,{application,stop,[test]}},
+ {remove,{foo,brutal_purge,brutal_purge}},
+ {remove,{bar,brutal_purge,brutal_purge}},
+ {remove,{baz,brutal_purge,brutal_purge}},
+ {purge,[foo,bar,baz]},
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,start,[test,temporary]}}] = X4_3,
+
+ %% load
+ {ok, X4_4} = systools_rc:translate_scripts([Up4],
+ [Test#application{type=load}],
+ [Test]),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {apply,{application,stop,[test]}},
+ {remove,{foo,brutal_purge,brutal_purge}},
+ {remove,{bar,brutal_purge,brutal_purge}},
+ {remove,{baz,brutal_purge,brutal_purge}},
+ {purge,[foo,bar,baz]},
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}},
+ {apply,{application,load,[test]}}] = X4_4,
+
+ %% none
+ {ok, X4_5} = systools_rc:translate_scripts([Up4],
+ [Test#application{type=none}],
+ [Test]),
+ [{load_object_code,{test,"1.0",[foo,bar,baz]}},
+ point_of_no_return,
+ {apply,{application,stop,[test]}},
+ {remove,{foo,brutal_purge,brutal_purge}},
+ {remove,{bar,brutal_purge,brutal_purge}},
+ {remove,{baz,brutal_purge,brutal_purge}},
+ {purge,[foo,bar,baz]},
+ {load,{foo,brutal_purge,brutal_purge}},
+ {load,{bar,brutal_purge,brutal_purge}},
+ {load,{baz,brutal_purge,brutal_purge}}] = X4_5,
+
ok.
diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl
index 37aa44c198..c8a4e92f24 100644
--- a/lib/sasl/test/test_lib.hrl
+++ b/lib/sasl/test/test_lib.hrl
@@ -1,3 +1,3 @@
-define(ertsvsn,"4.4").
--define(kernelvsn,"2.15.3").
--define(stdlibvsn,"1.18.3").
+-define(kernelvsn,"2.16.4").
+-define(stdlibvsn,"1.19.4").
diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml
index c1ea0a91f9..95a33e603e 100644
--- a/lib/snmp/doc/src/snmpa_mib_data.xml
+++ b/lib/snmp/doc/src/snmpa_mib_data.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2013</year><year>2013</year>
+ <year>2013</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/snmp/doc/src/snmpa_mib_storage.xml b/lib/snmp/doc/src/snmpa_mib_storage.xml
index a857ce79e8..791fbc80fe 100644
--- a/lib/snmp/doc/src/snmpa_mib_storage.xml
+++ b/lib/snmp/doc/src/snmpa_mib_storage.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2013</year><year>2013</year>
+ <year>2013</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src
index 904d17954b..cbd292e4c3 100644
--- a/lib/snmp/src/app/snmp.app.src
+++ b/lib/snmp/src/app/snmp.app.src
@@ -136,4 +136,6 @@
%% configuration and use), and in that case mnesia must also be started,
%% before snmp.
{applications, [kernel, stdlib]},
- {mod, {snmp_app, []}}]}.
+ {mod, {snmp_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","mnesia-4.12",
+ "kernel-3.0","erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile
index f22b7ea8ee..7bc9dd07d4 100644
--- a/lib/snmp/test/Makefile
+++ b/lib/snmp/test/Makefile
@@ -93,10 +93,10 @@ ifeq ($(SNMP_DEBUG),e)
SNMP_FLAGS += -Dsnmp_error
endif
ifeq ($(SNMP_DEBUG),l)
- SNMP_FLAGS += -Dsnmp_log
+ SNMP_FLAGS += -Dsnmp_error -Dsnmp_log
endif
ifeq ($(SNMP_DEBUG),d)
- SNMP_FLAGS += -Dsnmp_debug
+ SNMP_FLAGS += -Dsnmp_error -Dsnmp_log -Dsnmp_debug
endif
ifeq ($(DONT_USE_TS),true)
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 3d658bf8e8..fd8315ec4d 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. All Rights Reserved.
+# Copyright Ericsson AB 2004-2014. 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
@@ -42,6 +42,7 @@ TEST_UTIL_MODULES = \
snmp_test_manager \
snmp_test_mgr \
snmp_test_mgr_misc \
+ snmp_test_mgr_counter_server \
sa \
klas3 \
test1 \
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 89a6ce1253..2a9f2e842d 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -19,10 +19,6 @@
-module(snmp_agent_test).
-%% TODO
-%% * Test fault-tolerance (kill master etc)
-%%
-
-export([
all/0,
groups/0,
@@ -41,7 +37,7 @@
v1_processing/1,
big/1,
big2/1,
- loop_mib/1,
+ loop_mib_1/1,
api/1,
subagent/1,
mnesia/1,
@@ -394,8 +390,9 @@
usm_read/0,
usm_del_user/0,
usm_bad/0,
- loop_mib_1/0,
- loop_mib_2/0,
+ loop_mib_1_test/0,
+ loop_mib_2_test/0,
+ loop_mib_3_test/0,
otp_1129_i/1,
otp_1162_test/0,
otp_1131_test/0,
@@ -546,8 +543,9 @@ groups() ->
init_per_suite(Config0) when is_list(Config0) ->
- ?DBG("init_per_suite -> entry with"
- "~n Config0: ~p", [Config0]),
+ p("init_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config0, erlang:nodes()]),
Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0),
Config2 = snmp_test_lib:fix_data_dir(Config1),
@@ -558,16 +556,32 @@ init_per_suite(Config0) when is_list(Config0) ->
Config3 = [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2],
- ?DBG("init_per_suite -> end with"
- "~n Config3: ~p", [Config3]),
+ snmp_test_mgr_counter_server:start(),
+
+ p("init_per_suite -> end when"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config3, erlang:nodes()]),
Config3.
end_per_suite(Config) when is_list(Config) ->
- ?DBG("end_per_suite -> entry with"
- "~n Config: ~p", [Config]),
+ p("end_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config, erlang:nodes()]),
+
+ case snmp_test_mgr_counter_server:stop() of
+ {ok, _Counters} ->
+ p("end_per_suite -> sucessfully stopped counter server"
+ "~n Counters: ~p", [_Counters]);
+
+ {error, Reason} ->
+ p("end_per_suite -> failed stopping counter server"
+ "~n Reason: ~p", [Reason])
+ end,
+ p("end_per_suite -> end when"
+ "~n Nodes: ~p", [erlang:nodes()]),
Config.
@@ -675,10 +689,16 @@ end_per_group(_GroupName, Config) ->
%% ---- Init Per TestCase ----
init_per_testcase(Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
- "~n Config: ~p", [Config]),
+ p("init_per_testcase -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config, erlang:nodes()]),
- init_per_testcase1(Case, Config).
+ Result = init_per_testcase1(Case, Config),
+
+ p("init_per_testcase -> done when"
+ "~n Result: ~p"
+ "~n Nodes: ~p", [Result, erlang:nodes()]),
+ Result.
init_per_testcase1(otp8395 = Case, Config) when is_list(Config) ->
?DBG("init_per_testcase1 -> entry with"
@@ -719,12 +739,18 @@ init_per_testcase1(_Case, Config) when is_list(Config) ->
%% ---- End Per TestCase ----
end_per_testcase(Case, Config) when is_list(Config) ->
- ?DBG("end_per_testcase -> entry with"
- "~n Config: ~p", [Config]),
+ p("end_per_testcase -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config, erlang:nodes()]),
display_log(Config),
- end_per_testcase1(Case, Config).
+ Result = end_per_testcase1(Case, Config),
+
+ p("end_per_testcase -> done with"
+ "~n Result: ~p"
+ "~n Nodes: ~p", [Result, erlang:nodes()]),
+ Result.
end_per_testcase1(otp8395, Config) when is_list(Config) ->
otp8395({fin, Config});
@@ -1173,7 +1199,7 @@ mse_simple(X) -> ?P(mse_simple), simple(X).
mse_v1_processing(X) -> ?P(mse_v1_processing), v1_processing(X).
mse_big(X) -> ?P(mse_big), big(X).
mse_big2(X) -> ?P(mse_big2), big2(X).
-mse_loop_mib(X) -> ?P(mse_loop_mib), loop_mib(X).
+mse_loop_mib(X) -> ?P(mse_loop_mib), loop_mib_1(X).
mse_api(X) -> ?P(mse_api), api(X).
mse_sa_register(X) -> ?P(mse_sa_register), sa_register(X).
mse_v1_trap(X) -> ?P(mse_v1_trap), v1_trap(X).
@@ -1194,7 +1220,7 @@ msd_simple(X) -> ?P(msd_simple), simple(X).
msd_v1_processing(X) -> ?P(msd_v1_processing), v1_processing(X).
msd_big(X) -> ?P(msd_big), big(X).
msd_big2(X) -> ?P(msd_big2), big2(X).
-msd_loop_mib(X) -> ?P(msd_loop_mib), loop_mib(X).
+msd_loop_mib(X) -> ?P(msd_loop_mib), loop_mib_1(X).
msd_api(X) -> ?P(msd_api), api(X).
msd_sa_register(X) -> ?P(msd_sa_register), sa_register(X).
msd_v1_trap(X) -> ?P(msd_v1_trap), v1_trap(X).
@@ -1215,7 +1241,7 @@ msm_simple(X) -> ?P(msm_simple), simple(X).
msm_v1_processing(X) -> ?P(msm_v1_processing), v1_processing(X).
msm_big(X) -> ?P(msm_big2), big(X).
msm_big2(X) -> ?P(msm_loop_mib), big2(X).
-msm_loop_mib(X) -> ?P(msm_loop_mib), loop_mib(X).
+msm_loop_mib(X) -> ?P(msm_loop_mib), loop_mib_1(X).
msm_api(X) -> ?P(msm_api), api(X).
msm_sa_register(X) -> ?P(msm_sa_register), sa_register(X).
msm_v1_trap(X) -> ?P(msm_v1_trap), v1_trap(X).
@@ -1618,7 +1644,7 @@ v1_cases() ->
v1_processing,
big,
big2,
- loop_mib,
+ loop_mib_1,
api,
subagent,
mnesia,
@@ -2095,9 +2121,9 @@ await_dummy_manager_started(Pid) ->
{ok,Pid,Port};
{'EXIT', Pid, Reason} ->
{error, Pid, Reason};
- O ->
+ _O ->
?LOG("dummy_manager_start -> received unknown message:"
- "~n ~p",[O]),
+ "~n ~p",[_O]),
await_dummy_manager_started(Pid)
end.
@@ -2120,16 +2146,16 @@ dummy_manager_send_trap2(Pid) ->
dummy_manager_await_trap2_ack() ->
?DBG("dummy_manager_await_trap2 -> entry",[]),
receive
- {received_trap,Trap} ->
- ?LOG("dummy_manager_await_trap2 -> received trap: ~p",[Trap]),
+ {received_trap, _Trap} ->
+ ?LOG("dummy_manager_await_trap2 -> received trap: ~p", [_Trap]),
%% Note:
%% Without this sleep the v2_inform_i testcase failes! There
%% is no relation between these two test cases as far as I
%% able to figure out...
?SLEEP(60000),
ok;
- O ->
- ?ERR("dummy_manager_await_trap2 -> unexpected message: ~p",[O]),
+ _O ->
+ ?ERR("dummy_manager_await_trap2 -> unexpected message: ~p",[_O]),
ok
after 10000 ->
?ERR("dummy_manager_await_trap2 -> timeout",[]),
@@ -2155,32 +2181,34 @@ dummy_manager_loop(P,S,MA) ->
"~n Trap: ~p",[Trap]),
snmpa:send_trap(MA, Trap, "standard trap"),
dummy_manager_loop(P,S,MA);
- {udp, _UdpId, Ip, UdpPort, Bytes} ->
+ {udp, _UdpId, _Ip, _UdpPort, Bytes} ->
?LOG("dummy_manager_loop -> received upd message"
"~n from: ~p:~p"
"~n size: ~p",
- [Ip, UdpPort, dummy_manager_message_sz(Bytes)]),
+ [_Ip, _UdpPort, dummy_manager_message_sz(Bytes)]),
R = dummy_manager_handle_message(Bytes),
- ?DBG("dummy_manager_loop -> R: ~p",[R]),
+ ?DBG("dummy_manager_loop -> R: ~p", [R]),
P ! R,
- dummy_manager_loop(P,S,MA);
+ dummy_manager_loop(P, S, MA);
stop ->
?DBG("dummy_manager_loop -> received stop request",[]),
P ! {dummy_manager_stopping, self()},
gen_udp:close(S),
exit(normal);
- O ->
+ _O ->
?LOG("dummy_manager_loop -> received unknown message:"
- "~n ~p",[O]),
- dummy_manager_loop(P,S,MA)
+ "~n ~p", [_O]),
+ dummy_manager_loop(P, S, MA)
end.
+-ifdef(snmp_log).
dummy_manager_message_sz(B) when is_binary(B) ->
size(B);
dummy_manager_message_sz(L) when is_list(L) ->
length(L);
dummy_manager_message_sz(_) ->
undefined.
+-endif.
dummy_manager_handle_message(Bytes) ->
case (catch snmp_pdus:dec_message(Bytes)) of
@@ -3398,11 +3426,11 @@ simple_standard_test() ->
db_notify_client(suite) -> [];
db_notify_client(Config) when is_list(Config) ->
?P(db_notify_client),
- {SaNode, MgrNode, MibDir} = init_case(Config),
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
?DBG("db_notify_client -> case initiated: "
"~n SaNode: ~p"
"~n MgrNode: ~p"
- "~n MibDir: ~p", [SaNode, MgrNode, MibDir]),
+ "~n MibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
?DBG("db_notify_client -> maximize verbosity", []),
snmpa_local_db:verbosity(trace),
Self = self(),
@@ -4153,8 +4181,8 @@ ma_v2_inform1(MA) ->
CmdExp =
fun(ok) ->
ok;
- ({ok, Val}) ->
- ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [Val]),
+ ({ok, _Val}) ->
+ ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [_Val]),
ok;
({error, Id, Extra}) ->
{error, {unexpected, Id, Extra}};
@@ -4189,10 +4217,10 @@ ma_v2_inform1(MA) ->
CmdSnmpTargets =
fun(T) ->
receive
- {snmp_targets, T, [Addr]} ->
+ {snmp_targets, T, [_Addr]} ->
?DBG("ma_v2_inform1 -> "
"received expected snmp_targets "
- "~n with receiver: ~p",[Addr]),
+ "~n with receiver: ~p", [_Addr]),
ok;
{snmp_targets, T, Addrs} ->
?ERR("ma_v2_inform1 -> "
@@ -4210,16 +4238,16 @@ ma_v2_inform1(MA) ->
Cmd06 =
fun() ->
receive
- {snmp_notification, Tag03, {got_response, Addr}} ->
+ {snmp_notification, Tag03, {got_response, _Addr}} ->
?DBG("ma_v2_inform1 -> "
"received expected snmp_notification "
- "[with manager response] from: ~n ~p",[Addr]),
+ "[with manager response] from: ~n ~p", [_Addr]),
ok;
- {snmp_notification, Tag03, {no_response, Addr}} ->
+ {snmp_notification, Tag03, {no_response, _Addr}} ->
?ERR("ma_v2_inform1 -> "
"received unexpected snmp_notification "
"[without manager response] from: ~n ~p",
- [Addr]),
+ [_Addr]),
{error, no_response}
after
20000 ->
@@ -4249,16 +4277,16 @@ ma_v2_inform1(MA) ->
Cmd10 =
fun() ->
receive
- {snmp_notification, Tag07, {got_response, Addr}} ->
+ {snmp_notification, Tag07, {got_response, _Addr}} ->
?ERR("ma_v2_inform1 -> "
"received unexpected snmp_notification "
- "[with manager response] from: ~n ~p", [Addr]),
+ "[with manager response] from: ~n ~p", [_Addr]),
{error, got_response};
- {snmp_notification, Tag07, {no_response, Addr}} ->
+ {snmp_notification, Tag07, {no_response, _Addr}} ->
?DBG("ma_v2_inform1 -> "
"received expected snmp_notification "
"[without manager response] from: ~n ~p",
- [Addr]),
+ [_Addr]),
ok
after
240000 ->
@@ -4302,8 +4330,8 @@ ma_v2_inform2(MA) ->
CmdExp =
fun(ok) ->
ok;
- ({ok, Val}) ->
- ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [Val]),
+ ({ok, _Val}) ->
+ ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [_Val]),
ok;
({error, Id, Extra}) ->
{error, {unexpected, Id, Extra}};
@@ -4383,8 +4411,8 @@ ma_v2_inform3(MA) ->
"~n send notification: testTrapv22", [MA]),
CmdExpectInform =
- fun(No, Response) ->
- ?DBG("CmdExpectInform -> ~p: ~n~p", [No, Response]),
+ fun(_No, Response) ->
+ ?DBG("CmdExpectInform -> ~p: ~n~p", [_No, Response]),
?expect2({inform, Response},
[{[sysUpTime, 0], any},
{[snmpTrapOID, 0], ?system ++ [0,1]}])
@@ -4393,8 +4421,8 @@ ma_v2_inform3(MA) ->
CmdExp =
fun(ok) ->
ok;
- ({ok, Val}) ->
- ?DBG("CmdExp -> Val: ~p", [Val]),
+ ({ok, _Val}) ->
+ ?DBG("CmdExp -> Val: ~p", [_Val]),
ok;
({error, Id, Extra}) ->
{error, {unexpected, Id, Extra}};
@@ -4505,17 +4533,17 @@ delivery_info(Tag, Address, DeliveryResult, Extra) ->
command_handler([]) ->
ok;
-command_handler([{No, Desc, Cmd}|Rest]) ->
- ?LOG("command_handler -> command ~w: ~n ~s", [No, Desc]),
+command_handler([{_No, _Desc, Cmd}|Rest]) ->
+ ?LOG("command_handler -> command ~w: ~n ~s", [_No, _Desc]),
case (catch Cmd()) of
ok ->
- ?LOG("command_handler -> ~w: ok",[No]),
+ ?LOG("command_handler -> ~w: ok", [_No]),
command_handler(Rest);
{error, Reason} ->
- ?ERR("command_handler -> ~w error: ~n~p",[No, Reason]),
+ ?ERR("command_handler -> ~w error: ~n~p", [_No, Reason]),
?line ?FAIL(Reason);
Error ->
- ?ERR("command_handler -> ~w unexpected: ~n~p",[No, Error]),
+ ?ERR("command_handler -> ~w unexpected: ~n~p", [_No, Error]),
?line ?FAIL({unexpected_command_result, Error})
end.
@@ -5516,57 +5544,59 @@ usm_bad() ->
%% works.
%% Load all std mibs that are not loaded by default.
%%-----------------------------------------------------------------
-loop_mib(suite) -> [];
-loop_mib(Config) when is_list(Config) ->
- ?P(loop_mib),
- ?LOG("loop_mib -> initiate case",[]),
+loop_mib_1(suite) -> [];
+loop_mib_1(Config) when is_list(Config) ->
+ ?P(loop_mib_1),
+ ?LOG("loop_mib_1 -> initiate case",[]),
%% snmpa:verbosity(master_agent,debug),
%% snmpa:verbosity(mib_server,info),
- {SaNode, MgrNode, MibDir} = init_case(Config),
- ?DBG("loop_mib -> ~n"
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
+ ?DBG("loop_mib_1 -> ~n"
"\tSaNode: ~p~n"
"\tMgrNode: ~p~n"
- "\tMibDir: ~p",[SaNode, MgrNode, MibDir]),
- ?DBG("loop_mib -> load mib SNMP-COMMUNITY-MIB",[]),
+ "\tMibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
+ ?DBG("loop_mib_1 -> load mib SNMP-COMMUNITY-MIB",[]),
?line load_master_std("SNMP-COMMUNITY-MIB"),
- ?DBG("loop_mib -> load mib SNMP-MPD-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-MPD-MIB",[]),
?line load_master_std("SNMP-MPD-MIB"),
- ?DBG("loop_mib -> load mib SNMP-TARGET-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-TARGET-MIB",[]),
?line load_master_std("SNMP-TARGET-MIB"),
- ?DBG("loop_mib -> load mib SNMP-NOTIFICATION-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-NOTIFICATION-MIB",[]),
?line load_master_std("SNMP-NOTIFICATION-MIB"),
- ?DBG("loop_mib -> load mib SNMP-FRAMEWORK-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-FRAMEWORK-MIB",[]),
?line load_master_std("SNMP-FRAMEWORK-MIB"),
- ?DBG("loop_mib -> load mib SNMP-VIEW-BASED-ACM-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-VIEW-BASED-ACM-MIB",[]),
?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"),
- ?DBG("loop_mib -> try",[]),
- try_test(loop_mib_1),
- ?DBG("loop_mib -> unload mib SNMP-COMMUNITY-MIB",[]),
+ ?DBG("loop_mib_1 -> try",[]),
+
+ try_test(loop_mib_1_test),
+
+ ?DBG("loop_mib_1 -> unload mib SNMP-COMMUNITY-MIB",[]),
?line unload_master("SNMP-COMMUNITY-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-MPD-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-MPD-MIB",[]),
?line unload_master("SNMP-MPD-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-TARGET-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-TARGET-MIB",[]),
?line unload_master("SNMP-TARGET-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-NOTIFICATION-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-NOTIFICATION-MIB",[]),
?line unload_master("SNMP-NOTIFICATION-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-FRAMEWORK-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-FRAMEWORK-MIB",[]),
?line unload_master("SNMP-FRAMEWORK-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-VIEW-BASED-ACM-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-VIEW-BASED-ACM-MIB",[]),
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
%% snmpa:verbosity(master_agent,log),
%% snmpa:verbosity(mib_server,silence),
- ?LOG("loop_mib -> done",[]).
+ ?LOG("loop_mib_1 -> done",[]).
loop_mib_2(suite) -> [];
loop_mib_2(Config) when is_list(Config) ->
?P(loop_mib_2),
?LOG("loop_mib_2 -> initiate case",[]),
- {SaNode, MgrNode, MibDir} = init_case(Config),
- ?DBG("loop_mib_2 -> ~n"
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
+ ?DBG("do_loop_mib_2 -> ~n"
"\tSaNode: ~p~n"
"\tMgrNode: ~p~n"
- "\tMibDir: ~p",[SaNode, MgrNode, MibDir]),
+ "\tMibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
?DBG("loop_mib_2 -> load mibs",[]),
?line load_master_std("SNMP-COMMUNITY-MIB"),
?line load_master_std("SNMP-MPD-MIB"),
@@ -5574,7 +5604,9 @@ loop_mib_2(Config) when is_list(Config) ->
?line load_master_std("SNMP-NOTIFICATION-MIB"),
?line load_master_std("SNMP-FRAMEWORK-MIB"),
?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"),
- try_test(loop_mib_2),
+
+ try_test(loop_mib_2_test),
+
?DBG("loop_mib_2 -> unload mibs",[]),
?line unload_master("SNMP-COMMUNITY-MIB"),
?line unload_master("SNMP-MPD-MIB"),
@@ -5589,18 +5621,18 @@ loop_mib_3(suite) -> [];
loop_mib_3(Config) when is_list(Config) ->
?P(loop_mib_3),
?LOG("loop_mib_3 -> initiate case",[]),
- {SaNode, MgrNode, MibDir} = init_case(Config),
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
?DBG("loop_mib_3 -> ~n"
"\tSaNode: ~p~n"
"\tMgrNode: ~p~n"
- "\tMibDir: ~p",[SaNode, MgrNode, MibDir]),
+ "\tMibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
?DBG("loop_mib_3 -> load mibs",[]),
?line load_master_std("SNMP-TARGET-MIB"),
?line load_master_std("SNMP-NOTIFICATION-MIB"),
?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"),
?line load_master_std("SNMP-USER-BASED-SM-MIB"),
- try_test(loop_mib_2),
+ try_test(loop_mib_3_test),
?DBG("loop_mib_3 -> unload mibs",[]),
?line unload_master("SNMP-TARGET-MIB"),
@@ -5611,17 +5643,16 @@ loop_mib_3(Config) when is_list(Config) ->
%% Req. As many mibs all possible
-loop_mib_1() ->
- ?DBG("loop_mib_1 -> entry",[]),
+loop_mib_1_test() ->
+ ?DBG("loop_mib_1_test -> entry",[]),
N = loop_it_1([1,1], 0),
io:format(user, "found ~w varibles\n", [N]),
?line N = if N < 100 -> 100;
true -> N
end.
-
loop_it_1(Oid, N) ->
- ?DBG("loop_it_1 -> entry with~n"
+ ?DBG("loop_it_1_test -> entry with~n"
"\tOid: ~p~n"
"\tN: ~p",[Oid,N]),
case get_next_req([Oid]) of
@@ -5629,13 +5660,13 @@ loop_it_1(Oid, N) ->
error_status = noError,
error_index = 0,
varbinds = [#varbind{oid = NOid,
- value = Value}]} when NOid > Oid ->
- ?DBG("loop_it_1 -> "
+ value = _Value}]} when NOid > Oid ->
+ ?DBG("loop_it_1_test -> "
"~n NOid: ~p"
- "~n Value: ~p",[NOid, Value]),
- ?line [Value2] = get_req(1, [NOid]), % must not be same
- ?DBG("loop_it_1 -> "
- "~n Value2: ~p",[Value2]),
+ "~n Value: ~p", [NOid, _Value]),
+ ?line [_Value2] = get_req(1, [NOid]), % must not be same
+ ?DBG("loop_it_1_test -> "
+ "~n Value2: ~p", [_Value2]),
loop_it_1(NOid, N+1);
#pdu{type = 'get-response',
@@ -5648,7 +5679,7 @@ loop_it_1(Oid, N) ->
error_status = noSuchName,
error_index = 1,
varbinds = [_]} ->
- ?DBG("loop_it_1 -> done: ~p",[N]),
+ ?DBG("loop_it_1_test -> done: ~p",[N]),
N;
#pdu{type = 'get-response',
@@ -5669,14 +5700,13 @@ loop_it_1(Oid, N) ->
%% Req. As many mibs all possible
-loop_mib_2() ->
- ?DBG("loop_mib_1 -> entry",[]),
+loop_mib_2_test() ->
+ ?DBG("loop_mib_2_test -> entry",[]),
N = loop_it_2([1,1], 0),
io:format(user, "found ~w varibles\n", [N]),
?line N = if N < 100 -> 100;
true -> N
end.
-
loop_it_2(Oid, N) ->
?DBG("loop_it_2 -> entry with"
@@ -5686,22 +5716,22 @@ loop_it_2(Oid, N) ->
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
- varbinds = [#varbind{oid = NOid, value = endOfMibView}]} ->
+ varbinds = [#varbind{oid = _NOid, value = endOfMibView}]} ->
?DBG("loop_it_2 -> "
- "~n NOid: ~p",[NOid]),
+ "~n NOid: ~p", [_NOid]),
N;
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
varbinds = [#varbind{oid = NOid,
- value = Value}]} when NOid > Oid ->
+ value = _Value}]} when NOid > Oid ->
?DBG("loop_it_2 -> "
"~n NOid: ~p"
- "~n Value: ~p",[NOid, Value]),
- ?line [Value2] = get_req(1, [NOid]), % must not be same
+ "~n Value: ~p", [NOid, _Value]),
+ ?line [_Value2] = get_req(1, [NOid]), % must not be same
?DBG("loop_it_2 -> "
- "~n Value2: ~p",[Value2]),
+ "~n Value2: ~p", [_Value2]),
loop_it_2(NOid, N+1);
#pdu{type = 'get-response',
@@ -5744,6 +5774,10 @@ loop_it_2(Oid, N) ->
end.
+loop_mib_3_test() ->
+ ?DBG("loop_mib_3_test -> entry",[]),
+ loop_mib_2_test().
+
%%%-----------------------------------------------------------------
%%% Testing of reported bugs and other tickets.
@@ -6611,16 +6645,16 @@ otp8395(Config) when is_list(Config) ->
AgentNode = ?config(agent_node, Config),
AgentLogDir = ?config(agent_log_dir, Config),
OutFile = join([AgentLogDir, "otp8395.txt"]),
- {ok, LogInfo} = rpc:call(AgentNode, snmpa, log_info, []),
- ?DBG("otp8395 -> LogInfo: ~p", [LogInfo]),
+ {ok, _LogInfo} = rpc:call(AgentNode, snmpa, log_info, []),
+ ?DBG("otp8395 -> LogInfo: ~p", [_LogInfo]),
%% SyncRes = rpc:call(AgentNode, snmp, log_sync, [?audit_trail_log_name]),
%% ?DBG("otp8395 -> SyncRes: ~p", [SyncRes]),
ok = agent_log_validation(AgentNode),
- LTTRes =
+ _LTTRes =
rpc:call(AgentNode, snmpa, log_to_txt, [AgentLogDir, [], OutFile]),
- ?DBG("otp8395 -> LTTRes: ~p", [LTTRes]),
+ ?DBG("otp8395 -> LTTRes: ~p", [_LTTRes]),
?SLEEP(1000),
?DBG("otp8395 -> done", []),
@@ -6941,10 +6975,10 @@ stop_stdalone_agent(Pid) when (node(Pid) =/= node()) ->
MRef = erlang:monitor(process, Pid),
rpc:call(node(Pid), ?MODULE, stop_stdalone_agent, [Pid]),
receive
- {'DOWN', MRef, process, Pid, Info} ->
+ {'DOWN', MRef, process, Pid, _Info} ->
?DBG("received expected DOWN message "
"regarding snmp agent supervisor: "
- "~n Info: ~p", [Info]),
+ "~n Info: ~p", [_Info]),
ok
after 5000 ->
?DBG("no DOWN message "
@@ -7003,9 +7037,9 @@ do_info(MaNode) ->
tree_size_bytes,
db_memory]}],
verify_info(Info, Keys),
- OldInfo = snmpa:old_info_format(Info),
- ?DBG("info_test1 -> OldInfo: ~n~p", [OldInfo]),
- verify_old_info(OldInfo),
+ %% OldInfo = snmpa:old_info_format(Info),
+ %% ?DBG("info_test1 -> OldInfo: ~n~p", [OldInfo]),
+ %% verify_old_info(OldInfo),
ok.
verify_info([], []) ->
@@ -7048,20 +7082,20 @@ verify_subinfo(Info0, [Key|Keys]) ->
verify_subinfo(Info, Keys)
end.
-verify_old_info(Info) ->
- Keys = [vsns, subagents, loaded_mibs,
- tree_size_bytes, process_memory, db_memory],
- verify_old_info(Keys, Info).
-
-verify_old_info([], _) ->
- ok;
-verify_old_info([Key|Keys], Info) ->
- case lists:keymember(Key, 1, Info) of
- true ->
- verify_old_info(Keys, Info);
- false ->
- ?FAIL({missing_old_info, Key})
- end.
+%% verify_old_info(Info) ->
+%% Keys = [vsns, subagents, loaded_mibs,
+%% tree_size_bytes, process_memory, db_memory],
+%% verify_old_info(Keys, Info).
+
+%% verify_old_info([], _) ->
+%% ok;
+%% verify_old_info([Key|Keys], Info) ->
+%% case lists:keymember(Key, 1, Info) of
+%% true ->
+%% verify_old_info(Keys, Info);
+%% false ->
+%% ?FAIL({missing_old_info, Key})
+%% end.
%% Index String - string used in index
is(S) -> [length(S) | S].
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 122289c28e..d7109253f7 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -299,10 +299,10 @@ call(N,M,F,A) ->
"~n Loc: ~p", [Rn, Loc]),
put(test_server_loc, Loc),
exit(Rn);
- {done, Ret, Zed} ->
+ {done, Ret, _Zed} ->
?DBG("call -> done:"
"~n Ret: ~p"
- "~n Zed: ~p", [Ret, Zed]),
+ "~n Zed: ~p", [Ret, _Zed]),
case Ret of
{error, Reason} ->
exit(Reason);
@@ -338,8 +338,8 @@ run(Mod, Func, Args, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = ?CRYPTO_START(),
- ?DBG("run -> Crypto: ~p", [Crypto]),
+ _CryptoRes = ?CRYPTO_START(),
+ ?DBG("run -> Crypto: ~p", [_CryptoRes]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = join(code:priv_dir(snmp), "mibs") ++ "/",
Vsn = get(vsn),
@@ -676,9 +676,9 @@ stop_agent(Config) when is_list(Config) ->
(catch process_info(Sup)),
(catch process_info(Par))]),
- Info = agent_info(Sup),
+ _Info = agent_info(Sup),
?DBG("stop_agent -> Agent info: "
- "~n ~p", [Info]),
+ "~n ~p", [_Info]),
stop_sup(Sup, Par),
@@ -1303,10 +1303,10 @@ get_req(Id, Vars) ->
{ok, Val} ->
?DBG("get_req -> response: ~p",[Val]),
Val;
- {error, _, {ExpFmt, ExpArg}, {ActFmt, ActArg}} ->
+ {error, _, {_ExpFmt, ExpArg}, {_ActFmt, ActArg}} ->
?DBG("get_req -> error for ~p: "
- "~n " ++ ExpFmt ++
- "~n " ++ ActFmt,
+ "~n " ++ _ExpFmt ++
+ "~n " ++ _ActFmt,
[Id] ++ ExpArg ++ ActArg),
exit({unexpected_response, ExpArg, ActArg});
Error ->
@@ -1527,9 +1527,9 @@ rewrite_target_addr_conf(Dir, NewPort) ->
case file:read_file_info(TAFile) of
{ok, _} ->
ok;
- {error, R} ->
+ {error, _R} ->
?ERR("failure reading file info of "
- "target address config file: ~p",[R]),
+ "target address config file: ~p", [_R]),
ok
end,
diff --git a/lib/snmp/test/snmp_appup_test.erl b/lib/snmp/test/snmp_appup_test.erl
index 99994a2410..021d42a978 100644
--- a/lib/snmp/test/snmp_appup_test.erl
+++ b/lib/snmp/test/snmp_appup_test.erl
@@ -63,33 +63,9 @@ end_per_group(_GroupName, Config) ->
init_per_suite(suite) -> [];
init_per_suite(doc) -> [];
init_per_suite(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
- TopDir = filename:join(PrivDir, appup),
- case file:make_dir(TopDir) of
- ok ->
- ok;
- Error ->
- fail({failed_creating_subsuite_top_dir, Error})
- end,
- AppFile = file_name(?APPLICATION, ".app"),
- AppupFile = file_name(?APPLICATION, ".appup"),
- [{app_file, AppFile},
- {appup_file, AppupFile},
- {appup_topdir, TopDir} | Config].
+ Config.
-file_name(App, Ext) ->
- Env = init:get_arguments(),
- LibDir =
- case lists:keysearch(clearcase, 1, Env) of
- false ->
- code:lib_dir(App);
- _ ->
- ".."
- end,
- filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-
-
end_per_suite(suite) -> [];
end_per_suite(doc) -> [];
end_per_suite(Config) when is_list(Config) ->
@@ -108,467 +84,6 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup_file(suite) ->
- [];
-appup_file(doc) ->
- "Perform a simple check of the appup file";
+%% Perform a simple check of the appup file
appup_file(Config) when is_list(Config) ->
- AppupFile = key1search(appup_file, Config),
- AppFile = key1search(app_file, Config),
- Modules = modules(AppFile),
- check_appup(AppupFile, Modules).
-
-modules(File) ->
- case file:consult(File) of
- {ok, [{application,snmp,Info}]} ->
- case lists:keysearch(modules,1,Info) of
- {value, {modules, Modules}} ->
- Modules;
- false ->
- fail({bad_appinfo, Info})
- end;
- Error ->
- fail({bad_appfile, Error, File})
- end.
-
-
-check_appup(AppupFile, Modules) ->
- case file:consult(AppupFile) of
- {ok, [{V, UpFrom, DownTo}]} ->
- check_appup(V, UpFrom, DownTo, Modules);
- Else ->
- fail({bad_appupfile, Else})
- end.
-
-
-check_appup(V, UpFrom, DownTo, Modules) ->
- check_version(V),
- check_depends(up, UpFrom, Modules),
- check_depends(down, DownTo, Modules),
- check_module_subset(up, UpFrom),
- check_module_subset(down, DownTo),
- ok.
-
-check_depends(_, [], _) ->
- ok;
-check_depends(UpDown, [Dep|Deps], Modules) ->
- check_depend(UpDown, Dep, Modules),
- check_depends(UpDown, Deps, Modules).
-
-
-check_depend(up = UpDown, {add_application, ?APPLICATION} = Instr, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [UpDown, Instr, Modules]),
- ok;
-check_depend(down = UpDown, {remove_application, ?APPLICATION} = Instr,
- Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [UpDown, Instr, Modules]),
- ok;
-check_depend(UpDown, {V, Instructions}, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n V: ~p"
- "~n Modules: ~p", [UpDown, V, Modules]),
- check_version(V),
- case check_instructions(UpDown,
- Instructions, Instructions, [], [], Modules) of
- {_Good, []} ->
- ok;
- {_, Bad} ->
- fail({bad_instructions, Bad, UpDown})
- end.
-
-
-check_instructions(_, [], _, Good, Bad, _) ->
- {lists:reverse(Good), lists:reverse(Bad)};
-check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instr: ~p", [UpDown,Instr]),
- case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of
- ok ->
- check_instructions(UpDown, Instrs, AllInstr,
- [Instr|Good], Bad, Modules);
- {error, Reason} ->
- check_instructions(UpDown, Instrs, AllInstr, Good,
- [{Instr, Reason}|Bad], Modules)
- end;
-check_instructions(UpDown, Instructions, _, _, _, _) ->
- fail({bad_instructions, {UpDown, Instructions}}).
-
-check_instruction(_, {restart_application, ?APPLICATION}, _, _Modules) ->
- d("check_instruction -> entry when restart_application instruction"),
- ok;
-
-%% A new module is added
-check_instruction(up, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when up-add_module instruction with"
- "~n Module: ~p", [Module]),
- check_module(Module, Modules);
-
-%% An old module is re-added
-check_instruction(down, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when down-add_module instruction with"
- "~n Module: ~p", [Module]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- error({existing_readded_module, Module})
- end;
-
-check_instruction(up, {delete_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when up-delete_module instruction with"
- "~n Module: ~p", [Module]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- error({module_cannot_be_deleted, Module})
- end;
-
-%% An new module is deleted
-check_instruction(down, {delete_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when down-delete_module instruction with"
- "~n Module: ~p", [Module]),
- check_module(Module, Modules);
-
-%% Removing a module on upgrade:
-%% - the module has been removed from the app-file.
-%% - check that no module depends on this (removed) module
-check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules)
- when is_atom(Module) and is_atom(Pre) and is_atom(Post) ->
- d("check_instruction -> entry when up-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- check_purge(Pre),
- check_purge(Post);
- ok ->
- error({existing_removed_module, Module})
- end;
-
-%% Removing a module on downgrade: the module exist
-%% in the app-file.
-check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules)
- when is_atom(Module) and is_atom(Pre) and is_atom(Post) ->
- d("check_instruction -> entry when down-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- ok ->
- check_purge(Pre),
- check_purge(Post),
- check_no_remove_depends(Module, AllInstr);
- {error, {unknown_module, Module, Modules}} ->
- error({nonexisting_removed_module, Module})
- end;
-
-check_instruction(_, {load_module, Module, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module) and
- is_atom(Pre) and
- is_atom(Post) and
- is_list(Depend) ->
- d("check_instruction -> entry when load_module instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {update, Module, Change, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module) and
- is_atom(Pre) and
- is_atom(Post) and
- is_list(Depend) ->
- d("check_instruction -> entry when update instruction with"
- "~n Module: ~p"
- "~n Change: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Change, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_change(Change),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {update, Module, supervisor}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when supervisor update instruction with"
- "~n Module: ~p", [Module]),
- check_module(Module, Modules);
-
-check_instruction(_, {apply, {Module, Function, Args}}, _, _Modules)
- when is_atom(Module) and is_atom(Function) and is_list(Args) ->
- d("check_instruction -> entry when apply instruction with"
- "~n Module: ~p"
- "~n Function: ~p"
- "~n Args: ~p", [Module, Function, Args]),
- check_apply(Module, Function, Args);
-
-check_instruction(_, Instr, _AllInstr, _Modules) ->
- error({error, {unknown_instruction, Instr}}).
-
-%% If Module X depends on Module Y, then module Y must have an update
-%% instruction of some sort (otherwise the depend is faulty).
-updated_modules([], Modules) ->
- d("updated_modules -> entry when done with"
- "~n Modules: ~p", [Modules]),
- Modules;
-updated_modules([Instr|Instrs], Modules) ->
- d("updated_modules -> entry with"
- "~n Instr: ~p"
- "~n Modules: ~p", [Instr,Modules]),
- case instruction_module(Instr) of
- {module, Module} ->
- d("updated_modules -> Module: ~p", [Module]),
- updated_modules(Instrs, [Module|Modules]);
- no_module ->
- updated_modules(Instrs, Modules)
- end.
-
-instruction_module({add_module, Module}) ->
- {module, Module};
-instruction_module({delete_module, Module}) ->
- {module, Module};
-instruction_module({remove, {Module, _, _}}) ->
- {module, Module};
-instruction_module({load_module, Module, _, _, _}) ->
- {module, Module};
-instruction_module({update, Module, _, _, _, _}) ->
- {module, Module};
-instruction_module({update, Module, _}) ->
- {module, Module};
-instruction_module({apply, {_, _, _}}) ->
- no_module;
-instruction_module(Instr) ->
- d("instruction_module -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- error({error, {unknown_instruction, Instr}}).
-
-
-%% Check that the modules handled in an instruction set for version X
-%% is a subset of the instruction set for version X-1.
-check_module_subset(Direction, Instructions) ->
- d("check_module_subset(~w) -> entry when"
- "~n Instructions: ~p", [Direction,Instructions]),
- do_check_module_subset(modules_of(Instructions)).
-
-do_check_module_subset([]) ->
- ok;
-do_check_module_subset([_]) ->
- ok;
-do_check_module_subset([{_V1, Mods1}|T]) ->
- d("do_check_module_subset -> entry with"
- "~n V1: ~s"
- "~n Mods1: ~p", [_V1, Mods1]),
- {V2, Mods2} = hd(T),
- d("do_check_module_subset -> "
- "~n V2: ~s"
- "~n Mods2: ~p", [V2, Mods2]),
- %% Check that the modules in V1 is a subset of V2
- case do_check_module_subset2(Mods1, Mods2) of
- ok ->
- do_check_module_subset(T);
- {error, Modules} ->
- fail({subset_missing_instructions, V2, Modules})
- end.
-
-do_check_module_subset2(_Mods1, [{restart_application, ?APPLICATION}]) ->
- ok;
-do_check_module_subset2(Mods1, Mods2) ->
- do_check_module_subset2(Mods1, Mods2, []).
-
-do_check_module_subset2([], _, []) ->
- ok;
-do_check_module_subset2([], _, Acc) ->
- {error, lists:reverse(Acc)};
-do_check_module_subset2([Mod|Mods], Mods2, Acc) ->
- case lists:member(Mod, Mods2) of
- true ->
- do_check_module_subset2(Mods, Mods2, Acc);
- false ->
- do_check_module_subset2(Mods, Mods2, [Mod|Acc])
- end.
-
-
-modules_of(Instructions) ->
- modules_of(Instructions, []).
-
-modules_of([], Acc) ->
- lists:reverse(Acc);
-modules_of([{_V,[{restart_application, ?APPLICATION}]}|T], Acc) ->
- modules_of(T, Acc);
-modules_of([{V,Instructions}|T], Acc) ->
- Mods = modules_of2(Instructions, []),
- modules_of(T, [{V, Mods}|Acc]).
-
-modules_of2([], Acc) ->
- lists:reverse(Acc);
-modules_of2([Instr|Instructions], Acc) ->
- d("module_of -> entry with"
- "~n Instr: ~p", [Instr]),
- case module_of(Instr) of
- {value, Mod} ->
- d("module_of -> Mod: ~p", [Mod]),
- modules_of2(Instructions, [Mod|Acc]);
- false ->
- modules_of2(Instructions, Acc)
- end.
-
-module_of({add_module, Module}) ->
- {value, Module};
-module_of({delete_module, Module}) ->
- {value, Module};
-module_of({remove, {Module, _Pre, _Post}}) ->
- {value, Module};
-module_of({load_module, Module, _Pre, _Post, _Depend}) ->
- {value, Module};
-module_of({update, Module, _Change, _Pre, _Post, _Depend}) ->
- {value, Module};
-module_of({update, Module, supervisor}) ->
- {value, Module};
-module_of(_) ->
- false.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-check_version(V) when is_list(V) ->
- ok;
-check_version(V) ->
- error({bad_version, V}).
-
-
-check_module(M, Modules) when is_atom(M) ->
- case lists:member(M, Modules) of
- true ->
- ok;
- false ->
- error({unknown_module, M, Modules})
- end;
-check_module(M, _) ->
- error({bad_module, M}).
-
-
-check_module_depend(M, [], _) when is_atom(M) ->
- ok;
-check_module_depend(M, Deps, Modules) when is_atom(M) and is_list(Deps) ->
- case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of
- [] ->
- ok;
- Unknown ->
- error({unknown_depend_modules, Unknown})
- end;
-check_module_depend(_M, D, _Modules) ->
- error({bad_depend, D}).
-
-
-check_no_remove_depends(_Module, []) ->
- ok;
-check_no_remove_depends(Module, [Instr|Instrs]) ->
- check_no_remove_depend(Module, Instr),
- check_no_remove_depends(Module, Instrs).
-
-check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, load_module, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, update, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(_, _) ->
- ok.
-
-
-check_change(soft) ->
- ok;
-check_change({advanced, _Something}) ->
- ok;
-check_change(Change) ->
- error({bad_change, Change}).
-
-
-check_purge(soft_purge) ->
- ok;
-check_purge(brutal_purge) ->
- ok;
-check_purge(Purge) ->
- error({bad_purge, Purge}).
-
-check_apply(Module, Function, Args) ->
- case (catch Module:module_info()) of
- Info when is_list(Info) ->
- check_exported(Function, Args, Info);
- {'EXIT', {undef, _}} ->
- error({not_existing_module, Module})
- end.
-
-check_exported(Function, Args, Info) ->
- case lists:keysearch(exports, 1, Info) of
- {value, {exports, FuncList}} ->
- Arity = length(Args),
- Arities = [A || {F, A} <- FuncList, F == Function],
- case lists:member(Arity, Arities) of
- true ->
- ok;
- false ->
- fail({not_exported_function, Function, Arity})
- end;
- _ ->
- error({bad_export, Info})
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-error(Reason) ->
- throw({error, Reason}).
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-d(F) ->
- d(F, []).
-
-d(F, A) ->
- d(true, F, A).
-
-d(true, F, A) ->
- io:format(F ++ "~n", A);
-d(_, _, _) ->
- ok.
-
+ ok = ?t:appup_test(snmp).
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 5fe18980bc..3a654a2805 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -139,6 +139,8 @@
-define(NS_TIMEOUT, 10000).
+-define(DEFAULT_MNESIA_DEBUG, none).
+
%%----------------------------------------------------------------------
%% Records
@@ -173,7 +175,9 @@ end_per_suite(Config) when is_list(Config) ->
init_per_testcase(Case, Config) when is_list(Config) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
+ io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
+ p(Case, "init_per_testcase begin when"
+ "~n Nodes: ~p~n~n", [erlang:nodes()]),
%% This version of the API, based on Addr and Port, has been deprecated
DeprecatedApiCases =
[
@@ -187,16 +191,25 @@ init_per_testcase(Case, Config) when is_list(Config) ->
simple_async_get_bulk1,
misc_async1
],
- case lists:member(Case, DeprecatedApiCases) of
- true ->
- %% ?SKIP(api_no_longer_supported);
- {skip, api_no_longer_supported};
- false ->
- init_per_testcase2(Case, Config)
- end.
+ Result =
+ case lists:member(Case, DeprecatedApiCases) of
+ true ->
+ %% ?SKIP(api_no_longer_supported);
+ {skip, api_no_longer_supported};
+ false ->
+ init_per_testcase2(Case, Config)
+ end,
+ p(Case, "init_per_testcase end when"
+ "~n Nodes: ~p"
+ "~n Result: ~p"
+ "~n~n", [Result, erlang:nodes()]),
+ Result.
init_per_testcase2(Case, Config) ->
- ?DBG("init_per_testcase2 -> ~p", [erlang:nodes()]),
+ ?DBG("init_per_testcase2 -> "
+ "~n Case: ~p"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Case, Config, erlang:nodes()]),
CaseTopDir = snmp_test_lib:init_testcase_top_dir(Case, Config),
@@ -314,6 +327,8 @@ init_per_testcase3(Case, Config) ->
end.
end_per_testcase(Case, Config) when is_list(Config) ->
+ p(Case, "end_per_testcase begin when"
+ "~n Nodes: ~p~n~n", [erlang:nodes()]),
?DBG("fin [~w] Nodes [1]: ~p", [Case, erlang:nodes()]),
Dog = ?config(watchdog, Config),
?WD_STOP(Dog),
@@ -322,6 +337,8 @@ end_per_testcase(Case, Config) when is_list(Config) ->
?DBG("fin [~w] Nodes [2]: ~p", [Case, erlang:nodes()]),
%% TopDir = ?config(top_dir, Conf2),
%% ?DEL_DIR(TopDir),
+ p(Case, "end_per_testcase end when"
+ "~n Nodes: ~p~n~n", [erlang:nodes()]),
Conf2.
end_per_testcase2(Case, Config) ->
@@ -428,10 +445,10 @@ groups() ->
{request_tests, [],
[
{group, get_tests},
- {group, get_next_tests},
+ {group, get_next_tests},
{group, set_tests},
- {group, bulk_tests},
- {group, misc_request_tests}
+ {group, bulk_tests},
+ {group, misc_request_tests}
]
},
{request_tests_mt, [],
@@ -5303,34 +5320,59 @@ init_manager(AutoInform, Config) ->
?line Node = start_manager_node(),
+ %% The point with this (try catch block) is to be
+ %% able to do some cleanup in case we fail to
+ %% start some of the apps. That is, if we fail to
+ %% start the apps (mnesia, crypto and snmp agent)
+ %% we stop the (agent) node!
- %% --
- %% Start and initiate crypto on manager node
- %%
-
- ?line ok = init_crypto(Node),
+ try
+ begin
- %%
- %% Write manager config
- %%
+ %% --
+ %% Start and initiate crypto on manager node
+ %%
+
+ ?line ok = init_crypto(Node),
+
+ %%
+ %% Write manager config
+ %%
+
+ ?line ok = write_manager_config(Config),
+
+ IRB = case AutoInform of
+ true ->
+ auto;
+ _ ->
+ user
+ end,
+ Conf = [{manager_node, Node}, {irb, IRB} | Config],
+ Vsns = [v1,v2,v3],
+ start_manager(Node, Vsns, Conf)
+ end
+ catch
+ T:E ->
+ StackTrace = ?STACK(),
+ p("Failure during manager start: "
+ "~n Error Type: ~p"
+ "~n Error: ~p"
+ "~n StackTrace: ~p", [T, E, StackTrace]),
+ %% And now, *try* to cleanup
+ (catch stop_node(Node)),
+ ?FAIL({failed_starting_manager, T, E, StackTrace})
+ end.
- ?line ok = write_manager_config(Config),
-
- IRB = case AutoInform of
- true ->
- auto;
- _ ->
- user
- end,
- Conf = [{manager_node, Node}, {irb, IRB} | Config],
- Vsns = [v1,v2,v3],
- start_manager(Node, Vsns, Conf).
-
fin_manager(Config) ->
Node = ?config(manager_node, Config),
- stop_manager(Node, Config),
- fin_crypto(Node),
- stop_node(Node),
+ StopMgrRes = stop_manager(Node),
+ StopCryptoRes = fin_crypto(Node),
+ StopNode = stop_node(Node),
+ p("fin_agent -> stop apps and (mgr node ~p) node results: "
+ "~n SNMP Mgr: ~p"
+ "~n Crypto: ~p"
+ "~n Node: ~p",
+ [Node, StopMgrRes, StopCryptoRes, StopNode]),
Config.
@@ -5352,52 +5394,93 @@ init_agent(Config) ->
?line Node = start_agent_node(),
+ %% The point with this (try catch block) is to be
+ %% able to do some cleanup in case we fail to
+ %% start some of the apps. That is, if we fail to
+ %% start the apps (mnesia, crypto and snmp agent)
+ %% we stop the (agent) node!
- %% --
- %% Start and initiate mnesia on agent node
- %%
-
- ?line ok = init_mnesia(Node, Dir),
-
-
- %% --
- %% Start and initiate crypto on agent node
- %%
-
- ?line ok = init_crypto(Node),
-
-
- %%
- %% Write agent config
- %%
-
- Vsns = [v1,v2],
- ?line ok = write_agent_config(Vsns, Config),
-
- Conf = [{agent_node, Node},
- {mib_dir, MibDir} | Config],
+ try
+ begin
+
+ %% --
+ %% Start and initiate mnesia on agent node
+ %%
+
+ ?line ok = init_mnesia(Node, Dir, ?config(mnesia_debug, Config)),
+
+
+ %% --
+ %% Start and initiate crypto on agent node
+ %%
+
+ ?line ok = init_crypto(Node),
+
+
+ %%
+ %% Write agent config
+ %%
+
+ Vsns = [v1,v2],
+ ?line ok = write_agent_config(Vsns, Config),
+
+ Conf = [{agent_node, Node},
+ {mib_dir, MibDir} | Config],
- %%
- %% Start the agent
- %%
-
- start_agent(Node, Vsns, Conf).
+ %%
+ %% Start the agent
+ %%
+
+ start_agent(Node, Vsns, Conf)
+ end
+ catch
+ T:E ->
+ StackTrace = ?STACK(),
+ p("Failure during agent start: "
+ "~n Error Type: ~p"
+ "~n Error: ~p"
+ "~n StackTrace: ~p", [T, E, StackTrace]),
+ %% And now, *try* to cleanup
+ (catch stop_node(Node)),
+ ?FAIL({failed_starting_agent, T, E, StackTrace})
+ end.
+
fin_agent(Config) ->
Node = ?config(agent_node, Config),
- stop_agent(Node, Config),
- fin_crypto(Node),
- fin_mnesia(Node),
- stop_node(Node),
+ StopAgentRes = stop_agent(Node),
+ StopCryptoRes = fin_crypto(Node),
+ StopMnesiaRes = fin_mnesia(Node),
+ StopNode = stop_node(Node),
+ p("fin_agent -> stop apps and (agent node ~p) node results: "
+ "~n SNMP Agent: ~p"
+ "~n Crypto: ~p"
+ "~n Mnesia: ~p"
+ "~n Node: ~p",
+ [Node, StopAgentRes, StopCryptoRes, StopMnesiaRes, StopNode]),
Config.
-init_mnesia(Node, Dir) ->
+init_mnesia(Node, Dir, MnesiaDebug)
+ when ((MnesiaDebug =/= none) andalso
+ (MnesiaDebug =/= debug) andalso (MnesiaDebug =/= trace)) ->
+ init_mnesia(Node, Dir, ?DEFAULT_MNESIA_DEBUG);
+init_mnesia(Node, Dir, MnesiaDebug) ->
?DBG("init_mnesia -> load application mnesia", []),
?line ok = load_mnesia(Node),
?DBG("init_mnesia -> application mnesia: set_env dir: ~n~p",[Dir]),
?line ok = set_mnesia_env(Node, dir, filename:join(Dir, "mnesia")),
+ %% Just in case, only set (known to be) valid values for debug
+ if
+ ((MnesiaDebug =:= debug) orelse (MnesiaDebug =:= trace)) ->
+ ?DBG("init_mnesia -> application mnesia: set_env debug: ~w",
+ [MnesiaDebug]),
+ ?line ok = set_mnesia_env(Node, debug, MnesiaDebug);
+ true ->
+ ok
+ end,
+
?DBG("init_mnesia -> create mnesia schema",[]),
?line case create_schema(Node) of
ok ->
@@ -5434,25 +5517,89 @@ fin_crypto(Node) ->
%% -- Misc application wrapper functions --
-load_app(Node, App) when (Node =:= node()) andalso is_atom(App) ->
- application:load(App);
-load_app(Node, App) when is_atom(App) ->
- rcall(Node, application, load, [App]).
-
-start_app(Node, App) when (Node =:= node()) andalso is_atom(App) ->
- application:start(App);
+load_app(Node, App) ->
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, {already_loaded, LoadedApp}}) when (LoadedApp =:= App) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed loading app ~w on ~p: "
+ "~n ~p", [App, Node, Reason]),
+ ?FAIL({failed_load, Node, App, Reason})
+ end,
+ do_load_app(Node, App, VerifySuccess).
+
+do_load_app(Node, App, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ %% Local app
+ exec(fun() -> application:load(App) end, VerifySuccess);
+do_load_app(Node, App, VerifySuccess) ->
+ %% Remote app
+ exec(fun() -> rcall(Node, application, load, [App]) end, VerifySuccess).
+
+
start_app(Node, App) ->
- rcall(Node, application, start, [App]).
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, {already_started, LoadedApp}}) when (LoadedApp =:= App) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed starting app ~w on ~p: "
+ "~n ~p", [App, Node, Reason]),
+ ?FAIL({failed_start, Node, App, Reason})
+ end,
+ start_app(Node, App, VerifySuccess).
+
+start_app(Node, App, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ exec(fun() -> application:start(App) end, VerifySuccess);
+start_app(Node, App, VerifySuccess) ->
+ exec(fun() -> rcall(Node, application, start, [App]) end, VerifySuccess).
+
+
+stop_app(Node, App) ->
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, {not_started, LoadedApp}}) when (LoadedApp =:= App) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed stopping app ~w on ~p: "
+ "~n ~p", [App, Node, Reason]),
+ ?FAIL({failed_stop, Node, App, Reason})
+ end,
+ stop_app(Node, App, VerifySuccess).
+
+stop_app(Node, App, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ exec(fun() -> application:stop(App) end, VerifySuccess);
+stop_app(Node, App, VerifySuccess) when is_atom(App) ->
+ exec(fun() -> rcall(Node, application, stop, [App]) end, VerifySuccess).
+
+
+set_app_env(Node, App, Key, Val) ->
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed setting app ~w env on ~p"
+ "~n Key: ~p"
+ "~n Val: ~p"
+ "~n Reason: ~p"
+ "~n ~p", [App, Node, Key, Val, Reason]),
+ ?FAIL({failed_set_app_env,
+ Node, App, Key, Val, Reason})
+ end,
+ set_app_env(Node, App, Key, Val, VerifySuccess).
-stop_app(Node, App) when (Node =:= node()) andalso is_atom(App) ->
- application:stop(App);
-stop_app(Node, App) when is_atom(App) ->
- rcall(Node, application, stop, [App]).
+set_app_env(Node, App, Key, Val, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ exec(fun() -> application:set_env(App, Key, Val) end, VerifySuccess);
+set_app_env(Node, App, Key, Val, VerifySuccess) when is_atom(App) ->
+ exec(fun() -> rcall(Node, application, set_env, [App, Key, Val]) end,
+ VerifySuccess).
-set_app_env(Node, App, Key, Val) when (Node =:= node()) andalso is_atom(App) ->
- application:set_env(App, Key, Val);
-set_app_env(Node, App, Key, Val) when is_atom(App) ->
- rcall(Node, application, set_env, [App, Key, Val]).
+
+exec(Cmd, VerifySuccess) ->
+ VerifySuccess(Cmd()).
%% -- Misc snmp wrapper functions --
@@ -5900,9 +6047,9 @@ start_manager(Node, Vsns, Conf0, _Opts) ->
Conf0.
-stop_manager(Node, Conf) ->
- stop_snmp(Node),
- Conf.
+stop_manager(Node) ->
+ stop_snmp(Node).
+
%% -- Misc agent wrapper functions --
@@ -5951,9 +6098,8 @@ start_agent(Node, Vsns, Conf0, _Opts) ->
?line ok = start_snmp(Node),
Conf0.
-stop_agent(Node, Conf) ->
- stop_snmp(Node),
- Conf.
+stop_agent(Node) ->
+ stop_snmp(Node).
agent_load_mib(Node, Mib) ->
rcall(Node, snmpa, load_mibs, [[Mib]]).
@@ -6015,17 +6161,18 @@ stop_node(Node) ->
rpc:cast(Node, erlang, halt, []),
await_stopped(Node, 5).
-await_stopped(_, 0) ->
+await_stopped(Node, 0) ->
+ p("await_stopped -> ~p still exist: giving up", [Node]),
ok;
await_stopped(Node, N) ->
Nodes = erlang:nodes(),
case lists:member(Node, Nodes) of
true ->
- ?DBG("[~w] ~p still exist", [N, Node]),
+ p("await_stopped -> ~p still exist: ~w", [Node, N]),
?SLEEP(1000),
await_stopped(Node, N-1);
false ->
- ?DBG("[~w] ~p gone", [N, Node]),
+ p("await_stopped -> ~p gone: ~w", [Node, N]),
ok
end.
@@ -6271,7 +6418,7 @@ p(F, A) ->
p(TName, F, A) ->
io:format("*** [~w][~s] ***"
- "~n" ++ F ++ "~n", [TName, formated_timestamp()|A]).
+ "~n " ++ F ++ "~n", [TName, formated_timestamp()|A]).
formated_timestamp() ->
snmp_test_lib:formated_timestamp().
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 40fcbce8f1..d4eb00ff91 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -657,7 +657,8 @@ make_vb(Oid) ->
#varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
make_request_id() ->
- random:uniform(16#FFFFFFF-1).
+ %% random:uniform(16#FFFFFFF-1).
+ snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647).
echo_pdu(PDU, MiniMIB) ->
io:format("~s", [snmp_misc:format_pdu(PDU, MiniMIB)]).
diff --git a/lib/snmp/test/snmp_test_mgr_counter_server.erl b/lib/snmp/test/snmp_test_mgr_counter_server.erl
new file mode 100644
index 0000000000..db31e0380b
--- /dev/null
+++ b/lib/snmp/test/snmp_test_mgr_counter_server.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2014. 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%
+%%
+
+%%
+%% The reason for this (test) counter server is that the
+%% agent test suite is implemented in such a way that the
+%% agent is started once and then used for several test cases.
+%% Each request is given a request id which *was* generated using
+%% random! It is therefor possible, although unlikely, that a
+%% request may get a request id that has recently been used,
+%% which will cause the agent to silently reject the request.
+%% For this reason, we start this server at the start of the
+%% agent suite and stop it at the end and all request ids are
+%% generated by this server.
+%%
+
+-module(snmp_test_mgr_counter_server).
+
+-export([start/0, stop/0, increment/4]).
+
+-define(SERVER, ?MODULE).
+-define(TAB, snmp_test_mgr_counter_tab).
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+
+-spec start() -> ok.
+
+start() ->
+ Parent = self(),
+ ReqIdServer = spawn(fun() -> init(Parent) end),
+ receive
+ {ReqIdServer, ok} ->
+ ok;
+ {ReqIdServer, {error, Reason}} ->
+ exit({failed_starting_counter_server, Reason})
+ after 5000 ->
+ exit(ReqIdServer, kill), % Cleanup, just in case
+ exit({failed_starting_counter_server, timeout})
+ end.
+
+-spec stop() -> {ok, Counters :: list()} | {error, Reason :: term()}.
+
+stop() ->
+ request(stop).
+
+
+-spec increment(Counter :: atom(),
+ Initial :: non_neg_integer(),
+ Increment :: pos_integer(),
+ Max :: pos_integer()) ->
+ Next :: pos_integer().
+
+increment(Counter, Initial, Increment, Max) ->
+ Request = {increment, Counter, Initial, Increment, Max},
+ case request(Request) of
+ {ok, ReqId} ->
+ ReqId;
+ {error, Reason} ->
+ exit(Reason)
+ end.
+
+
+request(Request) ->
+ Id = make_ref(),
+ Msg = {self(), Id, Request},
+ try
+ begin
+ global:send(?SERVER, Msg),
+ receive
+ {reply, Id, Reply} ->
+ {ok, Reply}
+ end
+ end
+ catch
+ T:E ->
+ {error, {T, E}}
+ end.
+
+
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+init(Parent) ->
+ p("starting"),
+ case global:register_name(?SERVER, self()) of
+ yes ->
+ p("name registration ok"),
+ Parent ! {self(), ok};
+ no ->
+ p("name registration failed"),
+ Parent ! {self(), registration_failed},
+ exit(registration_failed)
+ end,
+ ets:new(?TAB, [set, named_table, {keypos, 1}]),
+ loop().
+
+loop() ->
+ receive
+ {From, Id, {increment, Counter, Initial, Increment, Max}} ->
+ Position = 2,
+ Threshold = Max,
+ SetValue = Initial,
+ UpdateOp = {Position, Increment, Threshold, SetValue},
+ NextVal =
+ try ets:update_counter(?TAB, Counter, UpdateOp) of
+ Next when is_integer(Next) ->
+ p("increment ~w: (next) ~w", [Counter, Next]),
+ Next
+ catch
+ error:badarg ->
+ %% Oups, first time
+ p("increment ~w: (initial) ~w", [Counter, Initial]),
+ ets:insert(?TAB, {Counter, Initial}),
+ Initial
+ end,
+ From ! {reply, Id, NextVal},
+ loop();
+
+ {From, Id, stop} ->
+ p("stop"),
+ Counters = ets:tab2list(?TAB),
+ From ! {reply, Id, Counters},
+ exit(normal)
+ end.
+
+
+p(F) ->
+ p(F, []).
+
+p(F, A) ->
+ io:format("*** [~s] COUNTER-SERVER [~w] " ++ F ++ "~n",
+ [snmp_test_lib:formated_timestamp(), self() | A]).
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 533e313bdb..04c3cc9392 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 4.25.0.1
+SNMP_VSN = 4.25.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 5d5f2e5b91..7fbd70c87e 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -304,6 +304,23 @@
<c><![CDATA[true]]></c> if the password is valid and
<c><![CDATA[false]]></c> otherwise.</p>
</item>
+
+ <tag><c><![CDATA[{negotiation_timeout, integer()}]]></c></tag>
+ <item>
+ <p>Max time in milliseconds for the authentication negotiation. The default value is 2 minutes.
+ </p>
+ </item>
+
+ <tag><c><![CDATA[{parallel_login, boolean()}]]></c></tag>
+ <item>
+ <p>If set to false (the default value), only one login is handled a time. If set to true, an unlimited logins will be allowed simultanously. Note that this affects only the connections with authentication in progress, not the already authenticated connections.
+ </p>
+ <warning>
+ <p>Do not enable parallel_logins without protecting the server by other means like a firewall. If set to true, there is no protection against dos attacs.</p>
+ </warning>
+
+ </item>
+
<tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
<item>
<p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
@@ -367,8 +384,11 @@
</func>
<func>
- <name>stop() -> ok </name>
+ <name>stop() -> ok | {error, Reason}</name>
<fsummary>Stops the SSH application.</fsummary>
+ <type>
+ <v>Reason = term()</v>
+ </type>
<desc>
<p>Stops the SSH application. See also
<seealso marker="kernel:application">application(3)</seealso></p>
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 74d7293be0..e0a51b3574 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -38,6 +38,8 @@
{registered, []},
{applications, [kernel, stdlib, crypto, public_key]},
{env, []},
- {mod, {ssh_app, []}}]}.
+ {mod, {ssh_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","public_key-0.22","kernel-3.0",
+ "erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 32f7cc470b..1917c95f5a 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -19,13 +19,9 @@
{"%VSN%",
[
- {<<"2.1\\.*">>, [{restart_application, ssh}]},
- {<<"2.0\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {<<".*">>, [{restart_application, ssh}]}
],
[
- {<<"2.1\\.*">>,[{restart_application, ssh}]},
- {<<"2.0\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 9f571adba2..de6e8cc421 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,4 +1,4 @@
-%%
+%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
@@ -32,8 +32,8 @@
shell/1, shell/2, shell/3]).
%%--------------------------------------------------------------------
--spec start() -> ok.
--spec start(permanent | transient | temporary) -> ok.
+-spec start() -> ok | {error, term()}.
+-spec start(permanent | transient | temporary) -> ok | {error, term()}.
%%
%% Description: Starts the ssh application. Default type
%% is temporary. see application(3)
@@ -51,7 +51,7 @@ start(Type) ->
application:start(ssh, Type).
%%--------------------------------------------------------------------
--spec stop() -> ok.
+-spec stop() -> ok | {error, term()}.
%%
%% Description: Stops the ssh application.
%%--------------------------------------------------------------------
@@ -332,6 +332,12 @@ handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{rekey_limit, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{negotiation_timeout, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{parallel_login, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -360,6 +366,10 @@ handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), leng
end;
handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
Opt;
+handle_ssh_option({negotiation_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
+ Opt;
+handle_ssh_option({parallel_login, Value} = Opt) when Value==true ; Value==false ->
+ Opt;
handle_ssh_option({user, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({dsa_pass_phrase, Value} = Opt) when is_list(Value) ->
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 91905b2eaf..e57b07cee8 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -84,11 +84,14 @@ handle_connection(_Callback, Address, Port, Options, Socket) ->
SystemSup = ssh_system_sup:system_supervisor(Address, Port),
{ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
+ Timeout = proplists:get_value(negotiation_timeout,
+ proplists:get_value(ssh_opts, Options, []),
+ 2*60*1000),
ssh_connection_handler:start_connection(server, Socket,
[{supervisors, [{system_sup, SystemSup},
{subsystem_sup, SubSysSup},
{connection_sup, ConnectionSup}]}
- | Options], infinity).
+ | Options], Timeout).
handle_error(timeout) ->
ok;
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 409a1db6d5..45fd907383 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -196,7 +196,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
{authorized, User,
ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
false ->
- {not_authorized, {User, {passwd, Password}},
+ {not_authorized, {User, {error,"Bad user or password"}},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
authentications = "",
partial_success = false}, Ssh)}
@@ -228,7 +228,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
ssh_transport:ssh_packet(
#ssh_msg_userauth_success{}, Ssh)};
false ->
- {not_authorized, {User, {error, "Invalid signature"}},
+ {not_authorized, {User, undefined},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
authentications="publickey,password",
partial_success = false}, Ssh)}
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 3462b98172..322da50f21 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -110,8 +110,16 @@ start_connection(server = Role, Socket, Options, Timeout) ->
{ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
{_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
socket_control(Socket, Pid, Callback),
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout)
+ case proplists:get_value(parallel_login, Opts, false) of
+ true ->
+ spawn(fun() ->
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout)
+ end);
+ false ->
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout)
+ end
catch
exit:{noproc, _} ->
{error, ssh_not_started};
@@ -157,7 +165,7 @@ init([Role, Socket, SshOpts]) ->
%%--------------------------------------------------------------------
-spec open_channel(pid(), string(), iodata(), integer(), integer(),
- timeout()) -> {open, channel_id()} | {open_error, term(), string(), string()}.
+ timeout()) -> {open, channel_id()} | {error, term()}.
%%--------------------------------------------------------------------
open_channel(ConnectionHandler, ChannelType, ChannelSpecificData,
InitialWindowSize,
@@ -206,7 +214,7 @@ global_request(ConnectionHandler, Type, false = Reply, Data) ->
send_all_state_event(ConnectionHandler, {global_request, self(), Type, Reply, Data}).
%%--------------------------------------------------------------------
--spec send(pid(), channel_id(), integer(), iolist(), timeout()) ->
+-spec send(pid(), channel_id(), integer(), iodata(), timeout()) ->
ok | {error, timeout} | {error, closed}.
%%--------------------------------------------------------------------
send(ConnectionHandler, ChannelId, Type, Data, Timeout) ->
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index 21cdedc156..5692138a8a 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -65,7 +65,7 @@ is_auth_key(Key, User,Opts) ->
%% Used by client
is_host_key(Key, PeerName, Algorithm, Opts) ->
- case lookup_host_key(PeerName, Algorithm, Opts) of
+ case lookup_host_key(Key, PeerName, Algorithm, Opts) of
{ok, Key} ->
true;
_ ->
@@ -121,9 +121,9 @@ decode_ssh_file(Pem, Password) ->
%% return {ok, Key(s)} or {error, not_found}
%%
-lookup_host_key(Host, Alg, Opts) ->
+lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
Host1 = replace_localhost(Host),
- do_lookup_host_key(Host1, Alg, Opts).
+ do_lookup_host_key(KeyToMatch, Host1, Alg, Opts).
add_host_key(Host, Key, Opts) ->
@@ -204,10 +204,10 @@ replace_localhost("localhost") ->
replace_localhost(Host) ->
Host.
-do_lookup_host_key(Host, Alg, Opts) ->
+do_lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
case file:open(file_name(user, "known_hosts", Opts), [read, binary]) of
{ok, Fd} ->
- Res = lookup_host_key_fd(Fd, Host, Alg),
+ Res = lookup_host_key_fd(Fd, KeyToMatch, Host, Alg),
file:close(Fd),
{ok, Res};
{error, enoent} -> {error, not_found};
@@ -228,16 +228,16 @@ identity_pass_phrase('ssh-rsa') ->
identity_pass_phrase("ssh-rsa") ->
rsa_pass_phrase.
-lookup_host_key_fd(Fd, Host, KeyType) ->
+lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType) ->
case io:get_line(Fd, '') of
eof ->
{error, not_found};
Line ->
case ssh_decode_line(Line, known_hosts) of
[{Key, Attributes}] ->
- handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
+ handle_host(Fd, KeyToMatch, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
[] ->
- lookup_host_key_fd(Fd, Host, KeyType)
+ lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType)
end
end.
@@ -248,13 +248,13 @@ ssh_decode_line(Line, Type) ->
[]
end.
-handle_host(Fd, Host, HostList, Key, KeyType) ->
+handle_host(Fd, KeyToMatch, Host, HostList, Key, KeyType) ->
Host1 = host_name(Host),
- case lists:member(Host1, HostList) and key_match(Key, KeyType) of
- true ->
+ case lists:member(Host1, HostList) andalso key_match(Key, KeyType) of
+ true when KeyToMatch == Key ->
Key;
- false ->
- lookup_host_key_fd(Fd, Host, KeyType)
+ _ ->
+ lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType)
end.
host_name(Atom) when is_atom(Atom) ->
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 01a0988718..8d6c77c0ed 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -315,8 +315,8 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?UINT32(Len), Data:Le
recipient_channel = Recipient,
data = Data
};
-decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
- ?UINT32(DataType), Data/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
+ ?UINT32(DataType), ?UINT32(Len), Data:Len/binary>>) ->
#ssh_msg_channel_extended_data{
recipient_channel = Recipient,
data_type_code = DataType,
@@ -380,27 +380,30 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER),
language = Lang
};
+decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
+ ?UINT32(Len1), Inst:Len1/binary, ?UINT32(Len2), Lang:Len2/binary,
+ ?UINT32(NumPromtps), Data/binary>>) ->
+ #ssh_msg_userauth_info_request{
+ name = Name,
+ instruction = Inst,
+ language_tag = Lang,
+ num_prompts = NumPromtps,
+ data = Data};
+
+%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
#ssh_msg_userauth_pk_ok{
algorithm_name = Alg,
key_blob = KeyBlob
};
+%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/binary,
?UINT32(Len1), Lang:Len1/binary>>) ->
#ssh_msg_userauth_passwd_changereq{
prompt = Prompt,
languge = Lang
};
-decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
- ?UINT32(Len1), Inst:Len1/binary, ?UINT32(Len2), Lang:Len2/binary,
- ?UINT32(NumPromtps), Data/binary>>) ->
- #ssh_msg_userauth_info_request{
- name = Name,
- instruction = Inst,
- language_tag = Lang,
- num_prompts = NumPromtps,
- data = Data};
decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
#ssh_msg_userauth_info_response{
@@ -424,8 +427,9 @@ decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?UINT32(N)>>) ->
#ssh_msg_kex_dh_gex_request_old{
n = N
};
-decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP), ?UINT32(Len0), Prime:Len0/big-signed-integer,
- ?UINT32(Len1), Generator:Len1/big-signed-integer>>) ->
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP),
+ ?UINT32(Len0), Prime:Len0/big-signed-integer-unit:8,
+ ?UINT32(Len1), Generator:Len1/big-signed-integer-unit:8>>) ->
#ssh_msg_kex_dh_gex_group{
p = Prime,
g = Generator
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index 213b5c714d..52665635f0 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -673,7 +673,7 @@ resolve_symlinks_2(["." | RestPath], State0, LinkCnt, AccPath) ->
resolve_symlinks_2([".." | RestPath], State0, LinkCnt, AccPath) ->
%% Remove the last path component
AccPathComps0 = filename:split(AccPath),
- Path = case lists:reverse(tl(lists:reverse(AccPathComps0))) of
+ Path = case lists:droplast(AccPathComps0) of
[] ->
"";
AccPathComps ->
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index b4e3871efd..d2e52379fa 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -38,6 +38,7 @@ suite() ->
all() ->
[app_test,
+ appup_test,
{group, dsa_key},
{group, rsa_key},
{group, dsa_pass_key},
@@ -150,6 +151,11 @@ app_test(Config) when is_list(Config) ->
?t:app_test(ssh),
ok.
%%--------------------------------------------------------------------
+appup_test() ->
+ [{doc, "Appup file consistency test."}].
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(ssh).
+%%--------------------------------------------------------------------
misc_ssh_options() ->
[{doc, "Test that we can set some misc options not tested elsewhere, "
"some options not yet present are not decided if we should support or "
diff --git a/lib/ssh/test/ssh_unicode_SUITE.erl b/lib/ssh/test/ssh_unicode_SUITE.erl
index a896a425b9..cc916673b3 100644
--- a/lib/ssh/test/ssh_unicode_SUITE.erl
+++ b/lib/ssh/test/ssh_unicode_SUITE.erl
@@ -1,10 +1,7 @@
-%% Next line needed to enable utf8-strings in Erlang:
-%% -*- coding: utf-8 -*-
-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 910dca3889..4bc1a9a644 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -184,12 +184,6 @@
<item> The DER encoded trusted certificates. If this option
is supplied it will override the cacertfile option.</item>
- <tag>{cacertfile, path()}</tag>
- <item>Path to file containing PEM encoded
- CA certificates (trusted certificates used for verifying a peer
- certificate). May be omitted if you do not want to verify
- the peer.</item>
-
<tag>{ciphers, ciphers()}</tag>
<item>The cipher suites that should be supported. The function
<c>cipher_suites/0</c> can be used to find all ciphers that are
@@ -354,7 +348,13 @@ fun(srp, Username :: string(), UserState :: term()) ->
<item>Specifies if client should try to reuse sessions
when possible.
</item>
-
+
+ <tag>{cacertfile, path()}</tag>
+ <item>The path to a file containing PEM encoded CA certificates. The CA
+ certificates are used during server authentication and when building the
+ client certificate chain.
+ </item>
+
<tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</tag>
<tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</tag>
<item>
@@ -403,7 +403,17 @@ fun(srp, Username :: string(), UserState :: term()) ->
meaning in the server than in the client.</p>
<taglist>
-
+
+ <tag>{cacertfile, path()}</tag>
+ <item>The path to a file containing PEM encoded CA
+ certificates. The CA certificates are used to build the server
+ certificate chain, and for client authentication. Also the CAs
+ are used in the list of acceptable client CAs passed to the
+ client when a certificate is requested. May be omitted if there
+ is no need to verify the client and if there are not any
+ intermediate CAs for the server certificate.
+ </item>
+
<tag>{dh, der_encoded()}</tag>
<item>The DER encoded Diffie Hellman parameters. If this option
is supplied it will override the dhfile option.
diff --git a/lib/ssl/src/dtls.erl b/lib/ssl/src/dtls.erl
index 1cad9560b5..780bddeb10 100644
--- a/lib/ssl/src/dtls.erl
+++ b/lib/ssl/src/dtls.erl
@@ -31,25 +31,29 @@
handshake/1, handshake/2, handshake/3]).
%%--------------------------------------------------------------------
+%%
+%% Description: Connect to a DTLS server.
+%%--------------------------------------------------------------------
+
-spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} |
{error, reason()}.
+
+connect(Socket, Options) when is_port(Socket) ->
+ connect(Socket, Options, infinity).
+
-spec connect(host() | port(), [connect_option()] | inet:port_number(),
timeout() | list()) ->
{ok, #sslsocket{}} | {error, reason()}.
--spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
-
-%%
-%% Description: Connect to an DTLS server.
-%%--------------------------------------------------------------------
-connect(Socket, Options) when is_port(Socket) ->
- connect(Socket, Options, infinity).
connect(Socket, SslOptions, Timeout) when is_port(Socket) ->
DTLSOpts = [{protocol, dtls} | SslOptions],
ssl:connect(Socket, DTLSOpts, Timeout);
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
+
+-spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
connect(Host, Port, Options, Timeout) ->
DTLSOpts = [{protocol, dtls} | Options],
ssl:connect(Host, Port, DTLSOpts, Timeout).
@@ -65,38 +69,44 @@ listen(Port, Options) ->
ssl:listen(Port, DTLSOpts).
%%--------------------------------------------------------------------
--spec accept(#sslsocket{}) -> {ok, #sslsocket{}} |
- {error, reason()}.
--spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
- {error, reason()}.
%%
%% Description: Performs transport accept on an ssl listen socket
%%--------------------------------------------------------------------
+-spec accept(#sslsocket{}) -> {ok, #sslsocket{}} |
+ {error, reason()}.
accept(ListenSocket) ->
accept(ListenSocket, infinity).
+
+-spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
+ {error, reason()}.
accept(Socket, Timeout) ->
ssl:transport_accept(Socket, Timeout).
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}) -> ok | {error, reason()}.
--spec handshake(#sslsocket{} | port(), timeout()| [ssl_option()
- | transport_option()]) ->
- ok | {ok, #sslsocket{}} | {error, reason()}.
--spec handshake(port(), [ssl_option()| transport_option()], timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
+-spec handshake(#sslsocket{}) -> ok | {error, reason()}.
+
handshake(ListenSocket) ->
handshake(ListenSocket, infinity).
+
+-spec handshake(#sslsocket{} | port(), timeout()| [ssl_option()
+ | transport_option()]) ->
+ ok | {ok, #sslsocket{}} | {error, reason()}.
+
handshake(#sslsocket{} = Socket, Timeout) ->
ssl:ssl_accept(Socket, Timeout);
handshake(ListenSocket, SslOptions) when is_port(ListenSocket) ->
handshake(ListenSocket, SslOptions, infinity).
+
+-spec handshake(port(), [ssl_option()| transport_option()], timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
handshake(Socket, SslOptions, Timeout) when is_port(Socket) ->
ssl:ssl_accept(Socket, SslOptions, Timeout).
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index da2e076856..57f8dd86d3 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -20,222 +20,514 @@
%% Internal application API
-%%====================================================================
+-behaviour(gen_fsm).
+
+-include("dtls_connection.hrl").
+-include("dtls_handshake.hrl").
+-include("ssl_alert.hrl").
+-include("dtls_record.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_api.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_srp.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
%% Internal application API
-%%====================================================================
+%% Setup
+-export([start_fsm/8]).
+
+%% State transition handling
+-export([next_record/1, next_state/4%,
+ %%next_state_connection/2
+ ]).
+
+%% Handshake handling
+-export([%%renegotiate/1,
+ send_handshake/2, send_change_cipher/2]).
+
+%% Alert and close handling
+-export([send_alert/2, handle_own_alert/4, %%handle_close_alert/3,
+ handle_normal_shutdown/3
+ %%handle_unexpected_message/3,
+ %%alert_user/5, alert_user/8
+ ]).
+
+%% Data handling
+-export([%%write_application_data/3,
+ read_application_data/2%%,
+%% passive_receive/2, next_record_if_active/1
+ ]).
+
+%% Called by tls_connection_sup
+-export([start_link/7]).
+%% gen_fsm callbacks
+-export([init/1, hello/2, certify/2, cipher/2,
+ abbreviated/2, connection/2, handle_event/3,
+ handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-%% %%====================================================================
-%% %% State functions
-%% %%====================================================================
-
-%% -spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-%% hello(start, #state{host = Host, port = Port, role = client,
-%% ssl_options = SslOpts,
-%% session = #session{own_certificate = Cert} = Session0,
-%% session_cache = Cache, session_cache_cb = CacheCb,
-%% connection_states = ConnectionStates0,
-%% renegotiation = {Renegotiation, _},
-%% client_cookie = Cookie} = State0) ->
-%% Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts,
-%% Cache, CacheCb, Renegotiation, Cert),
-
-%% Version = Hello#client_hello.client_version,
-%% State1 = State0#state{negotiated_version = Version, %% Requested version
-%% session =
-%% Session0#session{session_id = Hello#client_hello.session_id},
-%% dtls_handshake_history = ssl_handshake:init_handshake_history()},
-
-%% State2 = send_flight(Hello, waiting, State1),
-
-%% {Record, State} = next_record(State2),
-%% next_state(hello, hello, Record, State);
-
-%% hello(start, #state{role = server} = State0) ->
-%% {Record, State} = next_record(State0),
-%% next_state(hello, hello, Record, State);
-
-%% hello(#hello_request{}, #state{role = client} = State0) ->
-%% {Record, State} = next_record(State0),
-%% next_state(hello, hello, Record, State);
-
-%% hello(#server_hello{cipher_suite = CipherSuite,
-%% compression_method = Compression} = Hello,
-%% #state{session = #session{session_id = OldId},
-%% connection_states = ConnectionStates0,
-%% role = client,
-%% negotiated_version = ReqVersion,
-%% renegotiation = {Renegotiation, _},
-%% ssl_options = SslOptions} = State1) ->
-%% State0 = flight_done(State1),
-%% case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
-%% #alert{} = Alert ->
-%% handle_own_alert(Alert, ReqVersion, hello, State0);
-%% {Version, NewId, ConnectionStates, NextProtocol} ->
-%% {KeyAlgorithm, _, _, _} =
-%% ssl_cipher:suite_definition(CipherSuite),
-
-%% PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
-
-%% NewNextProtocol = case NextProtocol of
-%% undefined ->
-%% State0#state.next_protocol;
-%% _ ->
-%% NextProtocol
-%% end,
-
-%% State = State0#state{key_algorithm = KeyAlgorithm,
-%% hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
-%% negotiated_version = Version,
-%% connection_states = ConnectionStates,
-%% premaster_secret = PremasterSecret,
-%% expecting_next_protocol_negotiation = NextProtocol =/= undefined,
-%% next_protocol = NewNextProtocol},
-
-%% case ssl_session:is_new(OldId, NewId) of
-%% true ->
-%% handle_new_session(NewId, CipherSuite, Compression,
-%% State#state{connection_states = ConnectionStates});
-%% false ->
-%% handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
-%% end
-%% end;
-
-%% hello(#hello_verify_request{cookie = Cookie},
-%% #state{host = Host, port = Port,
-%% session = #session{own_certificate = Cert},
-%% session_cache = Cache, session_cache_cb = CacheCb,
-%% ssl_options = SslOpts,
-%% connection_states = ConnectionStates0,
-%% renegotiation = {Renegotiation, _}} = State0) ->
-%% Hello = ssl_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts,
-%% Cache, CacheCb, Renegotiation, Cert),
-%% State1 = State0#state{
-%% tls_handshake_history = ssl_handshake:init_handshake_history(),
-%% client_cookie = Cookie},
-%% State2 = send_flight(Hello, waiting, State1),
-
-%% {Record, State} = next_record(State2),
-%% next_state(hello, hello, Record, State);
-
-
-%% %%--------------------------------------------------------------------
-%% -spec abbreviated(#hello_request{} | #finished{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-%% abbreviated(timeout, State) ->
-%% { next_state, abbreviated, State, hibernate };
-
-%% abbreviated(Msg, State) ->
-%% handle_unexpected_message(Msg, abbreviated, State).
-
-%% %%--------------------------------------------------------------------
-%% -spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} |
-%% #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-
-%% certify(timeout, State) ->
-%% { next_state, certify, State, hibernate };
-
-%% certify(Msg, State) ->
-%% handle_unexpected_message(Msg, certify, State).
-
-
-%% %%--------------------------------------------------------------------
-%% -spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-%% cipher(timeout, State) ->
-%% { next_state, cipher, State, hibernate };
-
-%% cipher(Msg, State) ->
-%% handle_unexpected_message(Msg, cipher, State).
-
-%% %%--------------------------------------------------------------------
-%% -spec connection(#hello_request{} | #client_hello{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-%% connection(timeout, State) ->
-%% {next_state, connection, State, hibernate};
-
-%% connection(Msg, State) ->
-%% handle_unexpected_message(Msg, connection, State).
-
-%% %%--------------------------------------------------------------------
-%% %%% Internal functions
-%% %%--------------------------------------------------------------------
-%% handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
-%% Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
-%% handle_own_alert(Alert, Version, {Info, Msg}, State).
-
-%% send_flight(HandshakeRec, FlightState, State) ->
-%% send_flight(FlightState, buffer_flight(HandshakeRec, State)).
-
-%% send_flight(FlightState, State = #state{negotiated_version = Version,
-%% flight_buffer = Buffer}) ->
-
-%% State1 = do_send_flight(queue:to_list(Buffer), [], State),
-%% finish_send_flight(Version, FlightState, State1).
-
-%% resend_flight(State = #state{negotiated_version = Version,
-%% flight_state = FlightState,
-%% flight_buffer = Buffer})
-%% when FlightState == finished; FlightState == waiting ->
-%% State1 = do_send_flight(queue:to_list(Buffer), [], State),
-%% finish_send_flight(Version, FlightState, State1);
-
-%% resend_flight(State) ->
-%% State.
-
-%% flight_done(State) ->
-%% cancel_dtls_retransmit_timer(State#state{flight_state = done,
-%% flight_buffer = undefined}).
-
-%% do_send_flight([], BinMsgs, State = #state{transport_cb = Transport, socket = Socket}) ->
-%% Transport:send(Socket, lists:reverse(BinMsgs)),
-%% State;
-%% do_send_flight([{Epoch, MsgSeq, HandshakeRec}|T], BinMsgs0,
-%% State = #state{negotiated_version = Version,
-%% connection_states = ConnectionStates0}) ->
-%% CS0 = ssl_record:connection_state_by_epoch(ConnectionStates0, Epoch, write),
-%% {BinMsgs, CS1} = encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0),
-%% ConnectionStates1 = ssl_record:set_connection_state_by_epoch(ConnectionStates0, CS1, write),
-%% do_send_flight(T, BinMsgs, State#state{connection_states = ConnectionStates1}).
-
-%% cancel_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = TimerRef}) ->
-%% cancel_timer(TimerRef),
-%% State#state{dtls_retransmit_timer = undefined}.
-
-%% rearm_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = undefined}) ->
-%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit),
-%% State#state{dtls_retransmit_timer = TimerRef};
-%% rearm_dtls_retransmit_timer(State) ->
-%% State.
-
-%% finish_send_flight({254, _}, waiting, State) ->
-%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit),
-%% State#state{
-%% dtls_retransmit_timer = TimerRef,
-%% last_retransmit = timestamp(),
-%% flight_state = waiting};
-
-%% finish_send_flight(_, FlightState, State) ->
-%% State#state{flight_state = FlightState}.
-
-%% timestamp() ->
-%% {Mega, Sec, Micro} = erlang:now(),
-%% Mega * 1000000 * 1000 + Sec * 1000 + (Micro div 1000).
-
-%% encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0) ->
-%% {_, Fragments} = ssl_handshake:encode_handshake(HandshakeRec, Version, MsgSeq, 1400),
-%% lists:foldl(fun(F, {Bin, C0}) ->
-%% {B, C1} = ssl_record:encode_handshake(F, Version, C0),
-%% {[B|Bin], C1} end, {BinMsgs0, CS0}, Fragments).
+%%====================================================================
+%% Internal application API
+%%====================================================================
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
+ User, {CbModule, _,_, _} = CbInfo,
+ Timeout) ->
+ try
+ {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket,
+ Opts, User, CbInfo]),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ ok = ssl_connection:handshake(SslSocket, Timeout),
+ {ok, SslSocket}
+ catch
+ error:{badmatch, {error, _} = Error} ->
+ Error
+ end;
+
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts,
+ User, {CbModule, _,_, _} = CbInfo,
+ Timeout) ->
+ try
+ {ok, Pid} = dtls_connection_sup:start_child_dist([Role, Host, Port, Socket,
+ Opts, User, CbInfo]),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ ok = ssl_connection:handshake(SslSocket, Timeout),
+ {ok, SslSocket}
+ catch
+ error:{badmatch, {error, _} = Error} ->
+ Error
+ end.
+
+send_handshake(Handshake, #state{negotiated_version = Version,
+ tls_handshake_history = Hist0,
+ connection_states = ConnectionStates0} = State0) ->
+ {BinHandshake, ConnectionStates, Hist} =
+ encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
+ send_flight(BinHandshake, State0#state{connection_states = ConnectionStates,
+ tls_handshake_history = Hist
+ }).
+
+send_alert(Alert, #state{negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0} = State0) ->
+ {BinMsg, ConnectionStates} =
+ ssl_alert:encode(Alert, Version, ConnectionStates0),
+ Transport:send(Socket, BinMsg),
+ State0#state{connection_states = ConnectionStates}.
+
+send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
+ socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport} = State0) ->
+ {BinChangeCipher, ConnectionStates} =
+ encode_change_cipher(Msg, Version, ConnectionStates0),
+ Transport:send(Socket, BinChangeCipher),
+ State0#state{connection_states = ConnectionStates}.
+
+%%====================================================================
+%% tls_connection_sup API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+ {ok, pid()} | ignore | {error, reason()}.
+%%
+%% Description: Creates a gen_fsm process which calls Module:init/1 to
+%% initialize. To ensure a synchronized start-up procedure, this function
+%% does not return until Module:init/1 has returned.
+%%--------------------------------------------------------------------
+start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
+ {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
+
+init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
+ process_flag(trap_exit, true),
+ State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ Handshake = ssl_handshake:init_handshake_history(),
+ TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ try ssl_config:init(SSLOpts0, Role) of
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
+ Session = State0#state.session,
+ State = State0#state{
+ tls_handshake_history = Handshake,
+ session = Session#session{own_certificate = OwnCert,
+ time_stamp = TimeStamp},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams},
+ gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State))
+ catch
+ throw:Error ->
+ gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0))
+ end.
+
+%%--------------------------------------------------------------------
+%% Description:There should be one instance of this function for each
+%% possible state name. Whenever a gen_fsm receives an event sent
+%% using gen_fsm:send_event/2, the instance of this function with the
+%% same name as the current state name StateName is called to handle
+%% the event. It is also called if a timeout occurs.
+%%
+hello(start, #state{host = Host, port = Port, role = client,
+ ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
+ Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
+ Cache, CacheCb, Renegotiation, Cert),
+
+ Version = Hello#client_hello.client_version,
+ Handshake0 = ssl_handshake:init_handshake_history(),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State1 = State0#state{connection_states = ConnectionStates,
+ negotiated_version = Version, %% Requested version
+ session =
+ Session0#session{session_id = Hello#client_hello.session_id},
+ tls_handshake_history = Handshake},
+ {Record, State} = next_record(State1),
+ next_state(hello, hello, Record, State);
+
+hello(Hello = #client_hello{client_version = ClientVersion,
+ extensions = #hello_extensions{hash_signs = HashSigns}},
+ State = #state{connection_states = ConnectionStates0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ ssl_options = SslOpts}) ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
+ case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
+ ConnectionStates0, Cert}, Renegotiation) of
+ {Version, {Type, Session},
+ ConnectionStates,
+ #hello_extensions{ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves} = ServerHelloExt} ->
+ ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
+ State#state{connection_states = ConnectionStates,
+ negotiated_version = Version,
+ session = Session,
+ client_ecc = {EllipticCurves, EcPointFormats}}, ?MODULE);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ClientVersion, hello, State)
+ end;
+hello(Hello,
+ #state{connection_states = ConnectionStates0,
+ negotiated_version = ReqVersion,
+ role = client,
+ renegotiation = {Renegotiation, _},
+ ssl_options = SslOptions} = State) ->
+ case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, State);
+ {Version, NewId, ConnectionStates, NextProtocol} ->
+ ssl_connection:handle_session(Hello,
+ Version, NewId, ConnectionStates, NextProtocol, State)
+ end;
+
+hello(Msg, State) ->
+ ssl_connection:hello(Msg, State, ?MODULE).
+
+abbreviated(Msg, State) ->
+ ssl_connection:abbreviated(Msg, State, ?MODULE).
+
+certify(Msg, State) ->
+ ssl_connection:certify(Msg, State, ?MODULE).
+
+cipher(Msg, State) ->
+ ssl_connection:cipher(Msg, State, ?MODULE).
+
+connection(#hello_request{}, #state{host = Host, port = Port,
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
+ Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
+ Cache, CacheCb, Renegotiation, Cert),
+ %% TODO DTLS version State1 = send_handshake(Hello, State0),
+ State1 = State0,
+ {Record, State} =
+ next_record(
+ State1#state{session = Session0#session{session_id
+ = Hello#client_hello.session_id}}),
+ next_state(connection, hello, Record, State);
+
+connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
+ %% Mitigate Computational DoS attack
+ %% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html
+ %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client
+ %% initiated renegotiation we will disallow many client initiated
+ %% renegotiations immediately after each other.
+ erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
+ hello(Hello, State#state{allow_renegotiate = false});
+
+connection(#client_hello{}, #state{role = server, allow_renegotiate = false} = State0) ->
+ Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
+ State = send_alert(Alert, State0),
+ next_state_connection(connection, State);
+
+connection(Msg, State) ->
+ ssl_connection:connection(Msg, State, tls_connection).
+
+%%--------------------------------------------------------------------
+%% Description: Whenever a gen_fsm receives an event sent using
+%% gen_fsm:send_all_state_event/2, this function is called to handle
+%% the event. Not currently used!
+%%--------------------------------------------------------------------
+handle_event(_Event, StateName, State) ->
+ {next_state, StateName, State, get_timeout(State)}.
+
+%%--------------------------------------------------------------------
+%% Description: Whenever a gen_fsm receives an event sent using
+%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
+%% the event.
+%%--------------------------------------------------------------------
+handle_sync_event(Event, From, StateName, State) ->
+ ssl_connection:handle_sync_event(Event, From, StateName, State).
+
+%%--------------------------------------------------------------------
+%% Description: This function is called by a gen_fsm when it receives any
+%% other message than a synchronous or asynchronous event
+%% (or a system message).
+%%--------------------------------------------------------------------
+
+%% raw data from socket, unpack records
+handle_info({Protocol, _, Data}, StateName,
+ #state{data_tag = Protocol} = State0) ->
+ %% Simplify for now to avoid dialzer warnings before implementation is compleate
+ %% case next_tls_record(Data, State0) of
+ %% {Record, State} ->
+ %% next_state(StateName, StateName, Record, State);
+ %% #alert{} = Alert ->
+ %% handle_normal_shutdown(Alert, StateName, State0),
+ %% {stop, {shutdown, own_alert}, State0}
+ %% end;
+ {Record, State} = next_tls_record(Data, State0),
+ next_state(StateName, StateName, Record, State);
+
+handle_info({CloseTag, Socket}, StateName,
+ #state{socket = Socket, close_tag = CloseTag,
+ negotiated_version = _Version} = State) ->
+ handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ {stop, {shutdown, transport_closed}, State};
+
+handle_info(Msg, StateName, State) ->
+ ssl_connection:handle_info(Msg, StateName, State).
+
+%%--------------------------------------------------------------------
+%% Description:This function is called by a gen_fsm when it is about
+%% to terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_fsm terminates with
+%% Reason. The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(Reason, StateName, State) ->
+ ssl_connection:terminate(Reason, StateName, State).
+
+%%--------------------------------------------------------------------
+%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateName, State, _Extra) ->
+ {ok, StateName, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
+ Seq = sequence(ConnectionStates0),
+ {EncHandshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version,
+ Seq),
+ Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake),
+ {Encoded, ConnectionStates} =
+ dtls_record:encode_handshake(FragmentedHandshake,
+ Version, ConnectionStates0),
+ {Encoded, ConnectionStates, Hist}.
+
+next_record(#state{%%flight = #flight{state = finished},
+ protocol_buffers =
+ #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]}
+ = Buffers,
+ connection_states = ConnStates0} = State) ->
+ case dtls_record:decode_cipher_text(CT, ConnStates0) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end;
+next_record(#state{socket = Socket,
+ transport_cb = Transport} = State) -> %% when FlightState =/= finished
+ ssl_socket:setopts(Transport, Socket, [{active,once}]),
+ {no_record, State};
+
+
+next_record(State) ->
+ {no_record, State}.
+
+next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
+ handle_own_alert(Alert, Version, Current, State);
+
+next_state(_,Next, no_record, State) ->
+ {next_state, Next, State, get_timeout(State)};
+
+%% next_state(_,Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
+%% Alerts = decode_alerts(EncAlerts),
+%% handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
+
+next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
+ State0 = #state{protocol_buffers =
+ #protocol_buffers{dtls_handshake_buffer = Buf0} = Buffers,
+ negotiated_version = Version}) ->
+ Handle =
+ fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Starts new handshake (renegotiation)
+ Hs0 = ssl_handshake:init_handshake_history(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0,
+ renegotiation = {true, peer}});
+ ({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Already in negotiation so it will be ignored!
+ ?MODULE:SName(Packet, State);
+ ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
+ Version = Packet#client_hello.client_version,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1,
+ renegotiation = {true, peer}});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) ->
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1});
+ (_, StopState) -> StopState
+ end,
+ try
+ {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0),
+ State = State0#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_packets = Packets,
+ dtls_handshake_buffer = Buf}},
+ handle_dtls_handshake(Handle, Next, State)
+ catch throw:#alert{} = Alert ->
+ handle_own_alert(Alert, Version, Current, State0)
+ end;
+
+next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) ->
+ %% Simplify for now to avoid dialzer warnings before implementation is compleate
+ %% case read_application_data(Data, State0) of
+ %% Stop = {stop,_,_} ->
+ %% Stop;
+ %% {Record, State} ->
+ %% next_state(StateName, StateName, Record, State)
+ %% end;
+ {Record, State} = read_application_data(Data, State0),
+ next_state(StateName, StateName, Record, State);
+
+next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
+ _ChangeCipher,
+ #state{connection_states = ConnectionStates0} = State0) ->
+ ConnectionStates1 =
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}),
+ next_state(Current, Next, Record, State);
+next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) ->
+ %% Ignore unknown type
+ {Record, State} = next_record(State0),
+ next_state(Current, Next, Record, State).
+
+handle_dtls_handshake(Handle, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{dtls_packets = [Packet]} = Buffers} = State) ->
+ FsmReturn = {next_state, StateName, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_packets = []}}},
+ Handle(Packet, FsmReturn);
+
+handle_dtls_handshake(Handle, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{dtls_packets = [Packet | Packets]} = Buffers} =
+ State0) ->
+ FsmReturn = {next_state, StateName, State0#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_packets =
+ Packets}}},
+ case Handle(Packet, FsmReturn) of
+ {next_state, NextStateName, State, _Timeout} ->
+ handle_dtls_handshake(Handle, NextStateName, State);
+ {stop, _,_} = Stop ->
+ Stop
+ end.
+
+
+send_flight(Fragments, #state{transport_cb = Transport, socket = Socket,
+ protocol_buffers = _PBuffers} = State) ->
+ Transport:send(Socket, Fragments),
+ %% Start retransmission
+ %% State#state{protocol_buffers =
+ %% (PBuffers#protocol_buffers){ #flight{state = waiting}}}}.
+ State.
+
+handle_own_alert(_,_,_, State) -> %% Place holder
+ {stop, {shutdown, own_alert}, State}.
+
+handle_normal_shutdown(_, _, _State) -> %% Place holder
+ ok.
+
+encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
+ dtls_record:encode_change_cipher_spec(Version, ConnectionStates).
+
+initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
+ {CbModule, DataTag, CloseTag, ErrorTag}) ->
+ ConnectionStates = ssl_record:init_connection_states(Role),
+
+ SessionCacheCb = case application:get_env(ssl, session_cb) of
+ {ok, Cb} when is_atom(Cb) ->
+ Cb;
+ _ ->
+ ssl_session_cache
+ end,
+
+ Monitor = erlang:monitor(process, User),
+
+ #state{socket_options = SocketOptions,
+ %% We do not want to save the password in the state so that
+ %% could be written in the clear into error logs.
+ ssl_options = SSLOptions#ssl_options{password = undefined},
+ session = #session{is_resumable = new},
+ transport_cb = CbModule,
+ data_tag = DataTag,
+ close_tag = CloseTag,
+ error_tag = ErrorTag,
+ role = Role,
+ host = Host,
+ port = Port,
+ socket = Socket,
+ connection_states = ConnectionStates,
+ protocol_buffers = #protocol_buffers{},
+ user_application = {Monitor, User},
+ user_data_buffer = <<>>,
+ session_cache_cb = SessionCacheCb,
+ renegotiation = {false, first},
+ start_or_recv_from = undefined,
+ send_queue = queue:new(),
+ protocol_cb = ?MODULE
+ }.
+read_application_data(_,State) ->
+ {#ssl_tls{fragment = <<"place holder">>}, State}.
+
+next_tls_record(_, State) ->
+ {#ssl_tls{fragment = <<"place holder">>}, State}.
+
+get_timeout(_) -> %% Place holder
+ infinity.
+
+next_state_connection(_, State) -> %% Place holder
+ {next_state, connection, State, get_timeout(State)}.
+
+sequence(_) ->
+ %%TODO real imp
+ 1.
diff --git a/lib/ssl/src/dtls_connection.hrl b/lib/ssl/src/dtls_connection.hrl
index b8dff479d5..08707dc8de 100644
--- a/lib/ssl/src/dtls_connection.hrl
+++ b/lib/ssl/src/dtls_connection.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -28,24 +28,19 @@
-include("ssl_connection.hrl").
-record(protocol_buffers, {
- dtls_packets = [] ::[binary()], % Not yet handled decode ssl/tls packets.
- dtls_record_buffer :: binary(), % Buffer of incomplete records
- dtls_handshake_buffer :: binary(), % Buffer of incomplete handshakes
- dtls_cipher_texts :: [binary()],
- dtls_cipher_texts_next :: [binary()] % Received for Epoch not yet active
+ dtls_packets = [], %%::[binary()], % Not yet handled decode ssl/tls packets.
+ dtls_record_buffer = <<>>, %%:: binary(), % Buffer of incomplete records
+ dtls_handshake_buffer = <<>>, %%:: binary(), % Buffer of incomplete handshakes
+ dtls_cipher_texts = [], %%:: [binary()],
+ dtls_cipher_texts_next %%:: [binary()] % Received for Epoch not yet active
}).
-record(flight, {
last_retransmit,
last_read_seq,
msl_timer,
- flight_state,
- flight_buffer, % buffer of not yet ACKed TLS records
- }).
-
--record(message_sequences, {
- read = 0,
- write = 0
+ state,
+ buffer % buffer of not yet ACKed TLS records
}).
-endif. % -ifdef(dtls_connection).
diff --git a/lib/ssl/src/dtls_connection_sup.erl b/lib/ssl/src/dtls_connection_sup.erl
index 9fe545be18..0b4711cfb4 100644
--- a/lib/ssl/src/dtls_connection_sup.erl
+++ b/lib/ssl/src/dtls_connection_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -26,8 +26,8 @@
-behaviour(supervisor).
%% API
--export([start_link/0]).
--export([start_child/1]).
+-export([start_link/0, start_link_dist/0]).
+-export([start_child/1, start_child_dist/1]).
%% Supervisor callback
-export([init/1]).
@@ -38,8 +38,14 @@
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+start_link_dist() ->
+ supervisor:start_link({local, dtls_connection_sup_dist}, ?MODULE, []).
+
start_child(Args) ->
supervisor:start_child(?MODULE, Args).
+
+start_child_dist(Args) ->
+ supervisor:start_child(dtls_connection_sup_dist, Args).
%%%=========================================================================
%%% Supervisor callback
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 5db2434753..31d525b295 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -20,11 +20,15 @@
-include("dtls_handshake.hrl").
-include("dtls_record.hrl").
-include("ssl_internal.hrl").
+-include("ssl_alert.hrl").
--export([client_hello/8, client_hello/9, hello/3,
+-export([client_hello/8, client_hello/9, hello/4,
get_dtls_handshake/2,
- dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
- encode_handshake/4]).
+ %%dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
+ encode_handshake/3]).
+
+-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} |
+ ssl_handshake:ssl_handshake().
%%====================================================================
%% Internal application API
@@ -54,12 +58,12 @@ client_hello(Host, Port, Cookie, ConnectionStates,
ciphers = UserSuites
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
- Version = dtls_record:highest_protocol_version(Versions),
+ Version = dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
CipherSuites = ssl_handshake:available_suites(UserSuites, Version),
- Extensions = ssl_handshake:client_hello_extensions(Host, Version, CipherSuites,
+ Extensions = ssl_handshake:client_hello_extensions(Host, dtls_v1:corresponding_tls_version(Version), CipherSuites,
SslOpts, ConnectionStates, Renegotiation),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
@@ -73,163 +77,197 @@ client_hello(Host, Port, Cookie, ConnectionStates,
extensions = Extensions
}.
-hello(Address, Port,
- #ssl_tls{epoch = _Epoch, record_seq = _Seq,
- version = Version} = Record) ->
- {[{Hello, _}], _, _} =
- get_dtls_handshake(Record,
- dtls_handshake_new_flight(undefined)),
- #client_hello{client_version = {Major, Minor},
- random = Random,
- session_id = SessionId,
- cipher_suites = CipherSuites,
- compression_methods = CompressionMethods} = Hello,
- CookieData = [address_to_bin(Address, Port),
- <<?BYTE(Major), ?BYTE(Minor)>>,
- Random, SessionId, CipherSuites, CompressionMethods],
- Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
-
- case Hello of
- #client_hello{cookie = Cookie} ->
- accept;
- _ ->
- %% generate HelloVerifyRequest
- HelloVerifyRequest = encode_handshake(#hello_verify_request{protocol_version = Version,
- cookie = Cookie},
- Version, 0, 1400),
- {reply, HelloVerifyRequest}
- end.
+hello(#server_hello{server_version = Version, random = Random,
+ cipher_suite = CipherSuite,
+ compression_method = Compression,
+ session_id = SessionId, extensions = HelloExt},
+ #ssl_options{versions = SupportedVersions} = SslOpt,
+ ConnectionStates0, Renegotiation) ->
+ case dtls_record:is_acceptable_version(Version, SupportedVersions) of
+ true ->
+ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation);
+ false ->
+ ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
+ end;
-%%--------------------------------------------------------------------
-encode_handshake(Package, Version, MsgSeq, Mss) ->
- {MsgType, Bin} = enc_hs(Package, Version),
+hello(#client_hello{client_version = ClientVersion}, _Options, {_,_,_,_,ConnectionStates,_}, _Renegotiation) ->
+ %% Return correct typ to make dialyzer happy until we have time to make the real imp.
+ {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{}}.
+
+%% hello(Address, Port,
+%% #ssl_tls{epoch = _Epoch, sequence_number = _Seq,
+%% version = Version} = Record) ->
+%% case get_dtls_handshake(Record,
+%% dtls_handshake_new_flight(undefined)) of
+%% {[Hello | _], _} ->
+%% hello(Address, Port, Version, Hello);
+%% {retransmit, HandshakeState} ->
+%% {retransmit, HandshakeState}
+%% end.
+
+%% hello(Address, Port, Version, Hello) ->
+%% #client_hello{client_version = {Major, Minor},
+%% random = Random,
+%% session_id = SessionId,
+%% cipher_suites = CipherSuites,
+%% compression_methods = CompressionMethods} = Hello,
+%% CookieData = [address_to_bin(Address, Port),
+%% <<?BYTE(Major), ?BYTE(Minor)>>,
+%% Random, SessionId, CipherSuites, CompressionMethods],
+%% Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
+
+%% case Hello of
+%% #client_hello{cookie = Cookie} ->
+%% accept;
+%% _ ->
+%% %% generate HelloVerifyRequest
+%% HelloVerifyRequest = enc_hs(#hello_verify_request{protocol_version = Version,
+%% cookie = Cookie},
+%% Version, 0, 1400),
+%% {reply, HelloVerifyRequest}
+%% end.
+
+%% %%--------------------------------------------------------------------
+encode_handshake(Handshake, Version, MsgSeq) ->
+ {MsgType, Bin} = enc_handshake(Handshake, Version),
Len = byte_size(Bin),
- HsHistory = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
- BinMsg = dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, 0, []),
- {HsHistory, BinMsg}.
+ EncHandshake = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
+ FragmentedHandshake = dtls_fragment(erlang:iolist_size(EncHandshake), MsgType, Len, MsgSeq, Bin, 0, []),
+ {EncHandshake, FragmentedHandshake}.
-%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
-spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | binary()) ->
- {[dtls_handshake()], #dtls_hs_state{}} | {retransmit, #dtls_hs_state{}}.
-%
-% Description: Given a DTLS state and new data from ssl_record, collects
-% and returns it as a list of handshake messages, also returns a new
-% DTLS state
-%--------------------------------------------------------------------
-% get_dtls_handshake(Record, <<>>) ->
-% get_dtls_handshake_aux(Record, dtls_hs_state_init());
+ {[dtls_handshake()], #dtls_hs_state{}} | {retransmit, #dtls_hs_state{}}.
+%%
+%% Description: Given a DTLS state and new data from ssl_record, collects
+%% and returns it as a list of handshake messages, also returns a new
+%% DTLS state
+%%--------------------------------------------------------------------
+get_dtls_handshake(Record, <<>>) ->
+ get_dtls_handshake_aux(Record, #dtls_hs_state{}); %% Init handshake state!?
get_dtls_handshake(Record, HsState) ->
get_dtls_handshake_aux(Record, HsState).
-%--------------------------------------------------------------------
--spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
-%
-% Description: Reset the DTLS decoder state for a new Epoch
-%--------------------------------------------------------------------
-% dtls_handshake_new_epoch(<<>>) ->
-% dtls_hs_state_init();
-dtls_handshake_new_epoch(HsState) ->
- HsState#dtls_hs_state{highest_record_seq = 0,
- starting_read_seq = HsState#dtls_hs_state.current_read_seq,
- fragments = gb_trees:empty(), completed = []}.
-
-%--------------------------------------------------------------------
--spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
-%
-% Description: Init the DTLS decoder state for a new Flight
-dtls_handshake_new_flight(ExpectedReadReq) ->
- #dtls_hs_state{current_read_seq = ExpectedReadReq,
- highest_record_seq = 0,
- starting_read_seq = 0,
- fragments = gb_trees:empty(), completed = []}.
+%% %%--------------------------------------------------------------------
+%% -spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
+%% %%
+%% %% Description: Reset the DTLS decoder state for a new Epoch
+%% %%--------------------------------------------------------------------
+%% dtls_handshake_new_epoch(<<>>) ->
+%% dtls_hs_state_init();
+%% dtls_handshake_new_epoch(HsState) ->
+%% HsState#dtls_hs_state{highest_record_seq = 0,
+%% starting_read_seq = HsState#dtls_hs_state.current_read_seq,
+%% fragments = gb_trees:empty(), completed = []}.
+
+%% %--------------------------------------------------------------------
+%% -spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
+%% %
+%% % Description: Init the DTLS decoder state for a new Flight
+%% dtls_handshake_new_flight(ExpectedReadReq) ->
+%% #dtls_hs_state{current_read_seq = ExpectedReadReq,
+%% highest_record_seq = 0,
+%% starting_read_seq = 0,
+%% fragments = gb_trees:empty(), completed = []}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
+ case ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite,
+ Compression, HelloExt, Version,
+ SslOpt, ConnectionStates0, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ {ConnectionStates, Protocol} ->
+ {Version, SessionId, ConnectionStates, Protocol}
+ end.
-dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
+dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
when byte_size(Bin) + 12 < Mss ->
FragmentLen = byte_size(Bin),
BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Bin],
lists:reverse([BinMsg|Acc]);
-dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
+dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
FragmentLen = Mss - 12,
<<Fragment:FragmentLen/bytes, Rest/binary>> = Bin,
BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Fragment],
- dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
+ dtls_fragment(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
get_dtls_handshake_aux(#ssl_tls{version = Version,
- record_seq = SeqNo,
- fragment = Data}, HsState) ->
+ sequence_number = SeqNo,
+ fragment = Data}, HsState) ->
get_dtls_handshake_aux(Version, SeqNo, Data, HsState).
get_dtls_handshake_aux(Version, SeqNo,
- <<?BYTE(Type), ?UINT24(Length),
- ?UINT16(MessageSeq),
- ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
- Body:FragmentLength/binary, Rest/binary>>,
- HsState0) ->
+ <<?BYTE(Type), ?UINT24(Length),
+ ?UINT16(MessageSeq),
+ ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
+ Body:FragmentLength/binary, Rest/binary>>,
+ HsState0) ->
case reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState0) of
- {retransmit, HsState1} ->
- case Rest of
- <<>> ->
- {retransmit, HsState1};
- _ ->
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState1)
- end;
- {HsState1, HighestSeqNo, MsgBody} ->
- HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
- HsState3 = process_dtls_fragments(Version, HsState2),
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
- HsState2 ->
- HsState3 = process_dtls_fragments(Version, HsState2),
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
- end;
+ FragmentOffset, FragmentLength,
+ Body, HsState0) of
+ {retransmit, HsState1} ->
+ case Rest of
+ <<>> ->
+ {retransmit, HsState1};
+ _ ->
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState1)
+ end;
+ {HsState1, HighestSeqNo, MsgBody} ->
+ HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
+ HsState3 = process_dtls_fragments(Version, HsState2),
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
+ HsState2 ->
+ HsState3 = process_dtls_fragments(Version, HsState2),
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
+ end;
get_dtls_handshake_aux(_Version, _SeqNo, <<>>, HsState) ->
- {lists:reverse(HsState#dtls_hs_state.completed),
- HsState#dtls_hs_state{completed = []}}.
+ {lists:reverse(HsState#dtls_hs_state.completed),
+ HsState#dtls_hs_state{completed = []}}.
dec_dtls_fragment(Version, SeqNo, Type, Length, MessageSeq, MsgBody,
- HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
+ HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
Raw = <<?BYTE(Type), ?UINT24(Length), ?UINT16(MessageSeq), ?UINT24(0), ?UINT24(Length), MsgBody/binary>>,
H = decode_handshake(Version, Type, MsgBody),
HsState#dtls_hs_state{completed = [{H,Raw}|Acc], highest_record_seq = erlang:max(HighestSeqNo, SeqNo)}.
process_dtls_fragments(Version,
- HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
- fragments = Fragments0}) ->
+ HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
+ fragments = Fragments0}) ->
case gb_trees:is_empty(Fragments0) of
- true ->
- HsState0;
- _ ->
- case gb_trees:smallest(Fragments0) of
- {CurrentReadSeq, {SeqNo, Type, Length, CurrentReadSeq, {Length, [{0, Length}], MsgBody}}} ->
- HsState1 = dtls_hs_state_process_seq(HsState0),
- HsState2 = dec_dtls_fragment(Version, SeqNo, Type, Length, CurrentReadSeq, MsgBody, HsState1),
- process_dtls_fragments(Version, HsState2);
- _ ->
- HsState0
- end
- end.
+ true ->
+ HsState0;
+ _ ->
+ case gb_trees:smallest(Fragments0) of
+ {CurrentReadSeq, {SeqNo, Type, Length, CurrentReadSeq, {Length, [{0, Length}], MsgBody}}} ->
+ HsState1 = dtls_hs_state_process_seq(HsState0),
+ HsState2 = dec_dtls_fragment(Version, SeqNo, Type, Length, CurrentReadSeq, MsgBody, HsState1),
+ process_dtls_fragments(Version, HsState2);
+ _ ->
+ HsState0
+ end
+ end.
dtls_hs_state_process_seq(HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
- fragments = Fragments0}) ->
+ fragments = Fragments0}) ->
Fragments1 = gb_trees:delete_any(CurrentReadSeq, Fragments0),
HsState0#dtls_hs_state{current_read_seq = CurrentReadSeq + 1,
- fragments = Fragments1}.
+ fragments = Fragments1}.
dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState0 = #dtls_hs_state{fragments = Fragments0}) ->
Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
HsState0#dtls_hs_state{fragments = Fragments1}.
reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
- Body, HsState0 = #dtls_hs_state{current_read_seq = undefined})
+ Body, HsState0 = #dtls_hs_state{current_read_seq = undefined})
when Type == ?CLIENT_HELLO;
Type == ?SERVER_HELLO;
- Type == ?HELLO_VERIFY_REQUEST ->
+ Type == ?HELLO_VERIFY_REQUEST ->
%% First message, should be client hello
%% return the current message and set the next expected Sequence
%%
@@ -245,8 +283,8 @@ reassemble_dtls_fragment(_SeqNo, _Type, Length, _MessageSeq, _, Length,
HsState;
reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
- Body, HsState0 =
- #dtls_hs_state{starting_read_seq = StartingReadSeq})
+ Body, HsState0 =
+ #dtls_hs_state{starting_read_seq = StartingReadSeq})
when MessageSeq < StartingReadSeq ->
%% this has to be the start of a new flight, let it through
%%
@@ -257,69 +295,69 @@ reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
{HsState, SeqNo, Body};
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
- _Body, HsState =
- #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body, HsState =
+ #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
{retransmit, HsState};
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
- _Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
HsState;
reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
- Body, HsState0 = #dtls_hs_state{current_read_seq = MessageSeq}) ->
+ Body, HsState0 = #dtls_hs_state{current_read_seq = MessageSeq}) ->
%% Message fully contained and it's the current seq
HsState1 = dtls_hs_state_process_seq(HsState0),
{HsState1, SeqNo, Body};
reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
- Body, HsState) ->
+ Body, HsState) ->
%% Message fully contained and it's the NOT the current seq -> buffer
Fragment = {SeqNo, Type, Length, MessageSeq,
- dtls_fragment_init(Length, 0, Length, Body)},
+ dtls_fragment_init(Length, 0, Length, Body)},
dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState);
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, FragmentOffset, FragmentLength,
- _Body,
- HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body,
+ HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when FragmentOffset + FragmentLength == Length andalso MessageSeq == (CurrentReadSeq - 1) ->
{retransmit, HsState};
reassemble_dtls_fragment(_SeqNo, _Type, _Length, MessageSeq, _FragmentOffset, _FragmentLength,
- _Body,
- HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body,
+ HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
HsState;
reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body,
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ FragmentOffset, FragmentLength,
+ Body,
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
case gb_trees:lookup(MessageSeq, Fragments0) of
- {value, Fragment} ->
- dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, Fragment, HsState);
- none ->
- dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState)
+ {value, Fragment} ->
+ dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, Fragment, HsState);
+ none ->
+ dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, HsState)
end.
dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ FragmentOffset, FragmentLength,
+ Body, HsState = #dtls_hs_state{fragments = Fragments0}) ->
Fragment = {SeqNo, Type, Length, MessageSeq,
- dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body)},
- Fragments1 = gb_trees:insert(MessageSeq, Fragment, Fragments0),
+ dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body)},
+ Fragments1 = gb_trees:insert(MessageSeq, Fragment, Fragments0),
HsState#dtls_hs_state{fragments = Fragments1}.
dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
FragmentOffset, FragmentLength,
- Body,
- {LastSeqNo, Type, Length, MessageSeq, FragBuffer0},
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ Body,
+ {LastSeqNo, Type, Length, MessageSeq, FragBuffer0},
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
FragBuffer1 = dtls_fragment_add(FragBuffer0, FragmentOffset, FragmentLength, Body),
Fragment = {erlang:max(SeqNo, LastSeqNo), Type, Length, MessageSeq, FragBuffer1},
Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
@@ -328,8 +366,8 @@ dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
%% Type, Length or Seq mismatch, drop everything...
%% Note: the RFC is not clear on how to handle this...
dtls_fragment_reassemble(_SeqNo, _Type, _Length, MessageSeq,
- _FragmentOffset, _FragmentLength, _Body, _Fragment,
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ _FragmentOffset, _FragmentLength, _Body, _Fragment,
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
Fragments1 = gb_trees:delete_any(MessageSeq, Fragments0),
HsState#dtls_hs_state{fragments = Fragments1}.
@@ -360,7 +398,7 @@ merge_fragment_list(Rest = [{HStart, _HEnd}|_], Frag = {_FStart, FEnd}, Acc)
lists:reverse(Acc) ++ [Frag|Rest];
merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
- when
+ when
FStart =< HEnd orelse FEnd >= HStart ->
Start = erlang:min(HStart, FStart),
End = erlang:max(HEnd, FEnd),
@@ -370,20 +408,20 @@ merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
add_fragment(List, {FragmentOffset, FragmentLength}) ->
merge_fragment_list(List, {FragmentOffset, FragmentOffset + FragmentLength}, []).
-enc_hs(#hello_verify_request{protocol_version = {Major, Minor},
- cookie = Cookie}, _Version) ->
- CookieLength = byte_size(Cookie),
+enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
+ cookie = Cookie}, _Version) ->
+ CookieLength = byte_size(Cookie),
{?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
- ?BYTE(CookieLength),
- Cookie/binary>>};
-
-enc_hs(#client_hello{client_version = {Major, Minor},
- random = Random,
- session_id = SessionID,
- cookie = Cookie,
- cipher_suites = CipherSuites,
- compression_methods = CompMethods,
- extensions = HelloExtensions}, Version) ->
+ ?BYTE(CookieLength),
+ Cookie/binary>>};
+
+enc_handshake(#client_hello{client_version = {Major, Minor},
+ random = Random,
+ session_id = SessionID,
+ cookie = Cookie,
+ cipher_suites = CipherSuites,
+ compression_methods = CompMethods,
+ extensions = HelloExtensions}, Version) ->
SIDLength = byte_size(SessionID),
BinCookie = enc_client_hello_cookie(Version, Cookie),
BinCompMethods = list_to_binary(CompMethods),
@@ -391,13 +429,13 @@ enc_hs(#client_hello{client_version = {Major, Minor},
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
-
+
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SIDLength), SessionID/binary,
- BinCookie/binary,
- ?UINT16(CsLength), BinCipherSuites/binary,
- ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-enc_hs(HandshakeMsg, Version) ->
+ ?BYTE(SIDLength), SessionID/binary,
+ BinCookie/binary,
+ ?UINT16(CsLength), BinCipherSuites/binary,
+ ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
+enc_handshake(HandshakeMsg, Version) ->
ssl_handshake:encode_handshake(HandshakeMsg, Version).
enc_client_hello_cookie(_, <<>>) ->
@@ -407,26 +445,26 @@ enc_client_hello_cookie(_, Cookie) ->
<<?BYTE(CookieLength), Cookie/binary>>.
decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID:SID_length/binary,
- ?BYTE(Cookie_length), Cookie:Cookie_length/binary,
- ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
- ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>) ->
-
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ ?BYTE(Cookie_length), Cookie:Cookie_length/binary,
+ ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
+ ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
+ Extensions/binary>>) ->
+
DecodedExtensions = ssl_handshake:decode_hello_extensions(Extensions),
-
+
#client_hello{
client_version = {Major,Minor},
random = Random,
- session_id = Session_ID,
+ session_id = Session_ID,
cookie = Cookie,
cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
compression_methods = Comp_methods,
extensions = DecodedExtensions
- };
+ };
decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
- ?BYTE(CookieLength), Cookie:CookieLength/binary>>) ->
+ ?BYTE(CookieLength), Cookie:CookieLength/binary>>) ->
#hello_verify_request{
protocol_version = {Major,Minor},
@@ -434,7 +472,7 @@ decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
decode_handshake(Version, Tag, Msg) ->
ssl_handshake:decode_handshake(Version, Tag, Msg).
-address_to_bin({A,B,C,D}, Port) ->
- <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
-address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
- <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
+%% address_to_bin({A,B,C,D}, Port) ->
+%% <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
+%% address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
+%% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl
index 5bdf45f627..3b57575b6d 100644
--- a/lib/ssl/src/dtls_handshake.hrl
+++ b/lib/ssl/src/dtls_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -53,6 +53,4 @@
completed
}).
--type dtls_handshake() :: #client_hello{} | #hello_verify_request{} | ssl_handshake().
-
-endif. % -ifdef(dtls_handshake).
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index b0a7976864..a7bbb6bc40 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -35,7 +35,7 @@
-export([decode_cipher_text/2]).
%% Encoding
--export([encode_plain_text/4]).
+-export([encode_plain_text/4, encode_handshake/3, encode_change_cipher_spec/2]).
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/2,
@@ -46,6 +46,11 @@
-export([init_connection_state_seq/2, current_connection_state_epoch/2,
set_connection_state_by_epoch/3, connection_state_by_epoch/3]).
+-export_type([dtls_version/0, dtls_atom_version/0]).
+
+-type dtls_version() :: ssl_record:ssl_version().
+-type dtls_atom_version() :: dtlsv1 | 'dtlsv1.2'.
+
-compile(inline).
%%====================================================================
@@ -70,7 +75,7 @@ get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -78,7 +83,7 @@ get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -86,7 +91,7 @@ get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
Rest/binary>>, Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -94,7 +99,7 @@ get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
@@ -125,14 +130,15 @@ encode_plain_text(Type, Version, Data,
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
WriteState1 = WriteState0#connection_state{compression_state = CompS1},
MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp),
- {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash),
+ {CipherFragment, WriteState} = ssl_record:cipher(dtls_v1:corresponding_tls_version(Version),
+ Comp, WriteState1, MacHash),
CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
{CipherText, ConnectionStates#connection_states{current_write =
WriteState#connection_state{sequence_number = Seq +1}}}.
decode_cipher_text(#ssl_tls{type = Type, version = Version,
epoch = Epoch,
- record_seq = Seq,
+ sequence_number = Seq,
fragment = CipherFragment} = CipherText,
#connection_states{current_read =
#connection_state{compression_state = CompressionS0,
@@ -141,7 +147,7 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
CompressAlg = SecParams#security_parameters.compression_algorithm,
{PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
CipherFragment, ReadState0),
- MacHash = calc_mac_hash(Type, Version, Epoch, Seq, PlainFragment, ReadState1),
+ MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment),
case ssl_record:is_correct_mac(Mac, MacHash) of
true ->
{Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
@@ -153,10 +159,27 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
+%%--------------------------------------------------------------------
+-spec encode_handshake(iolist(), dtls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a handshake message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_handshake(Frag, Version, ConnectionStates) ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
+
+%%--------------------------------------------------------------------
+-spec encode_change_cipher_spec(dtls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
+%%--------------------------------------------------------------------
+encode_change_cipher_spec(Version, ConnectionStates) ->
+ encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
--spec protocol_version(tls_atom_version() | tls_version()) ->
- tls_version() | tls_atom_version().
+-spec protocol_version(dtls_atom_version() | dtls_version()) ->
+ dtls_version() | dtls_atom_version().
%%
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
@@ -170,7 +193,7 @@ protocol_version({254, 253}) ->
protocol_version({254, 255}) ->
dtlsv1.
%%--------------------------------------------------------------------
--spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version().
+-spec lowest_protocol_version(dtls_version(), dtls_version()) -> dtls_version().
%%
%% Description: Lowes protocol version of two given versions
%%--------------------------------------------------------------------
@@ -183,7 +206,7 @@ lowest_protocol_version(Version = {M,_}, {N, _}) when M > N ->
lowest_protocol_version(_,Version) ->
Version.
%%--------------------------------------------------------------------
--spec highest_protocol_version([tls_version()]) -> tls_version().
+-spec highest_protocol_version([dtls_version()]) -> dtls_version().
%%
%% Description: Highest protocol version present in a list
%%--------------------------------------------------------------------
@@ -203,7 +226,7 @@ highest_protocol_version(_, [Version | Rest]) ->
%%--------------------------------------------------------------------
--spec supported_protocol_versions() -> [tls_version()].
+-spec supported_protocol_versions() -> [dtls_version()].
%%
%% Description: Protocol versions supported
%%--------------------------------------------------------------------
@@ -234,7 +257,7 @@ supported_connection_protocol_versions([]) ->
?ALL_DATAGRAM_SUPPORTED_VERSIONS.
%%--------------------------------------------------------------------
--spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean().
+-spec is_acceptable_version(dtls_version(), Supported :: [dtls_version()]) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
%%
@@ -244,7 +267,7 @@ is_acceptable_version(Version, Versions) ->
%%--------------------------------------------------------------------
--spec init_connection_state_seq(tls_version(), #connection_states{}) ->
+-spec init_connection_state_seq(dtls_version(), #connection_states{}) ->
#connection_state{}.
%%
%% Description: Copy the read sequence number to the write sequence number
@@ -343,5 +366,5 @@ calc_mac_hash(#connection_state{mac_secret = MacSecret,
Length, Fragment).
mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- dtls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ dtls_v1:mac_hash(Version, MacAlg, MacSecret, SeqNo, Type,
Length, Fragment).
diff --git a/lib/ssl/src/dtls_record.hrl b/lib/ssl/src/dtls_record.hrl
index e935d84bdf..edb77fb2b1 100644
--- a/lib/ssl/src/dtls_record.hrl
+++ b/lib/ssl/src/dtls_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -28,16 +28,15 @@
-include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes
-%% Used to handle tls_plain_text, tls_compressed and tls_cipher_text
+%% Used to handle dtls_plain_text, dtls_compressed and dtls_cipher_text
-record(ssl_tls, {
type,
version,
- record_seq, % used in plain_text
- epoch, % used in plain_text
- message_seq,
- fragment_offset,
- fragment_length,
+ epoch,
+ sequence_number,
+ offset,
+ length,
fragment
}).
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index 6e41641483..5a7ab32887 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -22,7 +22,7 @@
-export([suites/1, mac_hash/7, ecc_curves/1, corresponding_tls_version/1]).
--spec suites(Minor:: 253|255) -> [cipher_suite()].
+-spec suites(Minor:: 253|255) -> [ssl_cipher:cipher_suite()].
suites(Minor) ->
tls_v1:suites(corresponding_minor_tls_version(Minor)).
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 68ebc49e4a..99839f6149 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -47,6 +47,8 @@
{registered, [ssl_sup, ssl_manager]},
{applications, [crypto, public_key, kernel, stdlib]},
{env, []},
- {mod, {ssl_app, []}}]}.
+ {mod, {ssl_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","public_key-0.22","kernel-3.0",
+ "erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 3a64841976..b0ef292c4e 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,27 +1,36 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"5.3.2">>, [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {<<"5.3.1">>, [{restart_application, ssl}]},
- {<<"5.2\\*">>, [{restart_application, ssl}]},
- {<<"5.1\\*">>, [{restart_application, ssl}]},
- {<<"5.0\\*">>, [{restart_application, ssl}]},
- {<<"4\\.*">>, [{restart_application, ssl}]},
- {<<"3\\.*">>, [{restart_application, ssl}]}
+ {"5.3.3", [{load_module, ssl, soft_purge, soft_purge, []},
+ {load_module, ssl_connection, soft_purge, soft_purge, []},
+ {load_module, ssl_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_connection, soft_purge, soft_purge, []}]},
+ {"5.3.2", [{load_module, ssl, soft_purge, soft_purge, []},
+ {load_module, ssl_connection, soft_purge, soft_purge, []},
+ {load_module, ssl_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_connection, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.1($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"4\\..*">>, [{restart_application, ssl}]},
+ {<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"5.3.2">>, [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {<<"5.3.1">>, [{restart_application, ssl}]},
- {<<"5.2\\*">>, [{restart_application, ssl}]},
- {<<"5.1\\*">>, [{restart_application, ssl}]},
- {<<"5.0\\*">>, [{restart_application, ssl}]},
- {<<"4\\.*">>, [{restart_application, ssl}]},
- {<<"3\\.*">>, [{restart_application, ssl}]}
- ]}.
+ {"5.3.3", [{load_module, ssl, soft_purge, soft_purge, []},
+ {load_module, ssl_connection, soft_purge, soft_purge, []},
+ {load_module, ssl_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_connection, soft_purge, soft_purge, []}]},
+ {"5.3.2", [{load_module, ssl, soft_purge, soft_purge, []},
+ {load_module, ssl_connection, soft_purge, soft_purge, []},
+ {load_module, ssl_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_handshake, soft_purge, soft_purge, []},
+ {load_module, tls_connection, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.1($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"4\\..*">>, [{restart_application, ssl}]},
+ {<<"3\\..*">>, [{restart_application, ssl}]}
+ ]
+}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 8c2b84bc1e..a88bf45293 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -276,7 +276,7 @@ controlling_process(#sslsocket{pid = {Listen,
Transport:controlling_process(Listen, NewOwner).
%%--------------------------------------------------------------------
--spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} |
+-spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} |
{error, reason()}.
%%
%% Description: Returns ssl protocol and cipher used for the connection
@@ -312,7 +312,7 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
{error, enotconn}.
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec suite_definition(ssl_cipher:cipher_suite()) -> ssl_cipher:erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%%--------------------------------------------------------------------
@@ -330,8 +330,8 @@ negotiated_next_protocol(#sslsocket{pid = Pid}) ->
ssl_connection:negotiated_next_protocol(Pid).
%%--------------------------------------------------------------------
--spec cipher_suites() -> [erl_cipher_suite()].
--spec cipher_suites(erlang | openssl | all) -> [erl_cipher_suite()] | [string()].
+-spec cipher_suites() -> [ssl_cipher:erl_cipher_suite()].
+-spec cipher_suites(erlang | openssl | all) -> [ssl_cipher:erl_cipher_suite()] | [string()].
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
@@ -437,8 +437,8 @@ session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
{error, enotconn}.
%%---------------------------------------------------------------
--spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} |
- {available, [tls_atom_version()]}].
+-spec versions() -> [{ssl_app, string()} | {supported, [tls_record:tls_atom_version()]} |
+ {available, [tls_record:tls_atom_version()]}].
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
@@ -557,6 +557,9 @@ do_connect(Address, Port,
handle_options(Opts0, _Role) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
+ assert_proplist(Opts),
+ RecordCb = record_cb(Opts),
+
ReuseSessionFun = fun(_, _, _, _) -> true end,
DefaultVerifyNoneFun =
@@ -599,12 +602,14 @@ handle_options(Opts0, _Role) ->
end,
CertFile = handle_option(certfile, Opts, <<>>),
-
+
+ RecordCb = record_cb(Opts),
+
Versions = case handle_option(versions, Opts, []) of
[] ->
- tls_record:supported_protocol_versions();
+ RecordCb:supported_protocol_versions();
Vsns ->
- [tls_record:protocol_version(Vsn) || Vsn <- Vsns]
+ [RecordCb:protocol_version(Vsn) || Vsn <- Vsns]
end,
SSLOptions = #ssl_options{
@@ -626,7 +631,7 @@ handle_options(Opts0, _Role) ->
user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined),
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
- ciphers = handle_option(ciphers, Opts, []),
+ ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), hd(Versions)),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
@@ -712,7 +717,7 @@ validate_option(certfile, undefined = Value) ->
validate_option(certfile, Value) when is_binary(Value) ->
Value;
validate_option(certfile, Value) when is_list(Value) ->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(key, undefined) ->
undefined;
@@ -729,7 +734,7 @@ validate_option(keyfile, undefined) ->
validate_option(keyfile, Value) when is_binary(Value) ->
Value;
validate_option(keyfile, Value) when is_list(Value), Value =/= "" ->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(password, Value) when is_list(Value) ->
Value;
@@ -743,7 +748,7 @@ validate_option(cacertfile, undefined) ->
validate_option(cacertfile, Value) when is_binary(Value) ->
Value;
validate_option(cacertfile, Value) when is_list(Value), Value =/= ""->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(dh, Value) when Value == undefined;
is_binary(Value) ->
Value;
@@ -752,12 +757,12 @@ validate_option(dhfile, undefined = Value) ->
validate_option(dhfile, Value) when is_binary(Value) ->
Value;
validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
- list_to_binary(Value);
+ binary_filename(Value);
validate_option(psk_identity, undefined) ->
undefined;
validate_option(psk_identity, Identity)
when is_list(Identity), Identity =/= "", length(Identity) =< 65535 ->
- list_to_binary(Identity);
+ binary_filename(Identity);
validate_option(user_lookup_fun, undefined) ->
undefined;
validate_option(user_lookup_fun, {Fun, _} = Value) when is_function(Fun, 3) ->
@@ -766,17 +771,9 @@ validate_option(srp_identity, undefined) ->
undefined;
validate_option(srp_identity, {Username, Password})
when is_list(Username), is_list(Password), Username =/= "", length(Username) =< 255 ->
- {list_to_binary(Username), list_to_binary(Password)};
+ {unicode:characters_to_binary(Username),
+ unicode:characters_to_binary(Password)};
-validate_option(ciphers, Value) when is_list(Value) ->
- Version = tls_record:highest_protocol_version([]),
- try cipher_suites(Version, Value)
- catch
- exit:_ ->
- throw({error, {options, {ciphers, Value}}});
- error:_->
- throw({error, {options, {ciphers, Value}}})
- end;
validate_option(reuse_session, Value) when is_function(Value) ->
Value;
validate_option(reuse_sessions, Value) when is_boolean(Value) ->
@@ -936,16 +933,26 @@ emulated_options([Opt|Opts], Inet, Emulated) ->
emulated_options([], Inet,Emulated) ->
{Inet, Emulated}.
-cipher_suites(Version, []) ->
+handle_cipher_option(Value, Version) when is_list(Value) ->
+ try binary_cipher_suites(Version, Value) of
+ Suites ->
+ Suites
+ catch
+ exit:_ ->
+ throw({error, {options, {ciphers, Value}}});
+ error:_->
+ throw({error, {options, {ciphers, Value}}})
+ end.
+binary_cipher_suites(Version, []) -> %% Defaults to all supported suits
ssl_cipher:suites(Version);
-cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
+binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0],
- cipher_suites(Version, Ciphers);
-cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
+ binary_cipher_suites(Version, Ciphers);
+binary_cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
- cipher_suites(Version, Ciphers);
+ binary_cipher_suites(Version, Ciphers);
-cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
+binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
Supported0 = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites()
++ ssl_cipher:psk_suites(Version)
@@ -953,18 +960,18 @@ cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
Supported = ssl_cipher:filter_suites(Supported0),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of
[] ->
- Supported;
+ Supported; %% Defaults to all supported suits
Ciphers ->
Ciphers
end;
-cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
+binary_cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
%% Format: ["RC4-SHA","RC4-MD5"]
Ciphers = [ssl_cipher:openssl_suite(C) || C <- Ciphers0],
- cipher_suites(Version, Ciphers);
-cipher_suites(Version, Ciphers0) ->
+ binary_cipher_suites(Version, Ciphers);
+binary_cipher_suites(Version, Ciphers0) ->
%% Format: "RC4-SHA:RC4-MD5"
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
- cipher_suites(Version, Ciphers).
+ binary_cipher_suites(Version, Ciphers).
unexpected_format(Error) ->
lists:flatten(io_lib:format("Unexpected error: ~p", [Error])).
@@ -1032,7 +1039,30 @@ connection_cb(dtls) ->
connection_cb(Opts) ->
connection_cb(proplists:get_value(protocol, Opts, tls)).
+record_cb(tls) ->
+ tls_record;
+record_cb(dtls) ->
+ dtls_record;
+record_cb(Opts) ->
+ record_cb(proplists:get_value(protocol, Opts, tls)).
+
connection_sup(tls_connection) ->
tls_connection_sup;
connection_sup(dtls_connection) ->
dtls_connection_sup.
+
+binary_filename(FileName) ->
+ Enc = file:native_name_encoding(),
+ unicode:characters_to_binary(FileName, unicode, Enc).
+
+assert_proplist([]) ->
+ true;
+assert_proplist([{Key,_} | Rest]) when is_atom(Key) ->
+ assert_proplist(Rest);
+%% Handle exceptions
+assert_proplist([inet | Rest]) ->
+ assert_proplist(Rest);
+assert_proplist([inet6 | Rest]) ->
+ assert_proplist(Rest);
+assert_proplist([Value | _]) ->
+ throw({option_not_a_key_value_tuple, Value}).
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 5c842b4d19..db1535b5ec 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -38,7 +38,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec encode(#alert{}, tls_version(), #connection_states{}) ->
+-spec encode(#alert{}, ssl_record:ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description:
@@ -124,5 +124,17 @@ description_txt(?USER_CANCELED) ->
"user canceled";
description_txt(?NO_RENEGOTIATION) ->
"no renegotiation";
+description_txt(?UNSUPPORTED_EXTENSION) ->
+ "unsupported extension";
+description_txt(?CERTIFICATE_UNOBTAINABLE) ->
+ "certificate unobtainable";
+description_txt(?UNRECOGNISED_NAME) ->
+ "unrecognised name";
+description_txt(?BAD_CERTIFICATE_STATUS_RESPONSE) ->
+ "bad certificate status response";
+description_txt(?BAD_CERTIFICATE_HASH_VALUE) ->
+ "bad certificate hash value";
description_txt(?UNKNOWN_PSK_IDENTITY) ->
- "unknown psk identity".
+ "unknown psk identity";
+description_txt(Enum) ->
+ lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])).
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 2a8a91aefa..2d1f323085 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -48,7 +48,7 @@
%% unsupported_certificate(43),
%% certificate_revoked(44),
%% certificate_expired(45),
- %% certificate_unknown(46),
+%% certificate_unknown(46),
%% illegal_parameter(47),
%% unknown_ca(48),
%% access_denied(49),
@@ -60,6 +60,13 @@
%% internal_error(80),
%% user_canceled(90),
%% no_renegotiation(100),
+%% RFC 4366
+%% unsupported_extension(110),
+%% certificate_unobtainable(111),
+%% unrecognized_name(112),
+%% bad_certificate_status_response(113),
+%% bad_certificate_hash_value(114),
+%% RFC 4366
%% unknown_psk_identity(115),
%% (255)
%% } AlertDescription;
@@ -88,6 +95,11 @@
-define(INTERNAL_ERROR, 80).
-define(USER_CANCELED, 90).
-define(NO_RENEGOTIATION, 100).
+-define(UNSUPPORTED_EXTENSION, 110).
+-define(CERTIFICATE_UNOBTAINABLE, 111).
+-define(UNRECOGNISED_NAME, 112).
+-define(BAD_CERTIFICATE_STATUS_RESPONSE, 113).
+-define(BAD_CERTIFICATE_HASH_VALUE, 114).
-define(UNKNOWN_PSK_IDENTITY, 115).
-define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}).
diff --git a/lib/ssl/src/ssl_api.hrl b/lib/ssl/src/ssl_api.hrl
index 607991750f..22185ff60a 100644
--- a/lib/ssl/src/ssl_api.hrl
+++ b/lib/ssl/src/ssl_api.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -24,8 +24,6 @@
%% Visible in API
-export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0,
- erl_cipher_suite/0, %% From ssl_cipher.hrl
- tls_atom_version/0, %% From ssl_internal.hrl
prf_random/0, sslsocket/0]).
@@ -39,23 +37,24 @@
-type listen_option() :: socket_listen_option() | ssl_option() | transport_option().
-type socket_listen_option() :: gen_tcp:listen_option().
--type ssl_option() :: {verify, verify_type()} |
- {verify_fun, {fun(), InitialUserState::term()}} |
- {fail_if_no_peer_cert, boolean()} | {depth, integer()} |
- {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} |
- {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
- {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
- {user_lookup_fun, {fun(), InitialUserState::term()}} |
- {psk_identity, string()} |
- {srp_identity, {string(), string()}} |
- {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
- {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
- {next_protocols_advertised, list(binary())} |
- {client_preferred_next_protocols, binary(), client | server, list(binary())}.
+-type ssl_option() :: {versions, ssl_record:ssl_atom_version()} |
+ {verify, verify_type()} |
+ {verify_fun, {fun(), InitialUserState::term()}} |
+ {fail_if_no_peer_cert, boolean()} | {depth, integer()} |
+ {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} |
+ {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
+ {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
+ {user_lookup_fun, {fun(), InitialUserState::term()}} |
+ {psk_identity, string()} |
+ {srp_identity, {string(), string()}} |
+ {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
+ {next_protocols_advertised, list(binary())} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}.
-type verify_type() :: verify_none | verify_peer.
-type path() :: string().
--type ciphers() :: [erl_cipher_suite()] |
+-type ciphers() :: [ssl_cipher:erl_cipher_suite()] |
string(). % (according to old API)
-type ssl_imp() :: new | old.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index b2077c662a..78a328ace8 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -38,6 +38,21 @@
openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2]).
+-export_type([cipher_suite/0,
+ erl_cipher_suite/0, openssl_cipher_suite/0,
+ key_algo/0]).
+
+-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
+ | aes_128_cbc | aes_256_cbc.
+-type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
+-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
+-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
+-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
+-type cipher_suite() :: binary().
+-type cipher_enum() :: integer().
+-type openssl_cipher_suite() :: string().
+
+
-compile(inline).
%%--------------------------------------------------------------------
@@ -51,7 +66,7 @@ security_parameters(?TLS_NULL_WITH_NULL_NULL = CipherSuite, SecParams) ->
security_parameters(undefined, CipherSuite, SecParams).
%%--------------------------------------------------------------------
--spec security_parameters(tls_version() | undefined, cipher_suite(), #security_parameters{}) ->
+-spec security_parameters(ssl_record:ssl_version() | undefined, cipher_suite(), #security_parameters{}) ->
#security_parameters{}.
%%
%% Description: Returns a security parameters record where the
@@ -72,7 +87,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
--spec cipher(cipher_enum(), #cipher_state{}, binary(), iolist(), tls_version()) ->
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), iodata(), ssl_record:ssl_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
@@ -127,7 +142,7 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
{T, CS0#cipher_state{iv=NextIV}}.
%%--------------------------------------------------------------------
--spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) ->
+-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), ssl_record:ssl_version()) ->
{binary(), binary(), #cipher_state{}} | #alert{}.
%%
%% Description: Decrypts the data and the MAC using cipher described
@@ -200,7 +215,7 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
%%--------------------------------------------------------------------
--spec suites(tls_version()) -> [cipher_suite()].
+-spec suites(ssl_record:ssl_version()) -> [cipher_suite()].
%%
%% Description: Returns a list of supported cipher suites.
%%--------------------------------------------------------------------
@@ -229,7 +244,7 @@ anonymous_suites() ->
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA].
%%--------------------------------------------------------------------
--spec psk_suites(tls_version() | integer()) -> [cipher_suite()].
+-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
%% Description: Returns a list of the PSK cipher suites, only supported
%% if explicitly set by user.
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 62a5269def..3ce9c19aa9 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -26,16 +26,6 @@
-ifndef(ssl_cipher).
-define(ssl_cipher, true).
--type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc.
--type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
--type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
--type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
--type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
--type cipher_suite() :: binary().
--type cipher_enum() :: integer().
--type openssl_cipher_suite() :: string().
-
%%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with
% SSL record protocol
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 82106935cb..ed9e4d344f 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -52,6 +52,7 @@
%% SSL all state functions
-export([handle_sync_event/4, handle_info/3, terminate/3]).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -1757,12 +1758,12 @@ handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport,
Connection:handle_close_alert(Data, StateName, State)
end.
-handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>}}) ->
+handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) ->
%% No trusted certs specified
ok;
handle_trusted_certs_db(#state{cert_db_ref = Ref,
cert_db = CertDb,
- ssl_options = #ssl_options{cacertfile = undefined}}) ->
+ ssl_options = #ssl_options{cacertfile = <<>>}}) ->
%% Certs provided as DER directly can not be shared
%% with other connections and it is safe to delete them when the connection ends.
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index adb2e1debe..b01c6cb1b3 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -48,17 +48,17 @@
socket_options :: #socket_options{},
connection_states :: #connection_states{},
protocol_buffers :: term(), %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
- tls_handshake_history ::tls_handshake_history(),
+ tls_handshake_history :: ssl_handshake:ssl_handshake_history(),
cert_db :: reference(),
session :: #session{},
session_cache :: db_handle(),
session_cache_cb :: atom(),
- negotiated_version :: tls_version(),
+ negotiated_version :: ssl_record:ssl_version(),
client_certificate_requested = false :: boolean(),
- key_algorithm :: key_algo(),
+ key_algorithm :: ssl_cipher:key_algo(),
hashsign_algorithm = {undefined, undefined},
cert_hashsign_algorithm,
- public_key_info ::public_key_info(),
+ public_key_info ::ssl_handshake:public_key_info(),
private_key ::public_key:private_key(),
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
diffie_hellman_keys, % {PublicKey, PrivateKey}
@@ -73,7 +73,7 @@
renegotiation :: undefined | {boolean(), From::term() | internal | peer},
start_or_recv_from :: term(),
timer :: undefined | reference(), % start_or_recive_timer
- send_queue :: queue(),
+ send_queue :: queue:queue(),
terminated = false ::boolean(),
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 7b4cf8eb06..1108edcf48 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,6 +31,18 @@
-include("ssl_srp.hrl").
-include_lib("public_key/include/public_key.hrl").
+-export_type([ssl_handshake/0, ssl_handshake_history/0,
+ public_key_info/0, oid/0]).
+
+-type oid() :: tuple().
+-type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term().
+-type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}.
+-type ssl_handshake_history() :: {[binary()], [binary()]}.
+
+-type ssl_handshake() :: #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} |
+ #client_key_exchange{} | #finished{} | #certificate_verify{} |
+ #hello_request{} | #next_protocol{}.
+
%% Handshake messages
-export([hello_request/0, server_hello/4, server_hello_done/0,
certificate/4, certificate_request/4, key_exchange/3,
@@ -80,7 +92,7 @@ hello_request() ->
#hello_request{}.
%%--------------------------------------------------------------------
--spec server_hello(#session{}, tls_version(), #connection_states{},
+-spec server_hello(#session{}, ssl_record:ssl_version(), #connection_states{},
#hello_extensions{}) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
@@ -164,8 +176,8 @@ next_protocol(SelectedProtocol) ->
%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
- tls_version(), term(), public_key:private_key(),
- tls_handshake_history()) ->
+ ssl_record:ssl_version(), term(), public_key:private_key(),
+ ssl_handshake_history()) ->
#certificate_verify{} | ignore | #alert{}.
%%
%% Description: Creates a certificate_verify message, called by the client.
@@ -188,7 +200,7 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
end.
%%--------------------------------------------------------------------
--spec certificate_request(erl_cipher_suite(), db_handle(), certdb_ref(), tls_version()) ->
+-spec certificate_request(ssl_cipher:erl_cipher_suite(), db_handle(), certdb_ref(), ssl_record:ssl_version()) ->
#certificate_request{}.
%%
%% Description: Creates a certificate_request message, called by the server.
@@ -203,7 +215,7 @@ certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version) ->
certificate_authorities = Authorities
}.
%%--------------------------------------------------------------------
--spec key_exchange(client | server, tls_version(),
+-spec key_exchange(client | server, ssl_record:ssl_version(),
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
{dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
@@ -304,7 +316,7 @@ key_exchange(server, Version, {srp, {PublicKey, _},
ClientRandom, ServerRandom, PrivateKey).
%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
+-spec finished(ssl_record:ssl_version(), client | server, integer(), binary(), ssl_handshake_history()) ->
#finished{}.
%%
%% Description: Creates a handshake finished message
@@ -315,8 +327,7 @@ finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the curr
%% ---------- Handle handshake messages ----------
-verify_server_key(#server_key_params{params = Params,
- params_bin = EncParams,
+verify_server_key(#server_key_params{params_bin = EncParams,
signature = Signature},
HashSign = {HashAlgo, _},
ConnectionStates, Version, PubKeyInfo) ->
@@ -332,8 +343,8 @@ verify_server_key(#server_key_params{params = Params,
verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo).
%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
- binary(), tls_handshake_history()) -> valid | #alert{}.
+-spec certificate_verify(binary(), public_key_info(), ssl_record:ssl_version(), term(),
+ binary(), ssl_handshake_history()) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
@@ -347,7 +358,7 @@ certificate_verify(Signature, PublicKeyInfo, Version,
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
%%--------------------------------------------------------------------
--spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
+-spec verify_signature(ssl_record:ssl_version(), binary(), {term(), term()}, binary(),
public_key_info()) -> true | false.
%%
%% Description: Checks that a public_key signature is valid.
@@ -427,8 +438,8 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
end.
%%--------------------------------------------------------------------
--spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
- tls_handshake_history()) -> verified | #alert{}.
+-spec verify_connection(ssl_record:ssl_version(), #finished{}, client | server, integer(), binary(),
+ ssl_handshake_history()) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
@@ -444,7 +455,7 @@ verify_connection(Version, #finished{verify_data = Data},
end.
%%--------------------------------------------------------------------
--spec init_handshake_history() -> tls_handshake_history().
+-spec init_handshake_history() -> ssl_handshake_history().
%%
%% Description: Initialize the empty handshake history buffer.
@@ -453,8 +464,8 @@ init_handshake_history() ->
{[], []}.
%%--------------------------------------------------------------------
--spec update_handshake_history(tls_handshake_history(), Data ::term()) ->
- tls_handshake_history().
+-spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term()) ->
+ ssl_handshake:ssl_handshake_history().
%%
%% Description: Update the handshake history buffer with Data.
%%--------------------------------------------------------------------
@@ -568,7 +579,7 @@ server_key_exchange_hash(md5sha, Value) ->
server_key_exchange_hash(Hash, Value) ->
crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
--spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
+-spec prf(ssl_record:ssl_version(), binary(), binary(), [binary()], non_neg_integer()) ->
{ok, binary()} | {error, undefined}.
%%
%% Description: use the TLS PRF to generate key material
@@ -612,7 +623,7 @@ select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
HashSign
end.
%%--------------------------------------------------------------------
--spec select_cert_hashsign(#hash_sign_algos{}| undefined, oid(), tls_version() | {undefined, undefined}) ->
+-spec select_cert_hashsign(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version() | {undefined, undefined}) ->
{atom(), atom()}.
%%
@@ -632,7 +643,7 @@ select_cert_hashsign(undefined, ?'id-dsa', _) ->
{sha, dsa}.
%%--------------------------------------------------------------------
--spec master_secret(atom(), tls_version(), #session{} | binary(), #connection_states{},
+-spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
client | server) -> {binary(), #connection_states{}} | #alert{}.
%%
%% Description: Sets or calculates the master secret and calculate keys,
@@ -817,7 +828,7 @@ enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
end.
%%--------------------------------------------------------------------
--spec decode_client_key(binary(), key_algo(), tls_version()) ->
+-spec decode_client_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
#encrypted_premaster_secret{}
| #client_diffie_hellman_public{}
| #client_ec_diffie_hellman_public{}
@@ -832,7 +843,7 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
--spec decode_server_key(binary(), key_algo(), tls_version()) ->
+-spec decode_server_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
#server_key_params{}.
%%
%% Description: Decode server_key data and return appropriate type
@@ -1655,7 +1666,16 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len),
ExtData:Len/binary, Rest/binary>>, Acc) ->
<<?UINT16(_), EllipticCurveList/binary>> = ExtData,
- EllipticCurves = [tls_v1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList],
+ %% Ignore unknown curves
+ Pick = fun(Enum) ->
+ case tls_v1:enum_to_oid(Enum) of
+ undefined ->
+ false;
+ Oid ->
+ {true, Oid}
+ end
+ end,
+ EllipticCurves = lists:filtermap(Pick, [ECC || <<ECC:16>> <= EllipticCurveList]),
dec_hello_extensions(Rest, Acc#hello_extensions{elliptic_curves =
#elliptic_curves{elliptic_curve_list =
EllipticCurves}});
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 75160526b9..80284faef0 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -352,18 +352,4 @@
hostname = undefined
}).
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Dialyzer types
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
--type oid() :: tuple().
--type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term().
--type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}.
--type tls_handshake_history() :: {[binary()], [binary()]}.
-
--type ssl_handshake() :: #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} |
- #client_key_exchange{} | #finished{} | #certificate_verify{} |
- #hello_request{} | #next_protocol{}.
-
-
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 102215119d..cec5d8fbb1 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -30,8 +30,6 @@
-type from() :: term().
-type host() :: inet:ip_address() | inet:hostname().
-type session_id() :: 0 | binary().
--type tls_version() :: {integer(), integer()}.
--type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
-type certdb_ref() :: reference().
-type db_handle() :: term().
-type der_cert() :: binary().
@@ -73,7 +71,7 @@
-record(ssl_options, {
protocol :: tls | dtls,
- versions :: ['tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3] | ['dtlsv1.2' | dtlsv1],
+ versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API
verify :: verify_none | verify_peer,
verify_fun, %%:: fun(CertVerifyErrors::term()) -> boolean(),
fail_if_no_peer_cert :: boolean(),
@@ -82,13 +80,13 @@
validate_extensions_fun,
depth :: integer(),
certfile :: binary(),
- cert :: der_encoded(),
+ cert :: public_key:der_encoded(),
keyfile :: binary(),
- key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', der_encoded()},
+ key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()},
password :: string(),
- cacerts :: [der_encoded()],
+ cacerts :: [public_key:der_encoded()],
cacertfile :: binary(),
- dh :: der_encoded(),
+ dh :: public_key:der_encoded(),
dhfile :: binary(),
user_lookup_fun, % server option, fun to lookup the user
psk_identity :: binary(),
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 4d5eaeb607..fbc73e0e42 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -167,27 +167,27 @@ clean_cert_db(Ref, File) ->
ok.
%%--------------------------------------------------------------------
--spec register_session(inet:port_number(), #session{}) -> ok.
--spec register_session(host(), inet:port_number(), #session{}) -> ok.
%%
%% Description: Make the session available for reuse.
%%--------------------------------------------------------------------
+-spec register_session(host(), inet:port_number(), #session{}) -> ok.
register_session(Host, Port, Session) ->
cast({register_session, Host, Port, Session}).
+-spec register_session(inet:port_number(), #session{}) -> ok.
register_session(Port, Session) ->
cast({register_session, Port, Session}).
%%--------------------------------------------------------------------
--spec invalidate_session(inet:port_number(), #session{}) -> ok.
--spec invalidate_session(host(), inet:port_number(), #session{}) -> ok.
%%
%% Description: Make the session unavailable for reuse. After
%% a the session has been marked "is_resumable = false" for some while
%% it will be safe to remove the data from the session database.
%%--------------------------------------------------------------------
+-spec invalidate_session(host(), inet:port_number(), #session{}) -> ok.
invalidate_session(Host, Port, Session) ->
cast({invalidate_session, Host, Port, Session}).
+-spec invalidate_session(inet:port_number(), #session{}) -> ok.
invalidate_session(Port, Session) ->
cast({invalidate_session, Port, Session}).
diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl
index 9de50c8f26..e59aba0618 100644
--- a/lib/ssl/src/ssl_pkix_db.erl
+++ b/lib/ssl/src/ssl_pkix_db.erl
@@ -115,17 +115,17 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
new_trusted_cert_entry({MD5, File}, Db)
end.
%%--------------------------------------------------------------------
--spec cache_pem_file({binary(), binary()}, [db_handle()]) -> {ok, term()}.
--spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}.
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
+-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> {ok, term()}.
cache_pem_file({MD5, File}, [_CertsDb, _RefDb, PemChache]) ->
{ok, PemBin} = file:read_file(File),
Content = public_key:pem_decode(PemBin),
insert(MD5, Content, PemChache),
{ok, Content}.
+-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}.
cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) ->
{ok, PemBin} = file:read_file(File),
Content = public_key:pem_decode(PemBin),
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 018c8befe0..b0e9943e6d 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -50,6 +50,11 @@
%% Payload encryption/decryption
-export([cipher/4, decipher/3, is_correct_mac/2]).
+-export_type([ssl_version/0, ssl_atom_version/0]).
+
+-type ssl_version() :: {integer(), integer()}.
+-type ssl_atom_version() :: tls_record:tls_atom_version().
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -299,7 +304,7 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
%%--------------------------------------------------------------------
--spec encode_handshake(iolist(), tls_version(), #connection_states{}) ->
+-spec encode_handshake(iolist(), ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes a handshake message to send on the ssl-socket.
@@ -308,7 +313,7 @@ encode_handshake(Frag, Version, ConnectionStates) ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_alert_record(#alert{}, tls_version(), #connection_states{}) ->
+-spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes an alert message to send on the ssl-socket.
@@ -319,7 +324,7 @@ encode_alert_record(#alert{level = Level, description = Description},
ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(tls_version(), #connection_states{}) ->
+-spec encode_change_cipher_spec(ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
@@ -328,7 +333,7 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_data(binary(), tls_version(), #connection_states{}) ->
+-spec encode_data(binary(), ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes data to send on the ssl-socket.
@@ -356,7 +361,7 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
--spec cipher(tls_version(), iolist(), #connection_state{}, MacHash::binary()) ->
+-spec cipher(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
{CipherFragment::binary(), #connection_state{}}.
%%
%% Description: Payload encryption
@@ -372,7 +377,7 @@ cipher(Version, Fragment,
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
{CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
%%--------------------------------------------------------------------
--spec decipher(tls_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}}.
+-spec decipher(ssl_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index c17fa53a62..87ed233c0a 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -20,7 +20,7 @@
%%
%%----------------------------------------------------------------------
%% Purpose: Record and constant defenitions for the SSL-record protocol
-%% see RFC 2246
+% see RFC 2246
%%----------------------------------------------------------------------
-ifndef(ssl_record).
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
index 77b40a7b38..e1aeb11ca4 100644
--- a/lib/ssl/src/ssl_sup.erl
+++ b/lib/ssl/src/ssl_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -89,14 +89,14 @@ tls_connection_manager_child_spec() ->
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-dtls_connection_manager_child_spec() ->
- Name = dtls_connection,
- StartFunc = {dtls_connection_sup, start_link, []},
- Restart = permanent,
- Shutdown = 4000,
- Modules = [dtls_connection, ssl_connection],
- Type = supervisor,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+%% dtls_connection_manager_child_spec() ->
+%% Name = dtls_connection,
+%% StartFunc = {dtls_connection_sup, start_link, []},
+%% Restart = permanent,
+%% Shutdown = 4000,
+%% Modules = [dtls_connection, ssl_connection],
+%% Type = supervisor,
+%% {Name, StartFunc, Restart, Shutdown, Type, Modules}.
session_cb_init_args() ->
case application:get_env(ssl, session_cb_init_args) of
diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl
index d477b3df81..68f7f5dee2 100644
--- a/lib/ssl/src/ssl_v3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -130,7 +130,7 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV}.
--spec suites() -> [cipher_suite()].
+-spec suites() -> [ssl_cipher:cipher_suite()].
suites() ->
[
diff --git a/lib/ssl/src/tls.erl b/lib/ssl/src/tls.erl
index 3e7b2db9c2..c829129250 100644
--- a/lib/ssl/src/tls.erl
+++ b/lib/ssl/src/tls.erl
@@ -30,25 +30,29 @@
handshake/1, handshake/2, handshake/3]).
%%--------------------------------------------------------------------
--spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} |
- {error, reason()}.
--spec connect(host() | port(), [connect_option()] | inet:port_number(),
- timeout() | list()) ->
- {ok, #sslsocket{}} | {error, reason()}.
--spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
-
%%
%% Description: Connect to an TLS server.
%%--------------------------------------------------------------------
+-spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} |
+ {error, reason()}.
+
connect(Socket, Options) when is_port(Socket) ->
connect(Socket, Options, infinity).
+
+-spec connect(host() | port(), [connect_option()] | inet:port_number(),
+ timeout() | list()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
connect(Socket, SslOptions, Timeout) when is_port(Socket) ->
TLSOpts = [{protocol, tls} | SslOptions],
ssl:connect(Socket, TLSOpts, Timeout);
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
+
+-spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
connect(Host, Port, Options, Timeout) ->
TLSOpts = [{protocol, tls} | Options],
ssl:connect(Host, Port, TLSOpts, Timeout).
@@ -64,39 +68,44 @@ listen(Port, Options) ->
ssl:listen(Port, TLSOpts).
%%--------------------------------------------------------------------
--spec accept(#sslsocket{}) -> {ok, #sslsocket{}} |
- {error, reason()}.
--spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
- {error, reason()}.
%%
%% Description: Performs transport accept on an ssl listen socket
%%--------------------------------------------------------------------
+-spec accept(#sslsocket{}) -> {ok, #sslsocket{}} |
+ {error, reason()}.
accept(ListenSocket) ->
accept(ListenSocket, infinity).
+
+-spec accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
+ {error, reason()}.
accept(Socket, Timeout) ->
ssl:transport_accept(Socket, Timeout).
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}) -> ok | {error, reason()}.
--spec handshake(#sslsocket{} | port(), timeout()| [ssl_option()
- | transport_option()]) ->
- ok | {ok, #sslsocket{}} | {error, reason()}.
--spec handshake(port(), [ssl_option()| transport_option()], timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
+-spec handshake(#sslsocket{}) -> ok | {error, reason()}.
+
handshake(ListenSocket) ->
handshake(ListenSocket, infinity).
+-spec handshake(#sslsocket{} | port(), timeout()| [ssl_option()
+ | transport_option()]) ->
+ ok | {ok, #sslsocket{}} | {error, reason()}.
+
handshake(#sslsocket{} = Socket, Timeout) ->
ssl:ssl_accept(Socket, Timeout);
handshake(ListenSocket, SslOptions) when is_port(ListenSocket) ->
handshake(ListenSocket, SslOptions, infinity).
+
+-spec handshake(port(), [ssl_option()| transport_option()], timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
handshake(Socket, SslOptions, Timeout) when is_port(Socket) ->
ssl:ssl_accept(Socket, SslOptions, Timeout).
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 01abefca46..183cabcfcd 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -33,6 +33,8 @@
-export([client_hello/8, hello/4,
get_tls_handshake/3, encode_handshake/2, decode_handshake/3]).
+-type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake().
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -71,11 +73,11 @@ client_hello(Host, Port, ConnectionStates,
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
atom(), #connection_states{}, binary() | undefined},
boolean()) ->
- {tls_version(), session_id(), #connection_states{}, binary() | undefined}|
- {tls_version(), {resumed | new, #session{}}, #connection_states{},
- [binary()] | undefined,
- [oid()] | undefined, [oid()] | undefined} |
- #alert{}.
+ {tls_record:tls_version(), session_id(), #connection_states{}, binary() | undefined}|
+ {tls_record:tls_version(), {resumed | new, #session{}}, #connection_states{},
+ [binary()] | undefined,
+ [ssl_handshake:oid()] | undefined, [ssl_handshake:oid()] | undefined} |
+ #alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
@@ -122,7 +124,7 @@ hello(#client_hello{client_version = ClientVersion,
end.
%%--------------------------------------------------------------------
--spec encode_handshake(tls_handshake(), tls_version()) -> iolist().
+-spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist().
%%
%% Description: Encode a handshake packet
%%--------------------------------------------------------------------x
@@ -132,7 +134,7 @@ encode_handshake(Package, Version) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) ->
+-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist()) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
diff --git a/lib/ssl/src/tls_handshake.hrl b/lib/ssl/src/tls_handshake.hrl
index dbe930cb90..1646e5b6f2 100644
--- a/lib/ssl/src/tls_handshake.hrl
+++ b/lib/ssl/src/tls_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. 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
@@ -37,6 +37,4 @@
extensions
}).
--type tls_handshake() :: #client_hello{} | ssl_handshake().
-
-endif. % -ifdef(tls_handshake).
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 88107557a0..4da08e9c51 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -44,6 +44,11 @@
highest_protocol_version/1, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2]).
+-export_type([tls_version/0, tls_atom_version/0]).
+
+-type tls_version() :: ssl_record:ssl_version().
+-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
+
-compile(inline).
%%====================================================================
@@ -262,18 +267,18 @@ supported_protocol_versions([_|_] = Vsns) ->
Vsns.
%%--------------------------------------------------------------------
--spec is_acceptable_version(tls_version()) -> boolean().
--spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
%%
%%--------------------------------------------------------------------
+-spec is_acceptable_version(tls_version()) -> boolean().
is_acceptable_version({N,_})
when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
true;
is_acceptable_version(_) ->
false.
+-spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean().
is_acceptable_version({N,_} = Version, Versions)
when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
lists:member(Version, Versions);
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 7c7fdd64c3..067417d163 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -166,7 +166,7 @@ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV}.
--spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
+-spec mac_hash(integer(), binary(), integer(), integer(), tls_record:tls_version(),
integer(), binary()) -> binary().
mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
@@ -181,7 +181,7 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Fragment]),
Mac.
--spec suites(1|2|3) -> [cipher_suite()].
+-spec suites(1|2|3) -> [ssl_cipher:cipher_suite()].
suites(Minor) when Minor == 1; Minor == 2->
case sufficent_ec_support() of
@@ -439,7 +439,9 @@ enum_to_oid(24) -> ?secp384r1;
enum_to_oid(25) -> ?secp521r1;
enum_to_oid(26) -> ?brainpoolP256r1;
enum_to_oid(27) -> ?brainpoolP384r1;
-enum_to_oid(28) -> ?brainpoolP512r1.
+enum_to_oid(28) -> ?brainpoolP512r1;
+enum_to_oid(_) ->
+ undefined.
sufficent_ec_support() ->
CryptoSupport = crypto:supports(),
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 244eb5ce0a..2f8ff6f04e 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -39,6 +39,7 @@ MODULES = \
ssl_basic_SUITE \
ssl_cipher_SUITE \
ssl_certificate_verify_SUITE\
+ ssl_crl_SUITE\
ssl_dist_SUITE \
ssl_handshake_SUITE \
ssl_npn_hello_SUITE \
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 4603a9f846..0947657ca7 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -18,23 +18,68 @@
%%
-module(make_certs).
+-compile([export_all]).
--export([all/2]).
+%-export([all/1, all/2, rootCA/2, intermediateCA/3, endusers/3, enduser/3, revoke/3, gencrl/2, verify/3]).
--record(dn, {commonName,
+-record(config, {commonName,
organizationalUnitName = "Erlang OTP",
organizationName = "Ericsson AB",
localityName = "Stockholm",
countryName = "SE",
- emailAddress = "[email protected]"}).
+ emailAddress = "[email protected]",
+ default_bits = 2048,
+ v2_crls = true,
+ ecc_certs = false,
+ issuing_distribution_point = false,
+ openssl_cmd = "openssl"}).
+
+
+default_config() ->
+ #config{}.
+
+make_config(Args) ->
+ make_config(Args, #config{}).
+
+make_config([], C) ->
+ C;
+make_config([{organizationalUnitName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{organizationalUnitName = Name});
+make_config([{organizationName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{organizationName = Name});
+make_config([{localityName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{localityName = Name});
+make_config([{countryName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{countryName = Name});
+make_config([{emailAddress, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{emailAddress = Name});
+make_config([{default_bits, Bits}|T], C) when is_integer(Bits) ->
+ make_config(T, C#config{default_bits = Bits});
+make_config([{v2_crls, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{v2_crls = Bool});
+make_config([{ecc_certs, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{ecc_certs = Bool});
+make_config([{issuing_distribution_point, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{issuing_distribution_point = Bool});
+make_config([{openssl_cmd, Cmd}|T], C) when is_list(Cmd) ->
+ make_config(T, C#config{openssl_cmd = Cmd}).
+
+
+all([DataDir, PrivDir]) ->
+ all(DataDir, PrivDir).
all(DataDir, PrivDir) ->
- OpenSSLCmd = "openssl",
+ all(DataDir, PrivDir, #config{}).
+
+all(DataDir, PrivDir, C) when is_list(C) ->
+ all(DataDir, PrivDir, make_config(C));
+all(DataDir, PrivDir, C = #config{}) ->
+ ok = filelib:ensure_dir(filename:join(PrivDir, "erlangCA")),
create_rnd(DataDir, PrivDir), % For all requests
- rootCA(PrivDir, OpenSSLCmd, "erlangCA"),
- intermediateCA(PrivDir, OpenSSLCmd, "otpCA", "erlangCA"),
- endusers(PrivDir, OpenSSLCmd, "otpCA", ["client", "server"]),
- collect_certs(PrivDir, ["erlangCA", "otpCA"], ["client", "server"]),
+ rootCA(PrivDir, "erlangCA", C),
+ intermediateCA(PrivDir, "otpCA", "erlangCA", C),
+ endusers(PrivDir, "otpCA", ["client", "server", "revoked"], C),
+ endusers(PrivDir, "erlangCA", ["localhost"], C),
%% Create keycert files
SDir = filename:join([PrivDir, "server"]),
SC = filename:join([SDir, "cert.pem"]),
@@ -46,7 +91,14 @@ all(DataDir, PrivDir) ->
CK = filename:join([CDir, "key.pem"]),
CKC = filename:join([CDir, "keycert.pem"]),
append_files([CK, CC], CKC),
- remove_rnd(PrivDir).
+ RDir = filename:join([PrivDir, "revoked"]),
+ RC = filename:join([RDir, "cert.pem"]),
+ RK = filename:join([RDir, "key.pem"]),
+ RKC = filename:join([RDir, "keycert.pem"]),
+ revoke(PrivDir, "otpCA", "revoked", C),
+ append_files([RK, RC], RKC),
+ remove_rnd(PrivDir),
+ {ok, C}.
append_files(FileNames, ResultFileName) ->
{ok, ResultFile} = file:open(ResultFileName, [write]),
@@ -59,111 +111,176 @@ do_append_files([F|Fs], RF) ->
ok = file:write(RF, Data),
do_append_files(Fs, RF).
-rootCA(Root, OpenSSLCmd, Name) ->
- create_ca_dir(Root, Name, ca_cnf(Name)),
- DN = #dn{commonName = Name},
- create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)),
- ok.
+rootCA(Root, Name, C) ->
+ create_ca_dir(Root, Name, ca_cnf(C#config{commonName = Name})),
+ create_self_signed_cert(Root, Name, req_cnf(C#config{commonName = Name}), C),
+ file:copy(filename:join([Root, Name, "cert.pem"]), filename:join([Root, Name, "cacerts.pem"])),
+ gencrl(Root, Name, C).
-intermediateCA(Root, OpenSSLCmd, CA, ParentCA) ->
- CA = "otpCA",
- create_ca_dir(Root, CA, ca_cnf(CA)),
+intermediateCA(Root, CA, ParentCA, C) ->
+ create_ca_dir(Root, CA, ca_cnf(C#config{commonName = CA})),
CARoot = filename:join([Root, CA]),
- DN = #dn{commonName = CA},
CnfFile = filename:join([CARoot, "req.cnf"]),
- file:write_file(CnfFile, req_cnf(DN)),
+ file:write_file(CnfFile, req_cnf(C#config{commonName = CA})),
KeyFile = filename:join([CARoot, "private", "key.pem"]),
ReqFile = filename:join([CARoot, "req.pem"]),
- create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
+ create_req(Root, CnfFile, KeyFile, ReqFile, C),
CertFile = filename:join([CARoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile).
-
-endusers(Root, OpenSSLCmd, CA, Users) ->
- lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users).
-
-enduser(Root, OpenSSLCmd, CA, User) ->
+ sign_req(Root, ParentCA, "ca_cert", ReqFile, CertFile, C),
+ CACertsFile = filename:join(CARoot, "cacerts.pem"),
+ file:copy(filename:join([Root, ParentCA, "cacerts.pem"]), CACertsFile),
+ %% append this CA's cert to the cacerts file
+ {ok, Bin} = file:read_file(CertFile),
+ {ok, FD} = file:open(CACertsFile, [append]),
+ file:write(FD, ["\n", Bin]),
+ file:close(FD),
+ gencrl(Root, CA, C).
+
+endusers(Root, CA, Users, C) ->
+ [enduser(Root, CA, User, C) || User <- Users].
+
+enduser(Root, CA, User, C) ->
UsrRoot = filename:join([Root, User]),
file:make_dir(UsrRoot),
CnfFile = filename:join([UsrRoot, "req.cnf"]),
- DN = #dn{commonName = User},
- file:write_file(CnfFile, req_cnf(DN)),
+ file:write_file(CnfFile, req_cnf(C#config{commonName = User})),
KeyFile = filename:join([UsrRoot, "key.pem"]),
ReqFile = filename:join([UsrRoot, "req.pem"]),
- create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
+ create_req(Root, CnfFile, KeyFile, ReqFile, C),
+ %create_req(Root, CnfFile, KeyFile, ReqFile),
CertFileAllUsage = filename:join([UsrRoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFileAllUsage),
+ sign_req(Root, CA, "user_cert", ReqFile, CertFileAllUsage, C),
CertFileDigitalSigOnly = filename:join([UsrRoot, "digital_signature_only_cert.pem"]),
- sign_req(Root, OpenSSLCmd, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly).
-
-collect_certs(Root, CAs, Users) ->
- Bins = lists:foldr(
- fun(CA, Acc) ->
- File = filename:join([Root, CA, "cert.pem"]),
- {ok, Bin} = file:read_file(File),
- [Bin, "\n" | Acc]
- end, [], CAs),
- lists:foreach(
- fun(User) ->
- File = filename:join([Root, User, "cacerts.pem"]),
- file:write_file(File, Bins)
- end, Users).
+ sign_req(Root, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly, C),
+ CACertsFile = filename:join(UsrRoot, "cacerts.pem"),
+ file:copy(filename:join([Root, CA, "cacerts.pem"]), CACertsFile),
+ ok.
+
+revoke(Root, CA, User, C) ->
+ UsrCert = filename:join([Root, User, "cert.pem"]),
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -revoke ", UsrCert,
+ [" -crl_reason keyCompromise" || C#config.v2_crls ],
+ " -config ", CACnfFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+ gencrl(Root, CA, C).
+
+gencrl(Root, CA, C) ->
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ CACRLFile = filename:join([Root, CA, "crl.pem"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -gencrl ",
+ " -crlhours 24",
+ " -out ", CACRLFile,
+ " -config ", CACnfFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
-create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
+verify(Root, CA, User, C) ->
+ CAFile = filename:join([Root, User, "cacerts.pem"]),
+ CACRLFile = filename:join([Root, CA, "crl.pem"]),
+ CertFile = filename:join([Root, User, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " verify"
+ " -CAfile ", CAFile,
+ " -CRLfile ", CACRLFile, %% this is undocumented, but seems to work
+ " -crl_check ",
+ CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ try cmd(Cmd, Env) catch
+ exit:{eval_cmd, _, _} ->
+ invalid
+ end.
+
+create_self_signed_cert(Root, CAName, Cnf, C = #config{ecc_certs = true}) ->
CARoot = filename:join([Root, CAName]),
CnfFile = filename:join([CARoot, "req.cnf"]),
file:write_file(CnfFile, Cnf),
KeyFile = filename:join([CARoot, "private", "key.pem"]),
CertFile = filename:join([CARoot, "cert.pem"]),
- Cmd = [OpenSSLCmd, " req"
+ Cmd = [C#config.openssl_cmd, " ecparam"
+ " -out ", KeyFile,
+ " -name secp521r1 ",
+ %" -name sect283k1 ",
+ " -genkey "],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+
+ Cmd2 = [C#config.openssl_cmd, " req"
" -new"
" -x509"
" -config ", CnfFile,
- " -keyout ", KeyFile,
+ " -key ", KeyFile,
+ " -outform PEM ",
" -out ", CertFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env),
- fix_key_file(OpenSSLCmd, KeyFile).
-
-% openssl 1.0 generates key files in pkcs8 format by default and we don't handle this format
-fix_key_file(OpenSSLCmd, KeyFile) ->
- KeyFileTmp = KeyFile ++ ".tmp",
- Cmd = [OpenSSLCmd, " rsa",
- " -in ",
- KeyFile,
- " -out ",
- KeyFileTmp],
- cmd(Cmd, []),
- ok = file:rename(KeyFileTmp, KeyFile).
+ cmd(Cmd2, Env);
+create_self_signed_cert(Root, CAName, Cnf, C) ->
+ CARoot = filename:join([Root, CAName]),
+ CnfFile = filename:join([CARoot, "req.cnf"]),
+ file:write_file(CnfFile, Cnf),
+ KeyFile = filename:join([CARoot, "private", "key.pem"]),
+ CertFile = filename:join([CARoot, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " req"
+ " -new"
+ " -x509"
+ " -config ", CnfFile,
+ " -keyout ", KeyFile,
+ " -outform PEM",
+ " -out ", CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+
create_ca_dir(Root, CAName, Cnf) ->
CARoot = filename:join([Root, CAName]),
+ ok = filelib:ensure_dir(CARoot),
file:make_dir(CARoot),
create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
create_rnd(Root, filename:join([CAName, "private"])),
create_files(CARoot, [{"serial", "01\n"},
+ {"crlnumber", "01"},
{"index.txt", ""},
{"ca.cnf", Cnf}]).
-create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
- Cmd = [OpenSSLCmd, " req"
+create_req(Root, CnfFile, KeyFile, ReqFile, C = #config{ecc_certs = true}) ->
+ Cmd = [C#config.openssl_cmd, " ecparam"
+ " -out ", KeyFile,
+ " -name secp521r1 ",
+ %" -name sect283k1 ",
+ " -genkey "],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+ Cmd2 = [C#config.openssl_cmd, " req"
+ " -new ",
+ " -key ", KeyFile,
+ " -outform PEM ",
+ " -out ", ReqFile,
+ " -config ", CnfFile],
+ cmd(Cmd2, Env);
+ %fix_key_file(KeyFile).
+create_req(Root, CnfFile, KeyFile, ReqFile, C) ->
+ Cmd = [C#config.openssl_cmd, " req"
" -new"
" -config ", CnfFile,
+ " -outform PEM ",
" -keyout ", KeyFile,
" -out ", ReqFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env),
- fix_key_file(OpenSSLCmd, KeyFile).
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+ %fix_key_file(KeyFile).
+
-sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
+sign_req(Root, CA, CertType, ReqFile, CertFile, C) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
- Cmd = [OpenSSLCmd, " ca"
+ Cmd = [C#config.openssl_cmd, " ca"
" -batch"
" -notext"
" -config ", CACnfFile,
" -extensions ", CertType,
" -in ", ReqFile,
" -out ", CertFile],
- Env = [{"ROOTDIR", Root}],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
cmd(Cmd, Env).
%%
@@ -194,19 +311,19 @@ cmd(Cmd, Env) ->
FCmd = lists:flatten(Cmd),
Port = open_port({spawn, FCmd}, [stream, eof, exit_status, stderr_to_stdout,
{env, Env}]),
- eval_cmd(Port).
+ eval_cmd(Port, FCmd).
-eval_cmd(Port) ->
+eval_cmd(Port, Cmd) ->
receive
{Port, {data, _}} ->
- eval_cmd(Port);
+ eval_cmd(Port, Cmd);
{Port, eof} ->
ok
end,
receive
{Port, {exit_status, Status}} when Status /= 0 ->
%% io:fwrite("exit status: ~w~n", [Status]),
- exit({eval_cmd, Status})
+ exit({eval_cmd, Cmd, Status})
after 0 ->
ok
end.
@@ -215,7 +332,7 @@ eval_cmd(Port) ->
%% Contents of configuration files
%%
-req_cnf(DN) ->
+req_cnf(C) ->
["# Purpose: Configuration for requests (end users and CAs)."
"\n"
"ROOTDIR = $ENV::ROOTDIR\n"
@@ -224,10 +341,10 @@ req_cnf(DN) ->
"[req]\n"
"input_password = secret\n"
"output_password = secret\n"
- "default_bits = 1024\n"
+ "default_bits = ", integer_to_list(C#config.default_bits), "\n"
"RANDFILE = $ROOTDIR/RAND\n"
"encrypt_key = no\n"
- "default_md = sha1\n"
+ "default_md = md5\n"
"#string_mask = pkix\n"
"x509_extensions = ca_ext\n"
"prompt = no\n"
@@ -235,12 +352,12 @@ req_cnf(DN) ->
"\n"
"[name]\n"
- "commonName = ", DN#dn.commonName, "\n"
- "organizationalUnitName = ", DN#dn.organizationalUnitName, "\n"
- "organizationName = ", DN#dn.organizationName, "\n"
- "localityName = ", DN#dn.localityName, "\n"
- "countryName = ", DN#dn.countryName, "\n"
- "emailAddress = ", DN#dn.emailAddress, "\n"
+ "commonName = ", C#config.commonName, "\n"
+ "organizationalUnitName = ", C#config.organizationalUnitName, "\n"
+ "organizationName = ", C#config.organizationName, "\n"
+ "localityName = ", C#config.localityName, "\n"
+ "countryName = ", C#config.countryName, "\n"
+ "emailAddress = ", C#config.emailAddress, "\n"
"\n"
"[ca_ext]\n"
@@ -249,8 +366,7 @@ req_cnf(DN) ->
"subjectKeyIdentifier = hash\n"
"subjectAltName = email:copy\n"].
-
-ca_cnf(CA) ->
+ca_cnf(C) ->
["# Purpose: Configuration for CAs.\n"
"\n"
"ROOTDIR = $ENV::ROOTDIR\n"
@@ -258,21 +374,23 @@ ca_cnf(CA) ->
"\n"
"[ca]\n"
- "dir = $ROOTDIR/", CA, "\n"
+ "dir = $ROOTDIR/", C#config.commonName, "\n"
"certs = $dir/certs\n"
"crl_dir = $dir/crl\n"
"database = $dir/index.txt\n"
"new_certs_dir = $dir/newcerts\n"
"certificate = $dir/cert.pem\n"
"serial = $dir/serial\n"
- "crl = $dir/crl.pem\n"
+ "crl = $dir/crl.pem\n",
+ ["crlnumber = $dir/crlnumber\n" || C#config.v2_crls],
"private_key = $dir/private/key.pem\n"
"RANDFILE = $dir/private/RAND\n"
"\n"
- "x509_extensions = user_cert\n"
+ "x509_extensions = user_cert\n",
+ ["crl_extensions = crl_ext\n" || C#config.v2_crls],
"unique_subject = no\n"
"default_days = 3600\n"
- "default_md = sha1\n"
+ "default_md = md5\n"
"preserve = no\n"
"policy = policy_match\n"
"\n"
@@ -286,6 +404,13 @@ ca_cnf(CA) ->
"emailAddress = supplied\n"
"\n"
+ "[crl_ext]\n"
+ "authorityKeyIdentifier=keyid:always,issuer:always\n",
+ ["issuingDistributionPoint=critical, @idpsec\n" || C#config.issuing_distribution_point],
+
+ "[idpsec]\n"
+ "fullname=URI:http://localhost:8000/",C#config.commonName,"/crl.pem\n"
+
"[user_cert]\n"
"basicConstraints = CA:false\n"
"keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
@@ -293,6 +418,12 @@ ca_cnf(CA) ->
"authorityKeyIdentifier = keyid,issuer:always\n"
"subjectAltName = email:copy\n"
"issuerAltName = issuer:copy\n"
+ "crlDistributionPoints=@crl_section\n"
+
+ "[crl_section]\n"
+ %% intentionally invalid
+ "URI.1=http://localhost/",C#config.commonName,"/crl.pem\n"
+ "URI.2=http://localhost:8000/",C#config.commonName,"/crl.pem\n"
"\n"
"[user_cert_digital_signature_only]\n"
@@ -310,4 +441,7 @@ ca_cnf(CA) ->
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid:always,issuer:always\n"
"subjectAltName = email:copy\n"
- "issuerAltName = issuer:copy\n"].
+ "issuerAltName = issuer:copy\n"
+ "crlDistributionPoints=@crl_section\n"
+ ].
+
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index ddc511c652..0148e1f5bc 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -84,8 +84,10 @@ all_versions_groups ()->
basic_tests() ->
[app,
+ appup,
alerts,
send_close,
+ version_option,
connect_twice,
connect_dist,
clear_pem_cache
@@ -94,6 +96,7 @@ basic_tests() ->
options_tests() ->
[der_input,
misc_ssl_options,
+ ssl_options_not_proplist,
socket_options,
invalid_inet_get_option,
invalid_inet_get_option_not_list,
@@ -291,6 +294,11 @@ app() ->
app(Config) when is_list(Config) ->
ok = ?t:app_test(ssl).
%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the ssl appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(ssl).
+%%--------------------------------------------------------------------
alerts() ->
[{doc, "Test ssl_alert:alert_txt/1"}].
alerts(Config) when is_list(Config) ->
@@ -301,7 +309,11 @@ alerts(Config) when is_list(Config) ->
?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR,
?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION,
?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED,
- ?NO_RENEGOTIATION],
+ ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE,
+ ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE,
+ ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY,
+ 255 %% Unsupported/unknow alert will result in a description too
+ ],
Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) |
[?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]],
lists:foreach(fun(Alert) ->
@@ -983,7 +995,7 @@ misc_ssl_options(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- %% Chek that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
+ %% Check that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
TestOpts = [{depth, 1},
{key, undefined},
{password, []},
@@ -1011,6 +1023,17 @@ misc_ssl_options(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+ssl_options_not_proplist() ->
+ [{doc,"Test what happens if an option is not a key value tuple"}].
+
+ssl_options_not_proplist(Config) when is_list(Config) ->
+ BadOption = {client_preferred_next_protocols,
+ client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>},
+ {option_not_a_key_value_tuple, BadOption} =
+ ssl:connect("twitter.com", 443, [binary, {active, false},
+ BadOption]).
+
+%%--------------------------------------------------------------------
versions() ->
[{doc,"Test API function versions/0"}].
@@ -1072,6 +1095,13 @@ send_close(Config) when is_list(Config) ->
{error, _} = ssl:send(SslS, "Hello world").
%%--------------------------------------------------------------------
+version_option() ->
+ [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}].
+version_option(Config) when is_list(Config) ->
+ Versions = proplists:get_value(supported, ssl:versions()),
+ [version_option_test(Config, Version) || Version <- Versions].
+
+%%--------------------------------------------------------------------
close_transport_accept() ->
[{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}].
@@ -2200,7 +2230,14 @@ der_input(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ ssl_test_lib:close(Client),
+
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ [CADb | _] = element(5, State),
+ [] = ets:tab2list(CADb).
+
%%--------------------------------------------------------------------
der_input_opts(Opts) ->
Certfile = proplists:get_value(certfile, Opts),
@@ -3488,3 +3525,28 @@ shutdown_both_result(Socket, client) ->
peername_result(S) ->
ssl:peername(S).
+
+version_option_test(Config, Version) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false}, {versions, [Version]}| ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false}, {versions, [Version]}| ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
new file mode 100644
index 0000000000..4eacf3adfc
--- /dev/null
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -0,0 +1,536 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2013. 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(ssl_crl_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-define(TIMEOUT, 120000).
+-define(LONG_TIMEOUT, 600000).
+-define(SLEEP, 1000).
+-define(OPENSSL_RENEGOTIATE, "R\n").
+-define(OPENSSL_QUIT, "Q\n").
+-define(OPENSSL_GARBAGE, "P\n").
+-define(EXPIRE, 10).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, basic},
+ {group, v1_crl},
+ {group, idp_crl}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {v1_crl, [], v1_crl_tests()},
+ {idp_crl, [], idp_crl_tests()}].
+
+basic_tests() ->
+ [crl_verify_valid, crl_verify_revoked].
+
+v1_crl_tests() ->
+ [crl_verify_valid, crl_verify_revoked].
+
+idp_crl_tests() ->
+ [crl_verify_valid, crl_verify_revoked].
+
+%%%================================================================
+%%% Suite init/end
+
+init_per_suite(Config0) ->
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ TLSVersion = ?config(tls_version, Config0),
+ OpenSSL_version = (catch os:cmd("openssl version")),
+ ct:log("TLS version: ~p~nOpenSSL version: ~p~n~n~p:module_info(): ~p~n~nssh:module_info(): ~p~n",
+ [TLSVersion, OpenSSL_version, ?MODULE, ?MODULE:module_info(), ssh:module_info()]),
+ case ssl_test_lib:enough_openssl_crl_support(OpenSSL_version) of
+ false ->
+ {skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])};
+ _ ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl:start(),
+ [{watchdog, Dog}, {openssl_version,OpenSSL_version} | Config0]
+ catch _C:_E ->
+ ct:log("crypto:start() caught ~p:~p",[_C,_E]),
+ {skip, "Crypto did not start"}
+ end
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+%%%================================================================
+%%% Group init/end
+
+init_per_group(Group, Config) ->
+ ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
+ ssl:start(),
+ inets:start(),
+ CertDir = filename:join(?config(priv_dir, Config), Group),
+ DataDir = ?config(data_dir, Config),
+ ServerRoot = make_dir_path([?config(priv_dir,Config), Group, tmp]),
+ Result = make_certs:all(DataDir, CertDir, cert_opts(Group)),
+ ct:log("~p:~p~nmake_certs:all(~n DataDir=~p,~n CertDir=~p,~n ServerRoot=~p~n Opts=~p~n) returned ~p~n", [?MODULE,?LINE,DataDir, CertDir, ServerRoot, cert_opts(Group), Result]),
+ %% start a HTTP server to serve the CRLs
+ {ok, Httpd} = inets:start(httpd, [{server_name, "localhost"}, {port, 8000},
+ {server_root, ServerRoot},
+ {document_root, CertDir},
+ {modules, [mod_get]}
+ ]),
+ ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
+ [{make_cert_result, Result}, {cert_dir, CertDir}, {httpd, Httpd} | Config].
+
+cert_opts(v1_crl) -> [{v2_crls, false}];
+cert_opts(idp_crl) -> [{issuing_distribution_point, true}];
+cert_opts(_) -> [].
+
+make_dir_path(PathComponents) ->
+ lists:foldl(fun(F,P0) -> file:make_dir(P=filename:join(P0,F)), P end,
+ "",
+ PathComponents).
+
+
+end_per_group(_GroupName, Config) ->
+ case ?config(httpd, Config) of
+ undefined -> ok;
+ Pid ->
+ ct:log("Stop httpd ~p",[Pid]),
+ ok = inets:stop(httpd, Pid)
+ ,ct:log("Stopped",[])
+ end,
+ inets:stop(),
+ ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
+ Config.
+
+%%%================================================================
+%%% Test cases
+
+crl_verify_valid() ->
+ [{doc,"Verify a simple valid CRL chain"}].
+crl_verify_valid(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ PrivDir = ?config(cert_dir, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "server", "key.pem"])},
+ {certfile, filename:join([PrivDir, "server", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])}],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ %{mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ ct:log("~p:~p~nreturn from ssl_test_lib:start_server:~n~p",[?MODULE,?LINE,Server]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CACerts = load_cert(filename:join([PrivDir, "erlangCA", "cacerts.pem"])),
+
+ ClientOpts = [{cacerts, CACerts},
+ {verify, verify_peer},
+ {verify_fun, {fun validate_function/3, {CACerts, []}}}],
+
+
+ ct:log("~p:~p~ncalling ssl_test_lib:start_client",[?MODULE,?LINE]),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_send, [Data]}},
+ %{mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+ ct:log("~p:~p~nreturn from ssl_test_lib:start_client:~n~p",[?MODULE,?LINE,Client]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+crl_verify_revoked() ->
+ [{doc,"Verify a simple valid CRL chain"}].
+crl_verify_revoked(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ PrivDir = ?config(cert_dir, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "revoked", "key.pem"])},
+ {certfile, filename:join([PrivDir, "revoked", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])}],
+ ct:log("~p:~p~nserver opts ~p~n", [?MODULE,?LINE, ServerOpts]),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ %{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CACerts = load_cert(filename:join([PrivDir, "erlangCA", "cacerts.pem"])),
+ ClientOpts = [{cacerts, CACerts},
+ {verify, verify_peer},
+ {verify_fun, {fun validate_function/3, {CACerts, []}}}],
+
+ {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ %{mfa, {?MODULE,
+ %erlang_ssl_receive, [Data]}},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ process_flag(trap_exit, false).
+
+%%%================================================================
+%%% Lib
+
+erlang_ssl_receive(Socket, Data) ->
+ ct:log("~p:~p~nConnection info: ~p~n",
+ [?MODULE,?LINE, ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, Data} ->
+ ct:log("~p:~p~nReceived ~p~n",[?MODULE,?LINE, Data]),
+ %% open_ssl server sometimes hangs waiting in blocking read
+ ssl:send(Socket, "Got it"),
+ ok;
+ {ssl, Socket, Byte} when length(Byte) == 1 ->
+ erlang_ssl_receive(Socket, tl(Data));
+ {Port, {data,Debug}} when is_port(Port) ->
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
+ erlang_ssl_receive(Socket,Data);
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 4000 ->
+ ct:fail({did_not_get, Data})
+ end.
+
+
+erlang_ssl_send(Socket, Data) ->
+ ct:log("~p:~p~nConnection info: ~p~n",
+ [?MODULE,?LINE, ssl:connection_info(Socket)]),
+ ssl:send(Socket, Data),
+ ok.
+
+load_certs(undefined) ->
+ undefined;
+load_certs(CertDir) ->
+ case file:list_dir(CertDir) of
+ {ok, Certs} ->
+ load_certs(lists:map(fun(Cert) -> filename:join(CertDir, Cert)
+ end, Certs), []);
+ {error, _} ->
+ undefined
+ end.
+
+load_certs([], Acc) ->
+ ct:log("~p:~p~nSuccessfully loaded ~p CA certificates~n", [?MODULE,?LINE, length(Acc)]),
+ Acc;
+load_certs([Cert|Certs], Acc) ->
+ case filelib:is_dir(Cert) of
+ true ->
+ load_certs(Certs, Acc);
+ _ ->
+ %ct:log("~p:~p~nLoading certificate ~p~n", [?MODULE,?LINE, Cert]),
+ load_certs(Certs, load_cert(Cert) ++ Acc)
+ end.
+
+load_cert(Cert) ->
+ {ok, Bin} = file:read_file(Cert),
+ case filename:extension(Cert) of
+ ".der" ->
+ %% no decoding necessary
+ [Bin];
+ _ ->
+ %% assume PEM otherwise
+ Contents = public_key:pem_decode(Bin),
+ [DER || {Type, DER, Cipher} <- Contents, Type == 'Certificate', Cipher == 'not_encrypted']
+ end.
+
+%% @doc Validator function for SSL negotiation.
+%%
+validate_function(Cert, valid_peer, State) ->
+ ct:log("~p:~p~nvaliding peer ~p with ~p intermediate certs~n",
+ [?MODULE,?LINE, get_common_name(Cert),
+ length(element(2, State))]),
+ %% peer certificate validated, now check the CRL
+ Res = (catch check_crl(Cert, State)),
+ ct:log("~p:~p~nCRL validate result for ~p: ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert), Res]),
+ {Res, State};
+validate_function(Cert, valid, {TrustedCAs, IntermediateCerts}=State) ->
+ case public_key:pkix_is_self_signed(Cert) of
+ true ->
+ ct:log("~p:~p~nroot certificate~n",[?MODULE,?LINE]),
+ %% this is a root cert, no CRL
+ {valid, {TrustedCAs, [Cert|IntermediateCerts]}};
+ false ->
+ %% check is valid CA certificate, add to the list of
+ %% intermediates
+ Res = (catch check_crl(Cert, State)),
+ ct:log("~p:~p~nCRL intermediate CA validate result for ~p: ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert), Res]),
+ {Res, {TrustedCAs, [Cert|IntermediateCerts]}}
+ end;
+validate_function(_Cert, _Event, State) ->
+ %ct:log("~p:~p~nignoring event ~p~n", [?MODULE,?LINE, _Event]),
+ {valid, State}.
+
+%% @doc Given a certificate, find CRL distribution points for the given
+%% certificate, fetch, and attempt to validate each CRL through
+%% issuer_function/4.
+%%
+check_crl(Cert, State) ->
+ %% pull the CRL distribution point(s) out of the certificate, if any
+ ct:log("~p:~p~ncheck_crl(~n Cert=~p,~nState=~p~n)",[?MODULE,?LINE,Cert,State]),
+ case pubkey_cert:select_extension(
+ ?'id-ce-cRLDistributionPoints',
+ pubkey_cert:extensions_list(Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.extensions)) of
+ undefined ->
+ ct:log("~p:~p~nno CRL distribution points for ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert)]),
+ %% fail; we can't validate if there's no CRL
+ no_crl;
+ CRLExtension ->
+ ct:log("~p:~p~nCRLExtension=~p)",[?MODULE,?LINE,CRLExtension]),
+ CRLDistPoints = CRLExtension#'Extension'.extnValue,
+ DPointsAndCRLs = lists:foldl(fun(Point, Acc) ->
+ %% try to read the CRL over http or from a
+ %% local file
+ case fetch_point(Point) of
+ not_available ->
+ ct:log("~p:~p~nfetch_point returned~n~p~n)",[?MODULE,?LINE,not_available]),
+ Acc;
+ Res ->
+ ct:log("~p:~p~nfetch_point returned~n~p~n)",[?MODULE,?LINE,Res]),
+ [{Point, Res} | Acc]
+ end
+ end, [], CRLDistPoints),
+ public_key:pkix_crls_validate(Cert,
+ DPointsAndCRLs,
+ [{issuer_fun,
+ {fun issuer_function/4, State}}])
+ end.
+
+%% @doc Given a list of distribution points for CRLs, certificates and
+%% both trusted and intermediary certificates, attempt to build and
+%% authority chain back via build_chain to verify that it is valid.
+%%
+issuer_function(_DP, CRL, _Issuer, {TrustedCAs, IntermediateCerts}) ->
+ %% XXX the 'Issuer' we get passed here is the AuthorityKeyIdentifier,
+ %% which we are not currently smart enough to understand
+ %% Read the CA certs out of the file
+ ct:log("~p:~p~nissuer_function(~nCRL=~p,~nLast param=~p)",[?MODULE,?LINE,CRL, {TrustedCAs, IntermediateCerts}]),
+ Certs = [public_key:pkix_decode_cert(DER, otp) || DER <- TrustedCAs],
+ %% get the real issuer out of the CRL
+ Issuer = public_key:pkix_normalize_name(
+ pubkey_cert_records:transform(
+ CRL#'CertificateList'.tbsCertList#'TBSCertList'.issuer, decode)),
+ %% assume certificates are ordered from root to tip
+ case find_issuer(Issuer, IntermediateCerts ++ Certs) of
+ undefined ->
+ ct:log("~p:~p~nunable to find certificate matching CRL issuer ~p~n",
+ [?MODULE,?LINE, Issuer]),
+ error;
+ IssuerCert ->
+ ct:log("~p:~p~nIssuerCert=~p~n)",[?MODULE,?LINE,IssuerCert]),
+ case build_chain({public_key:pkix_encode('OTPCertificate',
+ IssuerCert,
+ otp),
+ IssuerCert}, IntermediateCerts, Certs, []) of
+ undefined ->
+ error;
+ {OTPCert, Path} ->
+ {ok, OTPCert, Path}
+ end
+ end.
+
+%% @doc Attempt to build authority chain back using intermediary
+%% certificates, falling back on trusted certificates if the
+%% intermediary chain of certificates does not fully extend to the
+%% root.
+%%
+%% Returns: {RootCA :: #OTPCertificate{}, Chain :: [der_encoded()]}
+%%
+build_chain({DER, Cert}, IntCerts, TrustedCerts, Acc) ->
+ %% check if this cert is self-signed, if it is, we've reached the
+ %% root of the chain
+ Issuer = public_key:pkix_normalize_name(
+ Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer),
+ Subject = public_key:pkix_normalize_name(
+ Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
+ case Issuer == Subject of
+ true ->
+ case find_issuer(Issuer, TrustedCerts) of
+ undefined ->
+ ct:log("~p:~p~nself-signed certificate is NOT trusted~n",[?MODULE,?LINE]),
+ undefined;
+ TrustedCert ->
+ %% return the cert from the trusted list, to prevent
+ %% issuer spoofing
+ {TrustedCert,
+ [public_key:pkix_encode(
+ 'OTPCertificate', TrustedCert, otp)|Acc]}
+ end;
+ false ->
+ Match = lists:foldl(
+ fun(C, undefined) ->
+ S = public_key:pkix_normalize_name(C#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
+ %% compare the subject to the current issuer
+ case Issuer == S of
+ true ->
+ %% we've found our man
+ {public_key:pkix_encode('OTPCertificate', C, otp), C};
+ false ->
+ undefined
+ end;
+ (_E, A) ->
+ %% already matched
+ A
+ end, undefined, IntCerts),
+ case Match of
+ undefined when IntCerts /= TrustedCerts ->
+ %% continue the chain by using the trusted CAs
+ ct:log("~p:~p~nRan out of intermediate certs, switching to trusted certs~n",[?MODULE,?LINE]),
+ build_chain({DER, Cert}, TrustedCerts, TrustedCerts, Acc);
+ undefined ->
+ ct:log("Can't construct chain of trust beyond ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert)]),
+ %% can't find the current cert's issuer
+ undefined;
+ Match ->
+ build_chain(Match, IntCerts, TrustedCerts, [DER|Acc])
+ end
+ end.
+
+%% @doc Given a certificate and a list of trusted or intermediary
+%% certificates, attempt to find a match in the list or bail with
+%% undefined.
+find_issuer(Issuer, Certs) ->
+ lists:foldl(
+ fun(OTPCert, undefined) ->
+ %% check if this certificate matches the issuer
+ Normal = public_key:pkix_normalize_name(
+ OTPCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
+ case Normal == Issuer of
+ true ->
+ OTPCert;
+ false ->
+ undefined
+ end;
+ (_E, Acc) ->
+ %% already found a match
+ Acc
+ end, undefined, Certs).
+
+%% @doc Find distribution points for a given CRL and then attempt to
+%% fetch the CRL from the first available.
+fetch_point(#'DistributionPoint'{distributionPoint={fullName, Names}}) ->
+ Decoded = [{NameType,
+ pubkey_cert_records:transform(Name, decode)}
+ || {NameType, Name} <- Names],
+ ct:log("~p:~p~ncall fetch(~nDecoded=~p~n)",[?MODULE,?LINE,Decoded]),
+ fetch(Decoded).
+
+%% @doc Given a list of locations to retrieve a CRL from, attempt to
+%% retrieve either from a file or http resource and bail as soon as
+%% it can be found.
+%%
+%% Currently, only hand a armored PEM or DER encoded file, with
+%% defaulting to DER.
+%%
+fetch([]) ->
+ not_available;
+fetch([{uniformResourceIdentifier, "http"++_=URL}|Rest]) ->
+ ct:log("~p:~p~ngetting CRL from ~p~n", [?MODULE,?LINE, URL]),
+ ct:log("~p:~p~nlisteners to port 8000:~n~p~n)",[?MODULE,?LINE,os:cmd("netstat -tln|grep ':8000'")]),
+ case httpc:request(get, {URL, []}, [], [{body_format, binary}]) of
+ {ok, {_Status, _Headers, Body}} ->
+ case Body of
+ <<"-----BEGIN", _/binary>> ->
+ ct:log("~p:~p~npublic_key:pem_decode,~nBody=~p~n)",[?MODULE,?LINE,Body]),
+ [{'CertificateList',
+ DER, _}=CertList] = public_key:pem_decode(Body),
+ ct:log("~p:~p~npublic_key:pem_entry_decode,~nCertList=~p~n)",[?MODULE,?LINE,CertList]),
+ {DER, public_key:pem_entry_decode(CertList)};
+ _ ->
+ ct:log("~p:~p~npublic_key:pem_entry_decode,~nBody=~p~n)",[?MODULE,?LINE,{'CertificateList', Body, not_encrypted}]),
+ %% assume DER encoded
+ try
+ public_key:pem_entry_decode({'CertificateList', Body, not_encrypted})
+ of
+ CertList -> {Body, CertList}
+ catch
+ _C:_E ->
+ ct:log("~p:~p~nfailed DER assumption~nRest=~p", [?MODULE,?LINE,Rest]),
+ fetch(Rest)
+ end
+ end;
+ {error, _Reason} ->
+ ct:log("~p:~p~nfailed to get CRL ~p~n", [?MODULE,?LINE, _Reason]),
+ fetch(Rest);
+ Other ->
+ ct:log("~p:~p~nreally failed to get CRL ~p~n", [?MODULE,?LINE, Other]),
+ fetch(Rest)
+ end;
+fetch([Loc|Rest]) ->
+ %% unsupported CRL location
+ ct:log("~p:~p~nunable to fetch CRL from unsupported location ~p~n",
+ [?MODULE,?LINE, Loc]),
+ fetch(Rest).
+
+%% get the common name attribute out of an OTPCertificate record
+get_common_name(OTPCert) ->
+ %% You'd think there'd be an easier way than this giant mess, but I
+ %% couldn't find one.
+ {rdnSequence, Subject} = OTPCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject,
+ case [Attribute#'AttributeTypeAndValue'.value || [Attribute] <- Subject,
+ Attribute#'AttributeTypeAndValue'.type == ?'id-at-commonName'] of
+ [Att] ->
+ case Att of
+ {teletexString, Str} -> Str;
+ {printableString, Str} -> Str;
+ {utf8String, Bin} -> binary_to_list(Bin)
+ end;
+ _ ->
+ unknown
+ end.
+
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 7e8e8d2611..6d020c472b 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -34,6 +34,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() -> [decode_hello_handshake,
decode_single_hello_extension_correctly,
+ decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
encode_single_hello_sni_extension_correctly].
@@ -67,6 +68,17 @@ decode_single_hello_extension_correctly(_Config) ->
#renegotiation_info{renegotiated_connection = <<0>>}
= Extensions#hello_extensions.renegotiation_info.
+decode_supported_elliptic_curves_hello_extension_correctly(_Config) ->
+ % List of supported and unsupported curves (RFC4492:S5.1.1)
+ ClientEllipticCurves = [0, tls_v1:oid_to_enum(?sect233k1), 37, tls_v1:oid_to_enum(?sect193r2), 16#badc],
+ % Construct extension binary - modified version of ssl_handshake:encode_hello_extensions([#elliptic_curves{}], _)
+ EllipticCurveList = << <<X:16>> || X <- ClientEllipticCurves>>,
+ ListLen = byte_size(EllipticCurveList),
+ Len = ListLen + 2,
+ Extension = <<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary>>,
+ % after decoding we should see only valid curves
+ #hello_extensions{elliptic_curves = DecodedCurves} = ssl_handshake:decode_hello_extensions(Extension),
+ #elliptic_curves{elliptic_curve_list = [?sect233k1, ?sect193r2]} = DecodedCurves.
decode_unknown_hello_extension_correctly(_Config) ->
FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>,
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 7ed9adfcd9..7d8ece8d19 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -60,7 +60,7 @@ run_server(Opts) ->
Options = proplists:get_value(options, Opts),
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
- ct:log("ssl:listen(~p, ~p)~n", [Port, Options]),
+ ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]),
{ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
@@ -78,13 +78,13 @@ do_run_server(ListenSocket, AcceptSocket, Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- ct:log("Server: apply(~p,~p,~p)~n",
- [Module, Function, [AcceptSocket | Args]]),
+ ct:log("~p:~p~nServer: apply(~p,~p,~p)~n",
+ [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]),
case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of
no_result_msg ->
ok;
Msg ->
- ct:log("Server Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nServer Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg}
end,
receive
@@ -93,10 +93,10 @@ do_run_server(ListenSocket, AcceptSocket, Opts) ->
{listen, MFA} ->
run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]);
close ->
- ct:log("Server closing ~p ~n", [self()]),
+ ct:log("~p:~p~nServer closing ~p ~n", [?MODULE,?LINE, self()]),
Result = rpc:call(Node, Transport, close, [AcceptSocket], 500),
Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500),
- ct:log("Result ~p : ~p ~n", [Result, Result1]);
+ ct:log("~p:~p~nResult ~p : ~p ~n", [?MODULE,?LINE, Result, Result1]);
{ssl_closed, _} ->
ok
end.
@@ -116,7 +116,7 @@ connect(#sslsocket{} = ListenSocket, Opts) ->
end;
connect(ListenSocket, Opts) ->
Node = proplists:get_value(node, Opts),
- ct:log("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept,
[ListenSocket]),
AcceptSocket.
@@ -124,15 +124,17 @@ connect(ListenSocket, Opts) ->
connect(_, _, 0, AcceptSocket, _) ->
AcceptSocket;
connect(ListenSocket, Node, N, _, Timeout) ->
- ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~nssl:transport_accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
[ListenSocket]),
- ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, Timeout]),
case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of
ok ->
+ct:log("~p:~p~nok from ssl:ssl_accept@~p",[?MODULE,?LINE, Node]),
connect(ListenSocket, Node, N-1, AcceptSocket, Timeout);
Result ->
+ct:log("~p:~p~nssl:ssl_accept@~p ret ~p",[?MODULE,?LINE, Node,Result]),
Result
end.
@@ -148,11 +150,13 @@ remove_close_msg(ReconnectTimes) ->
start_client(Args) ->
Result = spawn_link(?MODULE, run_client_init, [lists:delete(return_socket, Args)]),
receive
- { connected, Socket } ->
- case lists:member(return_socket, Args) of
- true -> { Result, Socket };
- false -> Result
- end
+ {connected, Socket} ->
+ case lists:member(return_socket, Args) of
+ true -> {Result, Socket};
+ false -> Result
+ end;
+ {connect_failed, Reason} ->
+ {connect_failed, Reason}
end.
run_client_init(Opts) ->
@@ -166,27 +170,30 @@ run_client(Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]),
+ct:log("~p:~p~nnet_adm:ping(~p)=~p",[?MODULE,?LINE, Node,net_adm:ping(Node)]),
+%%ct:log("~p:~p~n~p:connect(~p, ~p, ~p)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Options, Node]),
+ct:log("~p:~p~n~p:connect(~p, ~p, ...)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Node]),
case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
{ok, Socket} ->
- Pid ! { connected, Socket },
- ct:log("Client: connected~n", []),
+ Pid ! {connected, Socket},
+ ct:log("~p:~p~nClient: connected~n", [?MODULE,?LINE]),
%% In special cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
send_selected_port(Pid, proplists:get_value(port, Options), Socket),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- ct:log("Client: apply(~p,~p,~p)~n",
- [Module, Function, [Socket | Args]]),
+ ct:log("~p:~p~nClient: apply(~p,~p,~p)~n",
+ [?MODULE,?LINE, Module, Function, [Socket | Args]]),
case rpc:call(Node, Module, Function, [Socket | Args]) of
no_result_msg ->
ok;
Msg ->
- ct:log("Client Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nClient Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg}
end,
receive
close ->
- ct:log("Client closing~n", []),
+ ct:log("~p:~p~nClient closing~n", [?MODULE,?LINE]),
rpc:call(Node, Transport, close, [Socket]);
{ssl_closed, Socket} ->
ok;
@@ -196,50 +203,42 @@ run_client(Opts) ->
{error, econnrefused = Reason} ->
case get(retries) of
N when N < 5 ->
+ ct:log("~p:~p~neconnrefused retries=~p sleep ~p",[?MODULE,?LINE, N,?SLEEP]),
put(retries, N+1),
ct:sleep(?SLEEP),
run_client(Opts);
_ ->
- ct:log("Client faild several times: connection failed: ~p ~n", [Reason]),
+ ct:log("~p:~p~nClient faild several times: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
Pid ! {self(), {error, Reason}}
end;
{error, Reason} ->
- ct:log("Client: connection failed: ~p ~n", [Reason]),
- Pid ! {self(), {error, Reason}}
+ ct:log("~p:~p~nClient: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
+ Pid ! {connect_failed, Reason};
+ {badrpc,BadRPC} ->
+ ct:log("~p:~p~nBad rpc: ~p",[?MODULE,?LINE, BadRPC]),
+ Pid ! {connect_failed, {badrpc,BadRPC}}
end.
close(Pid) ->
- ct:log("Close ~p ~n", [Pid]),
+ ct:log("~p:~p~nClose ~p ~n", [?MODULE,?LINE, Pid]),
Monitor = erlang:monitor(process, Pid),
Pid ! close,
receive
{'DOWN', Monitor, process, Pid, Reason} ->
erlang:demonitor(Monitor),
- ct:log("Pid: ~p down due to:~p ~n", [Pid, Reason])
+ ct:log("~p:~p~nPid: ~p down due to:~p ~n", [?MODULE,?LINE, Pid, Reason])
end.
check_result(Server, ServerMsg, Client, ClientMsg) ->
receive
- {Server, ServerMsg} ->
- receive
- {Client, ClientMsg} ->
- ok;
- Unexpected ->
- Reason = {{expected, {Client, ClientMsg}},
- {got, Unexpected}},
- ct:fail(Reason)
- end;
- {Client, ClientMsg} ->
- receive
- {Server, ServerMsg} ->
- ok;
- Unexpected ->
- Reason = {{expected, {Server, ClientMsg}},
- {got, Unexpected}},
- ct:fail(Reason)
- end;
+ {Server, ServerMsg} ->
+ check_result(Client, ClientMsg);
+
+ {Client, ClientMsg} ->
+ check_result(Server, ServerMsg);
+
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Server, ServerMsg, Client, ClientMsg);
Unexpected ->
@@ -253,7 +252,7 @@ check_result(Pid, Msg) ->
{Pid, Msg} ->
ok;
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Pid,Msg);
Unexpected ->
Reason = {{expected, {Pid, Msg}},
@@ -278,7 +277,7 @@ wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
%% Unexpected
end;
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
wait_for_result(Server, ServerMsg, Client, ClientMsg)
%% Unexpected ->
%% Unexpected
@@ -290,7 +289,7 @@ wait_for_result(Pid, Msg) ->
{Pid, Msg} ->
ok;
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
wait_for_result(Pid,Msg)
%% Unexpected ->
%% Unexpected
@@ -515,33 +514,33 @@ run_upgrade_server(Opts) ->
SslOptions = proplists:get_value(ssl_options, Opts),
Pid = proplists:get_value(from, Opts),
- ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ ct:log("~p:~p~ngen_tcp:listen(~p, ~p)~n", [?MODULE,?LINE, Port, TcpOptions]),
{ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
try
{ok, SslAcceptSocket} = case TimeOut of
infinity ->
- ct:log("ssl:ssl_accept(~p, ~p)~n",
- [AcceptSocket, SslOptions]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions]);
_ ->
- ct:log("ssl:ssl_accept(~p, ~p, ~p)~n",
- [AcceptSocket, SslOptions, TimeOut]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions, TimeOut]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions, TimeOut])
end,
{Module, Function, Args} = proplists:get_value(mfa, Opts),
Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]),
- ct:log("Upgrade Server Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nUpgrade Server Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg},
receive
close ->
- ct:log("Upgrade Server closing~n", []),
+ ct:log("~p:~p~nUpgrade Server closing~n", [?MODULE,?LINE]),
rpc:call(Node, ssl, close, [SslAcceptSocket])
end
catch error:{badmatch, Error} ->
@@ -559,24 +558,24 @@ run_upgrade_client(Opts) ->
TcpOptions = proplists:get_value(tcp_options, Opts),
SslOptions = proplists:get_value(ssl_options, Opts),
- ct:log("gen_tcp:connect(~p, ~p, ~p)~n",
- [Host, Port, TcpOptions]),
+ ct:log("~p:~p~ngen_tcp:connect(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, Host, Port, TcpOptions]),
{ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]),
send_selected_port(Pid, Port, Socket),
- ct:log("ssl:connect(~p, ~p)~n", [Socket, SslOptions]),
+ ct:log("~p:~p~nssl:connect(~p, ~p)~n", [?MODULE,?LINE, Socket, SslOptions]),
{ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- ct:log("apply(~p, ~p, ~p)~n",
- [Module, Function, [SslSocket | Args]]),
+ ct:log("~p:~p~napply(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, Module, Function, [SslSocket | Args]]),
Msg = rpc:call(Node, Module, Function, [SslSocket | Args]),
- ct:log("Upgrade Client Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nUpgrade Client Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg},
receive
close ->
- ct:log("Upgrade Client closing~n", []),
+ ct:log("~p:~p~nUpgrade Client closing~n", [?MODULE,?LINE]),
rpc:call(Node, ssl, close, [SslSocket])
end.
@@ -595,21 +594,21 @@ run_upgrade_server_error(Opts) ->
SslOptions = proplists:get_value(ssl_options, Opts),
Pid = proplists:get_value(from, Opts),
- ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ ct:log("~p:~p~ngen_tcp:listen(~p, ~p)~n", [?MODULE,?LINE, Port, TcpOptions]),
{ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
Error = case TimeOut of
infinity ->
- ct:log("ssl:ssl_accept(~p, ~p)~n",
- [AcceptSocket, SslOptions]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions]);
_ ->
- ct:log("ssl:ssl_accept(~p, ~p, ~p)~n",
- [AcceptSocket, SslOptions, TimeOut]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions, TimeOut]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions, TimeOut])
end,
@@ -628,26 +627,26 @@ run_server_error(Opts) ->
Options = proplists:get_value(options, Opts),
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
- ct:log("ssl:listen(~p, ~p)~n", [Port, Options]),
+ ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]),
case rpc:call(Node, Transport, listen, [Port, Options]) of
{ok, #sslsocket{} = ListenSocket} ->
%% To make sure error_client will
%% get {error, closed} and not {error, connection_refused}
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~nssl:transport_accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of
{error, _} = Error ->
Pid ! {self(), Error};
{ok, AcceptSocket} ->
- ct:log("ssl:ssl_accept(~p)~n", [AcceptSocket]),
+ ct:log("~p:~p~nssl:ssl_accept(~p)~n", [?MODULE,?LINE, AcceptSocket]),
Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
Pid ! {self(), Error}
end;
{ok, ListenSocket} ->
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("~p:accept(~p)~n", [Transport, ListenSocket]),
+ ct:log("~p:~p~n~p:accept(~p)~n", [?MODULE,?LINE, Transport, ListenSocket]),
case rpc:call(Node, Transport, accept, [ListenSocket]) of
{error, _} = Error ->
Pid ! {self(), Error}
@@ -669,7 +668,7 @@ run_client_error(Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]),
Error = rpc:call(Node, Transport, connect, [Host, Port, Options]),
Pid ! {self(), Error}.
@@ -892,7 +891,7 @@ der_to_pem(File, Entries) ->
cipher_result(Socket, Result) ->
Result = ssl:connection_info(Socket),
- ct:log("Successfull connect: ~p~n", [Result]),
+ ct:log("~p:~p~nSuccessfull connect: ~p~n", [?MODULE,?LINE, Result]),
%% Importante to send two packets here
%% to properly test "cipher state" handling
ssl:send(Socket, "Hello\n"),
@@ -1061,10 +1060,13 @@ check_sane_openssl_version(Version) ->
true
end.
+enough_openssl_crl_support("OpenSSL 0." ++ _) -> false;
+enough_openssl_crl_support(_) -> true.
+
wait_for_openssl_server() ->
receive
{Port, {data, Debug}} when is_port(Port) ->
- ct:log("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
%% openssl has started make sure
%% it will be in accept. Parsing
%% output is too error prone. (Even
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index a6e0efed25..e08f5dff78 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3.3
+SSL_VSN = 5.3.4
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 6f1e61e70c..ff77c3eea0 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -71,6 +71,7 @@ XML_REF3_FILES = \
lib.xml \
lists.xml \
log_mf_h.xml \
+ maps.xml \
math.xml \
ms_transform.xml \
orddict.xml \
diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml
index b0b3aa6dc1..b03a2fa0cc 100644
--- a/lib/stdlib/doc/src/array.xml
+++ b/lib/stdlib/doc/src/array.xml
@@ -3,7 +3,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2013</year>
+ <year>2007</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -84,7 +84,7 @@ the default value cannot be confused with the values of set entries.</p>
{'EXIT',{badarg,_}} = (catch array:get(18, A3)).</pre></description>
<datatypes>
<datatype>
- <name><marker id="type-array">array()</marker></name>
+ <name name="array" n_vars="1"/>
<desc>
<p>A functional, extendible array. The representation is
not documented and is subject to change without notice. Note that
@@ -92,6 +92,12 @@ the default value cannot be confused with the values of set entries.</p>
</desc>
</datatype>
<datatype>
+ <name name="array" n_vars="0"/>
+ <desc>
+ <p><c>array()</c> is equivalent to <c>array(term())</c>.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="array_indx"/>
</datatype>
<datatype>
@@ -189,7 +195,7 @@ the default value cannot be confused with the values of set entries.</p>
<desc><marker id="from_orddict-2"/>
-<p>Convert an ordered list of pairs <c>{Index, Value}</c> to a
+<p>Convert an ordered list of pairs <c>{Index, <anno>Value</anno>}</c> to a
corresponding extendible array. <c><anno>Default</anno></c> is used as the value for
uninitialized entries of the array. If <c><anno>Orddict</anno></c> is not a proper,
ordered list of pairs whose first elements are nonnegative
@@ -455,7 +461,7 @@ cannot be changed once the array has been created.</p>
<desc><marker id="sparse_to_orddict-1"/>
-<p>Convert the array to an ordered list of pairs <c>{Index, Value}</c>,
+<p>Convert the array to an ordered list of pairs <c>{Index, <anno>Value</anno>}</c>,
skipping default-valued entries.
</p>
<p><em>See also:</em> <seealso marker="#to_orddict-1">to_orddict/1</seealso>.</p>
@@ -476,7 +482,7 @@ cannot be changed once the array has been created.</p>
<desc><marker id="to_orddict-1"/>
-<p>Convert the array to an ordered list of pairs <c>{Index, Value}</c>.
+<p>Convert the array to an ordered list of pairs <c>{Index, <anno>Value</anno>}</c>.
</p>
<p><em>See also:</em> <seealso marker="#from_orddict-2">from_orddict/2</seealso>, <seealso marker="#sparse_to_orddict-1">sparse_to_orddict/1</seealso>.</p>
</desc></func></funcs>
diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml
index 6ff81b56ee..942fd1f45e 100644
--- a/lib/stdlib/doc/src/dict.xml
+++ b/lib/stdlib/doc/src/dict.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,9 +41,15 @@
<datatypes>
<datatype>
- <name><marker id="type-dict">dict()</marker></name>
+ <name name="dict" n_vars="2"/>
<desc><p>Dictionary as returned by <c>new/0</c>.</p></desc>
</datatype>
+ <datatype>
+ <name name="dict" n_vars="0"/>
+ <desc>
+ <p><c>dict()</c> is equivalent to <c>dict(term(), term())</c>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml
index c5d5707f4f..9b9b48584b 100644
--- a/lib/stdlib/doc/src/digraph.xml
+++ b/lib/stdlib/doc/src/digraph.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -98,7 +98,7 @@
<name name="d_protection"/>
</datatype>
<datatype>
- <name><marker id="type-digraph">digraph()</marker></name>
+ <name name="graph"/>
<desc><p>A digraph as returned by <c>new/0,1</c>.</p></desc>
</datatype>
<datatype>
diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml
index cf33530395..452341f7d2 100644
--- a/lib/stdlib/doc/src/epp.xml
+++ b/lib/stdlib/doc/src/epp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -46,8 +46,10 @@
valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the
case of the characters can be chosen freely. Examples:</p>
<pre>
-%% coding: utf-8
-%% For this file we have chosen encoding = Latin-1
+%% coding: utf-8</pre>
+ <pre>
+%% For this file we have chosen encoding = Latin-1</pre>
+ <pre>
%% -*- coding: latin-1 -*-</pre>
</description>
<datatypes>
@@ -64,11 +66,29 @@
</datatypes>
<funcs>
<func>
+ <name name="open" arity="1"/>
+ <fsummary>Open a file for preprocessing</fsummary>
+ <desc>
+ <p>Opens a file for preprocessing.</p>
+ <p>If <c>extra</c> is given in
+ <c><anno>Options</anno></c>, the return value will be
+ <c>{ok, <anno>Epp</anno>, <anno>Extra</anno>}</c> instead
+ of <c>{ok, <anno>Epp</anno>}</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="open" arity="2"/>
+ <fsummary>Open a file for preprocessing</fsummary>
+ <desc>
+ <p>Equivalent to <c>epp:open([{name, FileName}, {includes, IncludePath}])</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="open" arity="3"/>
<fsummary>Open a file for preprocessing</fsummary>
<desc>
- <p>Opens a file for preprocessing.</p>
+ <p>Equivalent to <c>epp:open([{name, FileName}, {includes, IncludePath},
+ {macros, PredefMacros}])</c>.</p>
</desc>
</func>
<func>
@@ -89,12 +109,24 @@
</desc>
</func>
<func>
- <name name="parse_file" arity="3"/>
+ <name name="parse_file" arity="2"/>
<fsummary>Preprocess and parse an Erlang source file</fsummary>
<desc>
<p>Preprocesses and parses an Erlang source file.
- Note that the tuple <c>{eof, <anno>Line</anno>}</c> returned at end-of-file is
- included as a "form".</p>
+ Note that the tuple <c>{eof, <anno>Line</anno>}</c> returned
+ at end-of-file is included as a "form".</p>
+ <p>If <c>extra</c> is given in
+ <c><anno>Options</anno></c>, the return value will be
+ <c>{ok, [<anno>Form</anno>], <anno>Extra</anno>}</c> instead
+ of <c>{ok, [<anno>Form</anno>]}</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_file" arity="3"/>
+ <fsummary>Preprocess and parse an Erlang source file</fsummary>
+ <desc>
+ <p>Equivalent to <c>epp:parse_file(FileName, [{includes, IncludePath},
+ {macros, PredefMacros}])</c>.</p>
</desc>
</func>
<func>
@@ -111,7 +143,7 @@
<p>Returns a string representation of an encoding. The string
is recognized by <c>read_encoding/1,2</c>,
<c>read_encoding_from_binary/1,2</c>, and
- <c>set_encoding/1</c> as a valid encoding.</p>
+ <c>set_encoding/1,2</c> as a valid encoding.</p>
</desc>
</func>
<func>
@@ -157,6 +189,22 @@
</desc>
</func>
<func>
+ <name name="set_encoding" arity="2"/>
+ <fsummary>Read and set the encoding of an IO device</fsummary>
+ <desc>
+ <p>Reads the <seealso marker="#encoding">encoding</seealso> from
+ an IO device and sets the encoding of the device
+ accordingly. The position of the IO device referenced by
+ <c><anno>File</anno></c> is not affected. If no valid
+ encoding can be read from the IO device the encoding of the
+ IO device is set to the
+ <seealso marker="#encoding">encoding</seealso> given by
+ <c><anno>Default</anno></c>.</p>
+ <p>Returns the read encoding, or <c>none</c> if no valid
+ encoding was found.</p>
+ </desc>
+ </func>
+ <func>
<name name="format_error" arity="1"/>
<fsummary>Format an error descriptor</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml
index 2d5aff3c6c..cf0bff48cd 100644
--- a/lib/stdlib/doc/src/erl_parse.xml
+++ b/lib/stdlib/doc/src/erl_parse.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -173,6 +173,7 @@
</func>
<func>
<name name="abstract" arity="2"/>
+ <type name="encoding_func"/>
<fsummary>Convert an Erlang term into an abstract form</fsummary>
<desc>
<p>Converts the Erlang data structure <c><anno>Data</anno></c> into an
@@ -183,7 +184,12 @@
selecting which integer lists will be considered
as strings. The default is to use the encoding returned by
<seealso marker="epp#default_encoding/0">
- <c>epp:default_encoding/0</c></seealso></p>
+ <c>epp:default_encoding/0</c></seealso>.
+ The value <c>none</c> means that no integer lists will be
+ considered as strings. The <c>encoding_func()</c> will be
+ called with one integer of a list at a time, and if it
+ returns <c>true</c> for every integer the list will be
+ considered a string.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 21cf8e4149..3df24bf688 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -171,6 +171,10 @@
<p>Returns a list of all tables at the node. Named tables are
given by their names, unnamed tables are given by their
table identifiers.</p>
+ <p>There is no guarantee of consistency in the returned list. Tables created
+ or deleted by other processes "during" the ets:all() call may or may
+ not be included in the list. Only tables created/deleted <em>before</em>
+ ets:all() is called are guaranteed to be included/excluded.</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml
index 8ca95df8fc..ea96c14472 100644
--- a/lib/stdlib/doc/src/gb_sets.xml
+++ b/lib/stdlib/doc/src/gb_sets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2013</year>
+ <year>2001</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -115,28 +115,40 @@
<datatypes>
<datatype>
- <name><marker id="type-gb_set">gb_set()</marker></name>
+ <name name="set" n_vars="1"/>
<desc><p>A GB set.</p></desc>
</datatype>
<datatype>
- <name name="iter"/>
+ <name name="set" n_vars="0"/>
+ <desc>
+ <p><c>set()</c> is equivalent to <c>set(term())</c>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="iter" n_vars="1"/>
<desc><p>A GB set iterator.</p></desc>
</datatype>
+ <datatype>
+ <name name="iter" n_vars="0"/>
+ <desc>
+ <p><c>iter()</c> is equivalent to <c>iter(term())</c>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
<name name="add" arity="2"/>
<name name="add_element" arity="2"/>
- <fsummary>Add a (possibly existing) element to a gb_set</fsummary>
+ <fsummary>Add a (possibly existing) element to a set</fsummary>
<desc>
- <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with
+ <p>Returns a new set formed from <c><anno>Set1</anno></c> with
<c><anno>Element</anno></c> inserted. If <c><anno>Element</anno></c> is already an
element in <c><anno>Set1</anno></c>, nothing is changed.</p>
</desc>
</func>
<func>
<name name="balance" arity="1"/>
- <fsummary>Rebalance tree representation of a gb_set</fsummary>
+ <fsummary>Rebalance tree representation of a set</fsummary>
<desc>
<p>Rebalances the tree representation of <c><anno>Set1</anno></c>. Note that
this is rarely necessary, but may be motivated when a large
@@ -148,9 +160,9 @@
</func>
<func>
<name name="delete" arity="2"/>
- <fsummary>Remove an element from a gb_set</fsummary>
+ <fsummary>Remove an element from a set</fsummary>
<desc>
- <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with
+ <p>Returns a new set formed from <c><anno>Set1</anno></c> with
<c><anno>Element</anno></c> removed. Assumes that <c><anno>Element</anno></c> is present
in <c><anno>Set1</anno></c>.</p>
</desc>
@@ -158,9 +170,9 @@
<func>
<name name="delete_any" arity="2"/>
<name name="del_element" arity="2"/>
- <fsummary>Remove a (possibly non-existing) element from a gb_set</fsummary>
+ <fsummary>Remove a (possibly non-existing) element from a set</fsummary>
<desc>
- <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with
+ <p>Returns a new set formed from <c><anno>Set1</anno></c> with
<c><anno>Element</anno></c> removed. If <c><anno>Element</anno></c> is not an element
in <c><anno>Set1</anno></c>, nothing is changed.</p>
</desc>
@@ -168,7 +180,7 @@
<func>
<name name="difference" arity="2"/>
<name name="subtract" arity="2"/>
- <fsummary>Return the difference of two gb_sets</fsummary>
+ <fsummary>Return the difference of two sets</fsummary>
<desc>
<p>Returns only the elements of <c><anno>Set1</anno></c> which are not also
elements of <c><anno>Set2</anno></c>.</p>
@@ -177,14 +189,14 @@
<func>
<name name="empty" arity="0"/>
<name name="new" arity="0"/>
- <fsummary>Return an empty gb_set</fsummary>
+ <fsummary>Return an empty set</fsummary>
<desc>
- <p>Returns a new empty gb_set.</p>
+ <p>Returns a new empty set.</p>
</desc>
</func>
<func>
<name name="filter" arity="2"/>
- <fsummary>Filter gb_set elements</fsummary>
+ <fsummary>Filter set elements</fsummary>
<desc>
<p>Filters elements in <c><anno>Set1</anno></c> using predicate function
<c><anno>Pred</anno></c>.</p>
@@ -192,7 +204,7 @@
</func>
<func>
<name name="fold" arity="3"/>
- <fsummary>Fold over gb_set elements</fsummary>
+ <fsummary>Fold over set elements</fsummary>
<desc>
<p>Folds <c><anno>Function</anno></c> over every element in <c><anno>Set</anno></c>
returning the final value of the accumulator.</p>
@@ -200,46 +212,46 @@
</func>
<func>
<name name="from_list" arity="1"/>
- <fsummary>Convert a list into a gb_set</fsummary>
+ <fsummary>Convert a list into a set</fsummary>
<desc>
- <p>Returns a gb_set of the elements in <c><anno>List</anno></c>, where
+ <p>Returns a set of the elements in <c><anno>List</anno></c>, where
<c><anno>List</anno></c> may be unordered and contain duplicates.</p>
</desc>
</func>
<func>
<name name="from_ordset" arity="1"/>
- <fsummary>Make a gb_set from an ordset list</fsummary>
+ <fsummary>Make a set from an ordset list</fsummary>
<desc>
- <p>Turns an ordered-set list <c><anno>List</anno></c> into a gb_set. The list
+ <p>Turns an ordered-set list <c><anno>List</anno></c> into a set. The list
must not contain duplicates.</p>
</desc>
</func>
<func>
<name name="insert" arity="2"/>
- <fsummary>Add a new element to a gb_set</fsummary>
+ <fsummary>Add a new element to a set</fsummary>
<desc>
- <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with
+ <p>Returns a new set formed from <c><anno>Set1</anno></c> with
<c><anno>Element</anno></c> inserted. Assumes that <c><anno>Element</anno></c> is not
present in <c><anno>Set1</anno></c>.</p>
</desc>
</func>
<func>
<name name="intersection" arity="2"/>
- <fsummary>Return the intersection of two gb_sets</fsummary>
+ <fsummary>Return the intersection of two sets</fsummary>
<desc>
<p>Returns the intersection of <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p>
</desc>
</func>
<func>
<name name="intersection" arity="1"/>
- <fsummary>Return the intersection of a list of gb_sets</fsummary>
+ <fsummary>Return the intersection of a list of sets</fsummary>
<desc>
- <p>Returns the intersection of the non-empty list of gb_sets.</p>
+ <p>Returns the intersection of the non-empty list of sets.</p>
</desc>
</func>
<func>
<name name="is_disjoint" arity="2"/>
- <fsummary>Check whether two gb_sets are disjoint</fsummary>
+ <fsummary>Check whether two sets are disjoint</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Set1</anno></c> and
<c><anno>Set2</anno></c> are disjoint (have no elements in common),
@@ -248,7 +260,7 @@
</func>
<func>
<name name="is_empty" arity="1"/>
- <fsummary>Test for empty gb_set</fsummary>
+ <fsummary>Test for empty set</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Set</anno></c> is an empty set, and
<c>false</c> otherwise.</p>
@@ -257,7 +269,7 @@
<func>
<name name="is_member" arity="2"/>
<name name="is_element" arity="2"/>
- <fsummary>Test for membership of a gb_set</fsummary>
+ <fsummary>Test for membership of a set</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of
<c><anno>Set</anno></c>, otherwise <c>false</c>.</p>
@@ -265,9 +277,9 @@
</func>
<func>
<name name="is_set" arity="1"/>
- <fsummary>Test for a gb_set</fsummary>
+ <fsummary>Test for a set</fsummary>
<desc>
- <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a gb_set,
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a set,
otherwise <c>false</c>.</p>
</desc>
</func>
@@ -281,7 +293,7 @@
</func>
<func>
<name name="iterator" arity="1"/>
- <fsummary>Return an iterator for a gb_set</fsummary>
+ <fsummary>Return an iterator for a set</fsummary>
<desc>
<p>Returns an iterator that can be used for traversing the
entries of <c><anno>Set</anno></c>; see <c>next/1</c>. The implementation
@@ -303,7 +315,7 @@
</func>
<func>
<name name="next" arity="1"/>
- <fsummary>Traverse a gb_set with an iterator</fsummary>
+ <fsummary>Traverse a set with an iterator</fsummary>
<desc>
<p>Returns <c>{<anno>Element</anno>, <anno>Iter2</anno>}</c> where <c><anno>Element</anno></c> is the
smallest element referred to by the iterator <c><anno>Iter1</anno></c>,
@@ -314,14 +326,14 @@
</func>
<func>
<name name="singleton" arity="1"/>
- <fsummary>Return a gb_set with one element</fsummary>
+ <fsummary>Return a set with one element</fsummary>
<desc>
- <p>Returns a gb_set containing only the element <c><anno>Element</anno></c>.</p>
+ <p>Returns a set containing only the element <c><anno>Element</anno></c>.</p>
</desc>
</func>
<func>
<name name="size" arity="1"/>
- <fsummary>Return the number of elements in a gb_set</fsummary>
+ <fsummary>Return the number of elements in a set</fsummary>
<desc>
<p>Returns the number of elements in <c><anno>Set</anno></c>.</p>
</desc>
@@ -356,24 +368,24 @@
</func>
<func>
<name name="to_list" arity="1"/>
- <fsummary>Convert a gb_set into a list</fsummary>
+ <fsummary>Convert a set into a list</fsummary>
<desc>
<p>Returns the elements of <c><anno>Set</anno></c> as a list.</p>
</desc>
</func>
<func>
<name name="union" arity="2"/>
- <fsummary>Return the union of two gb_sets</fsummary>
+ <fsummary>Return the union of two sets</fsummary>
<desc>
- <p>Returns the merged (union) gb_set of <c><anno>Set1</anno></c> and
+ <p>Returns the merged (union) set of <c><anno>Set1</anno></c> and
<c><anno>Set2</anno></c>.</p>
</desc>
</func>
<func>
<name name="union" arity="1"/>
- <fsummary>Return the union of a list of gb_sets</fsummary>
+ <fsummary>Return the union of a list of sets</fsummary>
<desc>
- <p>Returns the merged (union) gb_set of the list of gb_sets.</p>
+ <p>Returns the merged (union) set of the list of sets.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml
index 57e60eacb7..b2f237e1d7 100644
--- a/lib/stdlib/doc/src/gb_trees.xml
+++ b/lib/stdlib/doc/src/gb_trees.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2013</year>
+ <year>2001</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -59,13 +59,25 @@
<datatypes>
<datatype>
- <name><marker id="type-gb_tree">gb_tree()</marker></name>
+ <name name="tree" n_vars="2"/>
<desc><p>A GB tree.</p></desc>
</datatype>
<datatype>
- <name name="iter"/>
+ <name name="tree" n_vars="0"/>
+ <desc>
+ <p><c>tree()</c> is equivalent to <c>tree(term(), term())</c>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="iter" n_vars="2"/>
<desc><p>A GB tree iterator.</p></desc>
</datatype>
+ <datatype>
+ <name name="iter" n_vars="0"/>
+ <desc>
+ <p><c>iter()</c> is equivalent to <c>iter(term(), term())</c>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
@@ -108,9 +120,9 @@
<name name="enter" arity="3"/>
<fsummary>Insert or update key with value in a tree</fsummary>
<desc>
- <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c> if
+ <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> into <c><anno>Tree1</anno></c> if
the key is not present in the tree, otherwise updates
- <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>. Returns the
+ <c><anno>Key</anno></c> to value <c><anno>Value</anno></c> in <c><anno>Tree1</anno></c>. Returns the
new tree.</p>
</desc>
</func>
@@ -135,7 +147,7 @@
<name name="insert" arity="3"/>
<fsummary>Insert a new key and value in a tree</fsummary>
<desc>
- <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c>;
+ <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> into <c><anno>Tree1</anno></c>;
returns the new tree. Assumes that the key is not present in
the tree, crashes otherwise.</p>
</desc>
@@ -181,8 +193,8 @@
<name name="largest" arity="1"/>
<fsummary>Return largest key and value</fsummary>
<desc>
- <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the largest
- key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated
+ <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>}</c>, where <c><anno>Key</anno></c> is the largest
+ key in <c><anno>Tree</anno></c>, and <c><anno>Value</anno></c> is the value associated
with this key. Assumes that the tree is nonempty.</p>
</desc>
</func>
@@ -191,7 +203,7 @@
<fsummary>Look up a key in a tree</fsummary>
<desc>
<p>Looks up <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>; returns
- <c>{value, <anno>Val</anno>}</c>, or <c>none</c> if <c><anno>Key</anno></c> is not
+ <c>{value, <anno>Value</anno>}</c>, or <c>none</c> if <c><anno>Key</anno></c> is not
present.</p>
</desc>
</func>
@@ -207,7 +219,7 @@
<name name="next" arity="1"/>
<fsummary>Traverse a tree with an iterator</fsummary>
<desc>
- <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Iter2</anno>}</c> where <c><anno>Key</anno></c> is the
+ <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, <anno>Iter2</anno>}</c> where <c><anno>Key</anno></c> is the
smallest key referred to by the iterator <c><anno>Iter1</anno></c>, and
<c><anno>Iter2</anno></c> is the new iterator to be used for
traversing the remaining nodes, or the atom <c>none</c> if no
@@ -225,8 +237,8 @@
<name name="smallest" arity="1"/>
<fsummary>Return smallest key and value</fsummary>
<desc>
- <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the smallest
- key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated
+ <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>}</c>, where <c><anno>Key</anno></c> is the smallest
+ key in <c><anno>Tree</anno></c>, and <c><anno>Value</anno></c> is the value associated
with this key. Assumes that the tree is nonempty.</p>
</desc>
</func>
@@ -234,8 +246,8 @@
<name name="take_largest" arity="1"/>
<fsummary>Extract largest key and value</fsummary>
<desc>
- <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the
- largest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value
+ <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the
+ largest key in <c><anno>Tree1</anno></c>, <c><anno>Value</anno></c> is the value
associated with this key, and <c><anno>Tree2</anno></c> is this tree with
the corresponding node deleted. Assumes that the tree is
nonempty.</p>
@@ -245,8 +257,8 @@
<name name="take_smallest" arity="1"/>
<fsummary>Extract smallest key and value</fsummary>
<desc>
- <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the
- smallest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value
+ <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the
+ smallest key in <c><anno>Tree1</anno></c>, <c><anno>Value</anno></c> is the value
associated with this key, and <c><anno>Tree2</anno></c> is this tree with
the corresponding node deleted. Assumes that the tree is
nonempty.</p>
@@ -263,7 +275,7 @@
<name name="update" arity="3"/>
<fsummary>Update a key to new value in a tree</fsummary>
<desc>
- <p>Updates <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>;
+ <p>Updates <c><anno>Key</anno></c> to value <c><anno>Value</anno></c> in <c><anno>Tree1</anno></c>;
returns the new tree. Assumes that the key is present in the
tree.</p>
</desc>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 251a383cf8..ee3c51c62c 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -123,6 +123,14 @@
</desc>
</func>
<func>
+ <name name="droplast" arity="1"/>
+ <fsummary>Drop the last element of a list</fsummary>
+ <desc>
+ <p>Drops the last element of a <c><anno>List</anno></c>. The list should
+ be non-empty, otherwise the function will crash with a <c>function_clause</c></p>
+ </desc>
+ </func>
+ <func>
<name name="dropwhile" arity="2"/>
<fsummary>Drop elements from a list while a predicate is true</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
new file mode 100644
index 0000000000..76137e3dee
--- /dev/null
+++ b/lib/stdlib/doc/src/maps.xml
@@ -0,0 +1,318 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2013</year><year>2014</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.
+ </legalnotice>
+ <title>maps</title>
+ <prepared>Björn-Egil Dahlberg</prepared>
+ <docno>1</docno>
+ <date>2014-02-28</date>
+ <rev>A</rev>
+ </header>
+ <module>maps</module>
+ <modulesummary>Maps Processing Functions</modulesummary>
+ <description>
+ <p>This module contains functions for maps processing.</p>
+ </description>
+ <funcs>
+
+ <func>
+ <name name="find" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a tuple <c>{ok, Value}</c> where <c><anno>Value</anno></c> is the value associated with <c><anno>Key</anno></c>,
+ or <c>error</c> if no value is associated with <c><anno>Key</anno></c> in <c><anno>Map</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"hi" => 42},
+ Key = "hi",
+ maps:find(Key,Map).
+{ok,42} </code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="fold" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Calls <c>F(K, V, AccIn)</c> for every <c><anno>K</anno></c> to value <c><anno>V</anno></c>
+ association in <c><anno>Map</anno></c> in
+ arbitrary order. The function <c>fun F/3</c> must return a new accumulator
+ which is passed to the next successive call. <c>maps:fold/3</c> returns the final
+ value of the accumulator. The initial accumulator value <c><anno>Init</anno></c> is returned if
+ the map is empty.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Fun = fun(K,V,AccIn) when is_list(K) -> AccIn + V end,
+ Map = #{"k1" => 1, "k2" => 2, "k3" => 3},
+ maps:fold(Fun,0,Map).
+6</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="from_list" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function takes a list of key-value tuples elements and builds a
+ map. The associations may be in any order and both keys and values in the
+ association may be of any term. If the same key appears more than once,
+ the latter (rightmost) value is used and the previous values are ignored.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> List = [{"a",ignored},{1337,"value two"},{42,value_three},{"a",1}],
+ maps:from_list(List).
+#{42 => value_three,1337 => "value two","a" => 1}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="get" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns the value <c><anno>Value</anno></c> associated with <c><anno>Key</anno></c> if
+ <c><anno>Map</anno></c> contains <c><anno>Key</anno></c>.
+ If no value is associated with <c><anno>Key</anno></c> then the call will
+ fail with an exception.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Key = 1337,
+ Map = #{42 => value_two,1337 => "value one","a" => 1},
+ maps:get(Key,Map).
+"value one"</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="is_key" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns <c>true</c> if map <c><anno>Map</anno></c> contains <c><anno>Key</anno></c> and returns
+ <c>false</c> if it does not contain the <c><anno>Key</anno></c>.
+ The function will fail with an exception if <c><anno>Map</anno></c> is not a Map.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"42" => value}.
+#{"42"> => value}
+> maps:is_key("42",Map).
+true
+> maps:is_key(value,Map).
+false</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="keys" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a complete list of keys, in arbitrary order, which resides within <c><anno>Map</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ maps:keys(Map).
+[42,1337,"a"]</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="map" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function produces a new map <c><anno>Map2</anno></c> by calling the function <c>fun F(K, V1)</c> for
+ every <c><anno>K</anno></c> to value <c><anno>V1</anno></c> association in <c><anno>Map1</anno></c> in arbitrary order.
+ The function <c>fun F/2</c> must return the value <c><anno>V2</anno></c> to be associated with key <c><anno>K</anno></c> for
+ the new map <c><anno>Map2</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Fun = fun(K,V1) when is_list(K) -> V1*2 end,
+ Map = #{"k1" => 1, "k2" => 2, "k3" => 3},
+ maps:map(Fun,Map).
+#{"k1" => 2,"k2" => 4,"k3" => 6}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="merge" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Merges two maps into a single map <c><anno>Map3</anno></c>. If two keys exists in both maps the
+ value in <c><anno>Map1</anno></c> will be superseded by the value in <c><anno>Map2</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map1 = #{a => "value_one", b => "value_two"},
+ Map2 = #{a => 1, c => 2},
+ maps:merge(Map1,Map2).
+#{a => 1,b => "value_two",c => 2}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="new" arity="0"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a new empty map.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> maps:new().
+#{}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="put" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Associates <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> and inserts the association into map <c>Map2</c>.
+ If key <c><anno>Key</anno></c> already exists in map <c><anno>Map1</anno></c>, the old associated value is
+ replaced by value <c><anno>Value</anno></c>. The function returns a new map <c><anno>Map2</anno></c> containing the new association and
+ the old associations in <c><anno>Map1</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => 1}.
+#{"a" => 1}
+> maps:put("a", 42, Map).
+#{"a" => 42}
+> maps:put("b", 1337, Map).
+#{"a" => 1,"b" => 1337}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="remove" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function removes the <c><anno>Key</anno></c>, if it exists, and its associated value from
+ <c><anno>Map1</anno></c> and returns a new map <c><anno>Map2</anno></c> without key <c><anno>Key</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => 1}.
+#{"a" => 1}
+> maps:remove("a",Map).
+#{}
+> maps:remove("b",Map).
+#{"a" => 1}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="size" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function returns the number of key-value associations in the <c><anno>Map</anno></c>.
+ This operation happens in constant time.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_two,1337 => "value one","a" => 1},
+ maps:size(Map).
+3</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="to_list" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The fuction returns a list of pairs representing the key-value associations of <c><anno>Map</anno></c>,
+ where the pairs, <c>[{K1,V1}, ..., {Kn,Vn}]</c>, are returned in arbitrary order.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ maps:to_list(Map).
+[{42,value_three},{1337,"value two"},{"a",1}]</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="update" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ If <c><anno>Key</anno></c> exists in <c><anno>Map1</anno></c> the old associated value is
+ replaced by value <c><anno>Value</anno></c>. The function returns a new map <c><anno>Map2</anno></c> containing
+ the new associated value. If <c><anno>Key</anno></c> does not exist in <c><anno>Map1</anno></c> an exception is
+ generated.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => 1}.
+#{"a" => 1}
+> maps:update("a", 42, Map).
+#{"a" => 42}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="values" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a complete list of values, in arbitrary order, contained in map <c>M</c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ maps:values(Map).
+[value_three,"value two",1]</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="without" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a new map <c><anno>Map2</anno></c> without the keys <c>K1</c> through <c>Kn</c> and their associated values from map <c><anno>Map1</anno></c>.
+ Any key in <c><anno>Ks</anno></c> that does not exist in <c><anno>Map1</anno></c> are ignored.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ Ks = ["a",42,"other key"],
+ maps:without(Ks,Map).
+#{1337 => "value two"}</code>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml
index bcf063bf0f..9c994154d4 100644
--- a/lib/stdlib/doc/src/queue.xml
+++ b/lib/stdlib/doc/src/queue.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -90,9 +90,15 @@
<datatypes>
<datatype>
- <name><marker id="type-queue">queue()</marker></name>
+ <name name="queue" n_vars="1"/>
<desc><p>As returned by <c>new/0</c>.</p></desc>
</datatype>
+ <datatype>
+ <name name="queue" n_vars="0"/>
+ <desc>
+ <p><c>queue()</c> is equivalent to <c>queue(term())</c>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index 4ecd02a4bf..6c35578bdf 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -68,6 +68,7 @@
<xi:include href="lib.xml"/>
<xi:include href="lists.xml"/>
<xi:include href="log_mf_h.xml"/>
+ <xi:include href="maps.xml"/>
<xi:include href="math.xml"/>
<xi:include href="ms_transform.xml"/>
<xi:include href="orddict.xml"/>
diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml
index 4b220bdd85..c5b8dce4b7 100644
--- a/lib/stdlib/doc/src/sets.xml
+++ b/lib/stdlib/doc/src/sets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2013</year>
+ <year>2000</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -45,9 +45,15 @@
<datatypes>
<datatype>
- <name><marker id="type-dict">set()</marker></name>
+ <name name="set" n_vars="1"/>
<desc><p>As returned by <c>new/0</c>.</p></desc>
</datatype>
+ <datatype>
+ <name name="set" n_vars="0"/>
+ <desc>
+ <p><c>set()</c> is equivalent to <c>set(term())</c>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml
index 213ce7563f..60a04ed5e7 100644
--- a/lib/stdlib/doc/src/specs.xml
+++ b/lib/stdlib/doc/src/specs.xml
@@ -34,6 +34,7 @@
<xi:include href="../specs/specs_lib.xml"/>
<xi:include href="../specs/specs_lists.xml"/>
<xi:include href="../specs/specs_log_mf_h.xml"/>
+ <xi:include href="../specs/specs_maps.xml"/>
<xi:include href="../specs/specs_math.xml"/>
<xi:include href="../specs/specs_ms_transform.xml"/>
<xi:include href="../specs/specs_orddict.xml"/>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 8197684d2d..3a5027d595 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -262,12 +262,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
locally as <c>Name</c> using <c>register/2</c>. If
<c><anno>SupName</anno>={global,Name}</c> the supervisor is registered
globally as <c>Name</c> using <c>global:register_name/2</c>. If
- <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor
+ <c><anno>SupName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor
is registered as <c>Name</c> using the registry represented by
<c>Module</c>. The <c>Module</c> callback should export the functions
<c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>,
which should behave like the corresponding functions in <c>global</c>.
- Thus, <c>{via,global,Name}</c> is a valid reference.</p>
+ Thus, <c>{via,global,<anno>Name</anno>}</c> is a valid reference.</p>
<p>If no name is provided, the supervisor is not registered.</p>
<p><c><anno>Module</anno></c> is the name of the callback module.</p>
<p><c><anno>Args</anno></c> is an arbitrary term which is passed as
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index ab8b380f49..a46fa1289f 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -246,6 +246,22 @@
<c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name,
<c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without
an ID), and <c>HandlerState</c> is the handler's state.</p>
+ <p>If the callback module exports a <c>system_get_state/1</c> function, it will be called in the
+ target process to get its state. Its argument is the same as the <c>Misc</c> value returned by
+ <seealso marker="#get_status-1">get_status/1,2</seealso>, and the <c>system_get_state/1</c>
+ function is expected to extract the callback module's state from it. The <c>system_get_state/1</c>
+ function must return <c>{ok, State}</c> where <c>State</c> is the callback module's state.</p>
+ <p>If the callback module does not export a <c>system_get_state/1</c> function, <c>get_state/1,2</c>
+ assumes the <c>Misc</c> value is the callback module's state and returns it directly instead.</p>
+ <p>If the callback module's <c>system_get_state/1</c> function crashes or throws an exception, the
+ caller exits with error <c>{callback_failed, {Module, system_get_state}, {Class, Reason}}</c> where
+ <c>Module</c> is the name of the callback module and <c>Class</c> and <c>Reason</c> indicate
+ details of the exception.</p>
+ <p>The <c>system_get_state/1</c> function is primarily useful for user-defined
+ behaviours and modules that implement OTP <seealso marker="#special_process">special
+ processes</seealso>. The <c>gen_server</c>, <c>gen_fsm</c>, and <c>gen_event</c> OTP
+ behaviour modules export this function, and so callback modules for those behaviours
+ need not supply their own.</p>
<p>To obtain more information about a process, including its state, see
<seealso marker="#get_status-1">get_status/1</seealso> and
<seealso marker="#get_status-2">get_status/2</seealso>.</p>
@@ -289,6 +305,28 @@
function means that only the state of the particular event handler it was working on when it
failed or crashed is unchanged; it can still succeed in changing the states of other event
handlers registered in the same <c>gen_event</c> process.</p>
+ <p>If the callback module exports a <c>system_replace_state/2</c> function, it will be called in the
+ target process to replace its state using <c>StateFun</c>. Its two arguments are <c>StateFun</c>
+ and <c>Misc</c>, where <c>Misc</c> is the same as the <c>Misc</c> value returned by
+ <seealso marker="#get_status-1">get_status/1,2</seealso>. A <c>system_replace_state/2</c> function
+ is expected to return <c>{ok, NewState, NewMisc}</c> where <c>NewState</c> is the callback module's
+ new state obtained by calling <c>StateFun</c>, and <c>NewMisc</c> is a possibly new value used to
+ replace the original <c>Misc</c> (required since <c>Misc</c> often contains the callback
+ module's state within it).</p>
+ <p>If the callback module does not export a <c>system_replace_state/2</c> function,
+ <c>replace_state/2,3</c> assumes the <c>Misc</c> value is the callback module's state, passes it
+ to <c>StateFun</c> and uses the return value as both the new state and as the new value of
+ <c>Misc</c>.</p>
+ <p>If the callback module's <c>system_replace_state/2</c> function crashes or throws an exception,
+ the caller exits with error <c>{callback_failed, {Module, system_replace_state}, {Class, Reason}}</c>
+ where <c>Module</c> is the name of the callback module and <c>Class</c> and <c>Reason</c> indicate details
+ of the exception. If the callback module does not provide a <c>system_replace_state/2</c> function and
+ <c>StateFun</c> crashes or throws an exception, the caller exits with error
+ <c>{callback_failed, StateFun, {Class, Reason}}</c>.</p>
+ <p>The <c>system_replace_state/2</c> function is primarily useful for user-defined behaviours and
+ modules that implement OTP <seealso marker="#special_process">special processes</seealso>. The
+ <c>gen_server</c>, <c>gen_fsm</c>, and <c>gen_event</c> OTP behaviour modules export this function,
+ and so callback modules for those behaviours need not supply their own.</p>
</desc>
</func>
<func>
@@ -322,7 +360,7 @@
<section>
<title>Process Implementation Functions</title>
- <p>The following functions are used when implementing a
+ <p><marker id="special_process"/>The following functions are used when implementing a
special process. This is an ordinary process which does not use a
standard behaviour, but a process which understands the standard system messages.</p>
</section>
@@ -375,8 +413,9 @@
process continues the execution, or
<c><anno>Module</anno>:system_terminate(Reason, <anno>Parent</anno>, <anno>Debug</anno>, <anno>Misc</anno>)</c> if
the process should terminate. The <c><anno>Module</anno></c> must export
- <c>system_continue/3</c>, <c>system_terminate/4</c>, and
- <c>system_code_change/4</c> (see below).
+ <c>system_continue/3</c>, <c>system_terminate/4</c>,
+ <c>system_code_change/4</c>, <c>system_get_state/1</c> and
+ <c>system_replace_state/2</c> (see below).
</p>
<p>The <c><anno>Misc</anno></c> argument can be used to save internal data
in a process, for example its state. It is sent to
@@ -444,6 +483,34 @@
defined, the atom <c>undefined</c> is sent.</p>
</desc>
</func>
+ <func>
+ <name>Mod:system_get_state(Misc) -> {ok, State}</name>
+ <fsummary>Called when the process should return its current state</fsummary>
+ <type>
+ <v>Misc = term()</v>
+ <v>State = term()</v>
+ </type>
+ <desc>
+ <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
+ should return a term that reflects its current state. <c>State</c> is the
+ value returned by <c>sys:get_state/2</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>Mod:system_replace_state(StateFun, Misc) -> {ok, NState, NMisc}</name>
+ <fsummary>Called when the process should replace its current state</fsummary>
+ <type>
+ <v>StateFun = fun((State :: term()) -> NState)</v>
+ <v>Misc = term()</v>
+ <v>NState = term()</v>
+ <v>NMisc = term()</v>
+ </type>
+ <desc>
+ <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
+ should replace its current state. <c>NState</c> is the value returned by
+ <c>sys:replace_state/3</c>.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index ee7dd128f1..bebfbd4514 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,10 +41,10 @@
future.</p>
<p>The functionality described in EEP10 was implemented in Erlang/OTP
- as of R13A, but that was by no means the end of it. In R14B01 support
+ R13A, but that was by no means the end of it. In Erlang/OTP R14B01 support
for Unicode file names was added, although it was in no way complete
and was by default disabled on platforms where no guarantee was given
- for the file name encoding. With R16A came support for UTF-8 encoded
+ for the file name encoding. With Erlang/OTP R16A came support for UTF-8 encoded
source code, among with enhancements to many of the applications to
support both Unicode encoded file names as well as support for UTF-8
encoded files in several circumstances. Most notable is the support
@@ -52,8 +52,8 @@
for UTF-8 and more support for Unicode character sets in the
I/O-system.</p>
- <p>In 17.0, the encoding default for Erlang source files was
- switched to UTF-8 and in 18.0 Erlang will support atoms in the full
+ <p>In Erlang/OTP 17.0, the encoding default for Erlang source files was
+ switched to UTF-8 and in Erlang/OTP 18.0 Erlang will support atoms in the full
Unicode range, meaning full Unicode function and module
names</p>
@@ -222,7 +222,7 @@
<tag>Representation</tag>
<item>To handle Unicode characters in Erlang, we have to have a
common representation both in lists and binaries. The EEP (10) and
- the subsequent initial implementation in R13A settled a standard
+ the subsequent initial implementation in Erlang/OTP R13A settled a standard
representation of Unicode characters in Erlang.</item>
<tag>Manipulation</tag>
<item>The Unicode characters need to be processed by the Erlang
@@ -274,9 +274,9 @@
(<c>+fnu</c>) on platforms where this is not the default.</item>
<tag>Source code encoding</tag>
<item>When it comes to the Erlang source code, there is support
- for the UTF-8 encoding and bytewise encoding. The default in R16B
- is bytewise (or latin1) encoding. You can control the encoding by
- a comment like:
+ for the UTF-8 encoding and bytewise encoding. The default in
+ Erlang/OTP R16B was bytewise (or latin1) encoding; in Erlang/OTP 17.0
+ it was changed to UTF-8. You can control the encoding by a comment like:
<code>
%% -*- coding: utf-8 -*-
</code>
@@ -290,7 +290,7 @@
<item>Having the source code in UTF-8 also allows you to write
string literals containing Unicode characters with code points &gt;
255, although atoms, module names and function names will be
- restricted to the ISO-Latin-1 range until the 18.0 release. Binary
+ restricted to the ISO-Latin-1 range until the Erlang/OTP 18.0 release. Binary
literals where you use the <c>/utf8</c> type, can also be
expressed using Unicode characters &gt; 255. Having module names
using characters other than 7-bit ASCII can cause trouble on
@@ -304,7 +304,7 @@
<section>
<title>Standard Unicode Representation</title>
<p>In Erlang, strings are actually lists of integers. A string was
- up until R13 defined to be encoded in the ISO-latin-1 (ISO8859-1)
+ up until Erlang/OTP R13 defined to be encoded in the ISO-latin-1 (ISO8859-1)
character set, which is, code point by code point, a sub-range of
the Unicode character set.</p>
<p>The standard list encoding for strings was therefore easily
@@ -321,7 +321,7 @@
encoding has to be decided upon and the string should be converted
to a binary in the preferred encoding using
<c>unicode:characters_to_binary/{1,2,3}</c>. Strings are not
- generally lists of bytes, as they were before R13. They are lists of
+ generally lists of bytes, as they were before Erlang/OTP R13. They are lists of
characters. Characters are not generally bytes, they are Unicode
code points.</p>
@@ -447,8 +447,8 @@ Bin4 = &lt;&lt;"Hello"/utf16&gt;&gt;,</code>
probably will not appreciate). Another way is to keep it backwards
compatible so that only the ISO-Latin-1 character set is used to
detect a string. A third way would be to let the user decide
- exactly what Unicode ranges are to be viewed as characters. In
- R16B you can select either the whole Unicode range or the
+ exactly what Unicode ranges are to be viewed as characters. Since
+ Erlang/OTP R16B you can select either the whole Unicode range or the
ISO-Latin-1 range by supplying the startup flag <c>+pc
</c><i>Range</i>, where <i>Range</i> is either <c>latin1</c> or
<c>unicode</c>. For backwards compatibility, the default is
@@ -685,7 +685,7 @@ Eshell V5.10.1 (abort with ^G)
</item>
</taglist>
- <p>The Unicode file naming support was introduced with OTP release
+ <p>The Unicode file naming support was introduced with Erlang/OTP
R14B01. A VM operating in Unicode file name translation mode can
work with files having names in any language or character set (as
long as it is supported by the underlying OS and file system). The
@@ -709,7 +709,7 @@ Eshell V5.10.1 (abort with ^G)
problem even if it uses transparent file naming. Very few systems
have mixed file name encodings. A consistent UTF-8 named system will
work perfectly in Unicode file name mode. It was still however
- considered experimental in R14B01 and is still not the default on
+ considered experimental in Erlang/OTP R14B01 and is still not the default on
such systems. Unicode file name translation is turned on with the
<c>+fnu</c> switch to the On Linux, a VM started without explicitly
stating the file name translation mode will default to <c>latin1</c>
@@ -757,7 +757,7 @@ Eshell V5.10.1 (abort with ^G)
<title>Notes About Raw File Names</title>
<marker id="notes-about-raw-filenames"/>
<p>Raw file names were introduced together with Unicode file name
- support in erts-5.8.2 (OTP R14B01). The reason &quot;raw file
+ support in erts-5.8.2 (Erlang/OTP R14B01). The reason &quot;raw file
names&quot; was introduced in the system was to be able to
consistently represent file names given in different encodings on
the same system. Having the VM automatically translate a file name
@@ -798,10 +798,10 @@ Eshell V5.10.1 (abort with ^G)
the argument as a binary.</p>
<p>To force Unicode file name translation mode on systems where this
- is not the default was considered experimental in OTP R14B01 due to
+ is not the default was considered experimental in Erlang/OTP R14B01 due to
the fact that the initial implementation did not ignore wrongly
encoded file names, so that raw file names could spread unexpectedly
- throughout the system. Beginning with R16B, the wrongly encoded file
+ throughout the system. Beginning with Erlang/OTP R16B, the wrongly encoded file
names are only retrieved by special functions
(e.g. <c>file:list_dir_all/1</c>), so the impact on existing code is
much lower, why it is now supported. Unicode file name translation
@@ -848,6 +848,7 @@ Eshell V5.10.1 (abort with ^G)
</section>
<section>
<title>Unicode in Environment and Parameters</title>
+ <marker id="unicode_in_environment_and_parameters"/>
<p>Environment variables and their interpretation is handled much in
the same way as file names. If Unicode file names are enabled,
environment variables as well as parameters to the Erlang VM are
@@ -996,7 +997,8 @@ ok
</pre>
</section>
<section>
- <title><marker id="unicode_options_summary"/>Summary of Options</title>
+ <title>Summary of Options</title>
+ <marker id="unicode_options_summary"/>
<p>The Unicode support is controlled by both command line switches,
some standard environment variables and the version of OTP you are
using. Most options affect mainly the way Unicode data is displayed,
@@ -1032,7 +1034,7 @@ ok
<c>io</c>/<c>io_lib:format</c> with the <c>"~tp"</c> and
<c>~tP</c> formatting instructions, as described above.</p>
<p>You can check this option by calling io:printable_range/0,
- which in R16B will return <c>unicode</c> or <c>latin1</c>. To be
+ which will return <c>unicode</c> or <c>latin1</c>. To be
compatible with future (expected) extensions to the settings,
one should rather use <c>io_lib:printable_list/1</c> to check if
a list is printable according to the setting. That function will
@@ -1070,8 +1072,8 @@ ok
<item>
<p>This function returns the default encoding for Erlang source
files (if no encoding comment is present) in the currently
- running release. For R16 this returns <c>latin1</c> (meaning
- bytewise encoding). In 17.0 and forward it returns
+ running release. In Erlang/OTP R16B <c>latin1</c> was returned (meaning
+ bytewise encoding). In Erlang/OTP 17.0 and forward it returns
<c>utf8</c>.</p>
<p>The encoding of each file can be specified using comments as
described in
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index 66c21da193..48b376743d 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -123,6 +123,18 @@
</taglist>
</desc>
</datatype>
+ <datatype>
+ <name name="filename"/>
+ <p>The name of a zip file.</p>
+ </datatype>
+ <datatype><name name="extension"/></datatype>
+ <datatype><name name="extension_spec"/></datatype>
+ <datatype>
+ <name name="create_option"/>
+ <desc>
+ <p>These options are described in <seealso marker="#zip_options">create/3</seealso>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
@@ -160,6 +172,7 @@
set to <c>["gif", "jpg"]</c> and <c>uncompress</c> is set to
<c>["jpg"]</c>, only files with <c>"gif"</c> as extension will
be compressed. No other files will be compressed.</p>
+ <marker id="zip_options"></marker>
<p>The following options are available:</p>
<taglist>
<tag><c>cooked</c></tag>
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index 2f69e2b0a4..10d2ccea45 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -86,6 +86,8 @@
foldr/3, sparse_foldl/3, sparse_foldr/3, fix/1, relax/1, is_fix/1,
resize/1, resize/2]).
+-export_type([array/0, array/1]).
+
%%-define(TEST,1).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
@@ -144,18 +146,28 @@
%%--------------------------------------------------------------------------
+-type element_tuple(T) ::
+ {T, T, T, T, T, T, T, T, T, T}
+ | {element_tuple(T), element_tuple(T), element_tuple(T),
+ element_tuple(T), element_tuple(T), element_tuple(T),
+ element_tuple(T), element_tuple(T), element_tuple(T),
+ element_tuple(T), non_neg_integer()}.
+
+-type elements(T) :: non_neg_integer()
+ | element_tuple(T)
+ | nil(). % kill reference, for GC
+
-record(array, {size :: non_neg_integer(), %% number of defined entries
max :: non_neg_integer(), %% maximum number of entries
%% in current tree
default, %% the default value (usually 'undefined')
- elements %% the tuple tree
+ elements :: elements(_) %% the tuple tree
}).
-%% A declaration equivalent to the following one is hard-coded in erl_types.
-%% That declaration contains hard-coded information about the #array{}
-%% structure and the types of its fields. So, please make sure that any
-%% changes to its structure are also propagated to erl_types.erl.
-%%
-%% -opaque array() :: #array{}.
+
+-opaque array() :: array(term()).
+
+-opaque array(Type) ::
+ #array{default :: Type, elements :: elements(Type)}.
%%
%% Types
@@ -164,13 +176,13 @@
-type array_indx() :: non_neg_integer().
-type array_opt() :: {'fixed', boolean()} | 'fixed'
- | {'default', Value :: term()}
+ | {'default', Type :: term()}
| {'size', N :: non_neg_integer()}
| (N :: non_neg_integer()).
-type array_opts() :: array_opt() | [array_opt()].
--type indx_pair() :: {Index :: array_indx(), Value :: term()}.
--type indx_pairs() :: [indx_pair()].
+-type indx_pair(Type) :: {Index :: array_indx(), Type}.
+-type indx_pairs(Type) :: [indx_pair(Type)].
%%--------------------------------------------------------------------------
@@ -321,7 +333,7 @@ size(_) -> erlang:error(badarg).
%%
%% @see new/2
--spec default(Array :: array()) -> term().
+-spec default(Array :: array(Type)) -> Value :: Type.
default(#array{default = D}) -> D;
default(_) -> erlang:error(badarg).
@@ -404,7 +416,7 @@ new_test_() ->
%% automatically upon insertion; see also {@link set/3}.
%% @see relax/1
--spec fix(Array :: array()) -> array().
+-spec fix(Array :: array(Type)) -> array(Type).
fix(#array{}=A) ->
A#array{max = 0}.
@@ -452,7 +464,7 @@ fix_test_() ->
%% fix/1}.)
%% @see fix/1
--spec relax(Array :: array()) -> array().
+-spec relax(Array :: array(Type)) -> array(Type).
relax(#array{size = N}=A) ->
A#array{max = find_max(N-1, ?LEAFSIZE)}.
@@ -477,7 +489,8 @@ relax_test_() ->
%% integer, the call fails with reason `badarg'. If the given array has
%% fixed size, the resulting array will also have fixed size.
--spec resize(Size :: non_neg_integer(), Array :: array()) -> array().
+-spec resize(Size :: non_neg_integer(), Array :: array(Type)) ->
+ array(Type).
resize(Size, #array{size = N, max = M, elements = E}=A)
when is_integer(Size), Size >= 0 ->
@@ -508,7 +521,7 @@ resize(_Size, _) ->
%% @see resize/2
%% @see sparse_size/1
--spec resize(Array :: array()) -> array().
+-spec resize(Array :: array(Type)) -> array(Type).
resize(Array) ->
resize(sparse_size(Array), Array).
@@ -558,7 +571,7 @@ resize_test_() ->
%% @see get/2
%% @see reset/2
--spec set(I :: array_indx(), Value :: term(), Array :: array()) -> array().
+-spec set(I :: array_indx(), Value :: Type, Array :: array(Type)) -> array(Type).
set(I, Value, #array{size = N, max = M, default = D, elements = E}=A)
when is_integer(I), I >= 0 ->
@@ -621,7 +634,7 @@ expand(I, _S, X, D) ->
%% @see set/3
--spec get(I :: array_indx(), Array :: array()) -> term().
+-spec get(I :: array_indx(), Array :: array(Type)) -> Value :: Type.
get(I, #array{size = N, max = M, elements = E, default = D})
when is_integer(I), I >= 0 ->
@@ -661,7 +674,7 @@ get_1(I, E, _D) ->
%% TODO: a reset_range function
--spec reset(I :: array_indx(), Array :: array()) -> array().
+-spec reset(I :: array_indx(), Array :: array(Type)) -> array(Type).
reset(I, #array{size = N, max = M, default = D, elements = E}=A)
when is_integer(I), I >= 0 ->
@@ -747,7 +760,7 @@ set_get_test_() ->
%% @see from_list/2
%% @see sparse_to_list/1
--spec to_list(Array :: array()) -> list().
+-spec to_list(Array :: array(Type)) -> list(Value :: Type).
to_list(#array{size = 0}) ->
[];
@@ -820,7 +833,7 @@ to_list_test_() ->
%%
%% @see to_list/1
--spec sparse_to_list(Array :: array()) -> list().
+-spec sparse_to_list(Array :: array(Type)) -> list(Value :: Type).
sparse_to_list(#array{size = 0}) ->
[];
@@ -887,7 +900,7 @@ sparse_to_list_test_() ->
%% @equiv from_list(List, undefined)
--spec from_list(List :: list()) -> array().
+-spec from_list(List :: list(Value :: Type)) -> array(Type).
from_list(List) ->
from_list(List, undefined).
@@ -899,7 +912,7 @@ from_list(List) ->
%% @see new/2
%% @see to_list/1
--spec from_list(List :: list(), Default :: term()) -> array().
+-spec from_list(List :: list(Value :: Type), Default :: term()) -> array(Type).
from_list([], Default) ->
new({default,Default});
@@ -998,7 +1011,7 @@ from_list_test_() ->
%% @see from_orddict/2
%% @see sparse_to_orddict/1
--spec to_orddict(Array :: array()) -> indx_pairs().
+-spec to_orddict(Array :: array(Type)) -> indx_pairs(Value :: Type).
to_orddict(#array{size = 0}) ->
[];
@@ -1035,16 +1048,16 @@ to_orddict_3(N, R, D, L, E, S) ->
to_orddict_2(element(N, E), R, D, L),
E, S).
--spec push_pairs(non_neg_integer(), array_indx(), term(), indx_pairs()) ->
- indx_pairs().
+-spec push_pairs(non_neg_integer(), array_indx(), term(), indx_pairs(Type)) ->
+ indx_pairs(Type).
push_pairs(0, _I, _E, L) ->
L;
push_pairs(N, I, E, L) ->
push_pairs(N-1, I-1, E, [{I, E} | L]).
--spec push_tuple_pairs(non_neg_integer(), array_indx(), term(), indx_pairs()) ->
- indx_pairs().
+-spec push_tuple_pairs(non_neg_integer(), array_indx(), term(), indx_pairs(Type)) ->
+ indx_pairs(Type).
push_tuple_pairs(0, _I, _T, L) ->
L;
@@ -1090,7 +1103,7 @@ to_orddict_test_() ->
%%
%% @see to_orddict/1
--spec sparse_to_orddict(Array :: array()) -> indx_pairs().
+-spec sparse_to_orddict(Array :: array(Type)) -> indx_pairs(Value :: Type).
sparse_to_orddict(#array{size = 0}) ->
[];
@@ -1128,7 +1141,7 @@ sparse_to_orddict_3(N, R, D, L, E, S) ->
E, S).
-spec sparse_push_tuple_pairs(non_neg_integer(), array_indx(),
- _, _, indx_pairs()) -> indx_pairs().
+ _, _, indx_pairs(Type)) -> indx_pairs(Type).
sparse_push_tuple_pairs(0, _I, _D, _T, L) ->
L;
@@ -1170,7 +1183,7 @@ sparse_to_orddict_test_() ->
%% @equiv from_orddict(Orddict, undefined)
--spec from_orddict(Orddict :: indx_pairs()) -> array().
+-spec from_orddict(Orddict :: indx_pairs(Value :: Type)) -> array(Type).
from_orddict(Orddict) ->
from_orddict(Orddict, undefined).
@@ -1184,7 +1197,8 @@ from_orddict(Orddict) ->
%% @see new/2
%% @see to_orddict/1
--spec from_orddict(Orddict :: indx_pairs(), Default :: term()) -> array().
+-spec from_orddict(Orddict :: indx_pairs(Value :: Type), Default :: Type) ->
+ array(Type).
from_orddict([], Default) ->
new({default,Default});
@@ -1379,8 +1393,8 @@ from_orddict_test_() ->
%% @see foldr/3
%% @see sparse_map/2
--spec map(Function, Array :: array()) -> array() when
- Function :: fun((Index :: array_indx(), Value :: _) -> _).
+-spec map(Function, Array :: array(Type1)) -> array(Type2) when
+ Function :: fun((Index :: array_indx(), Type1) -> Type2).
map(Function, Array=#array{size = N, elements = E, default = D})
when is_function(Function, 2) ->
@@ -1471,8 +1485,8 @@ map_test_() ->
%%
%% @see map/2
--spec sparse_map(Function, Array :: array()) -> array() when
- Function :: fun((Index :: array_indx(), Value :: _) -> _).
+-spec sparse_map(Function, Array :: array(Type1)) -> array(Type2) when
+ Function :: fun((Index :: array_indx(), Type1) -> Type2).
sparse_map(Function, Array=#array{size = N, elements = E, default = D})
when is_function(Function, 2) ->
@@ -1567,8 +1581,8 @@ sparse_map_test_() ->
%% @see map/2
%% @see sparse_foldl/3
--spec foldl(Function, InitialAcc :: A, Array :: array()) -> B when
- Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B).
+-spec foldl(Function, InitialAcc :: A, Array :: array(Type)) -> B when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
foldl(Function, A, #array{size = N, elements = E, default = D})
when is_function(Function, 3) ->
@@ -1640,8 +1654,8 @@ foldl_test_() ->
%% @see foldl/3
%% @see sparse_foldr/3
--spec sparse_foldl(Function, InitialAcc :: A, Array :: array()) -> B when
- Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B).
+-spec sparse_foldl(Function, InitialAcc :: A, Array :: array(Type)) -> B when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
sparse_foldl(Function, A, #array{size = N, elements = E, default = D})
when is_function(Function, 3) ->
@@ -1717,8 +1731,8 @@ sparse_foldl_test_() ->
%% @see foldl/3
%% @see map/2
--spec foldr(Function, InitialAcc :: A, Array :: array()) -> B when
- Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B).
+-spec foldr(Function, InitialAcc :: A, Array :: array(Type)) -> B when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
foldr(Function, A, #array{size = N, elements = E, default = D})
when is_function(Function, 3) ->
@@ -1796,8 +1810,8 @@ foldr_test_() ->
%% @see foldr/3
%% @see sparse_foldl/3
--spec sparse_foldr(Function, InitialAcc :: A, Array :: array()) -> B when
- Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B).
+-spec sparse_foldr(Function, InitialAcc :: A, Array :: array(Type)) -> B when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
sparse_foldr(Function, A, #array{size = N, elements = E, default = D})
when is_function(Function, 3) ->
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 44dad04f43..c32da1624f 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -1785,6 +1785,7 @@ read_file_header(FileName, Access, RamFile) ->
Version =:= 9 ->
dets_v9:read_file_header(Fd, FileName);
true ->
+ _ = file:close(Fd),
throw({error, {not_a_dets_file, FileName}})
end.
@@ -2113,6 +2114,8 @@ test_bchunk_format(Head, Term) ->
do_open_file([Fname, Verbose], Parent, Server, Ref) ->
case catch fopen2(Fname, Ref) of
+ {error, {tooshort, _}} ->
+ err({error, {not_a_dets_file, Fname}});
{error, _Reason} = Error ->
err(Error);
{ok, Head} ->
@@ -2126,11 +2129,10 @@ do_open_file([Fname, Verbose], Parent, Server, Ref) ->
[Bad]),
{error, {dets_bug, Fname, Bad}}
end;
-do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref) ->
+do_open_file([Tab, OpenArgs, Verb], Parent, Server, _Ref) ->
case catch fopen3(Tab, OpenArgs) of
{error, {tooshort, _}} ->
- _ = file:delete(OpenArgs#open_args.file),
- do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref);
+ err({error, {not_a_dets_file, OpenArgs#open_args.file}});
{error, _Reason} = Error ->
err(Error);
{ok, Head} ->
@@ -2486,7 +2488,6 @@ fopen2(Fname, Tab) ->
{ok, _} ->
Acc = read_write,
Ram = false,
- %% Fd is not always closed upon error, but exit is soon called.
{ok, Fd, FH} = read_file_header(Fname, Acc, Ram),
Mod = FH#fileheader.mod,
Do = case Mod:check_file_header(FH, Fd) of
@@ -2542,7 +2543,6 @@ fopen_existing_file(Tab, OpenArgs) ->
ram_file = Ram, delayed_write = CacheSz, auto_save =
Auto, access = Acc, version = Version, debug = Debug} =
OpenArgs,
- %% Fd is not always closed upon error, but exit is soon called.
{ok, Fd, FH} = read_file_header(Fname, Acc, Ram),
V9 = (Version =:= 9) or (Version =:= default),
MinF = (MinSlots =:= default) or (MinSlots =:= FH#fileheader.min_no_slots),
diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl
index 7e198a2469..cf8fb3114a 100644
--- a/lib/stdlib/src/dict.erl
+++ b/lib/stdlib/src/dict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. 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
@@ -41,6 +41,8 @@
-export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]).
-export([fold/3,map/2,filter/2,merge/3]).
+-export_type([dict/0, dict/2]).
+
%% Low-level interface.
%%-export([get_slot/2,get_bucket/2,on_bucket/3,fold_dict/3,
%% maybe_expand/2,maybe_contract/2]).
@@ -53,6 +55,8 @@
-define(exp_size, (?seg_size * ?expand_load)).
-define(con_size, (?seg_size * ?contract_load)).
+-type segs(_Key, _Value) :: tuple().
+
%% Define a hashtable. The default values are the standard ones.
-record(dict,
{size=0 :: non_neg_integer(), % Number of elements
@@ -62,14 +66,13 @@
exp_size=?exp_size :: non_neg_integer(), % Size to expand at
con_size=?con_size :: non_neg_integer(), % Size to contract at
empty :: tuple(), % Empty segment
- segs :: tuple() % Segments
+ segs :: segs(_, _) % Segments
}).
-%% A declaration equivalent to the following one is hard-coded in erl_types.
-%% That declaration contains hard-coded information about the #dict{}
-%% structure and the types of its fields. So, please make sure that any
-%% changes to its structure are also propagated to erl_types.erl.
-%%
-%% -opaque dict() :: #dict{}.
+
+
+-opaque dict() :: dict(_, _).
+
+-opaque dict(Key, Value) :: #dict{segs :: segs(Key, Value)}.
-define(kv(K,V), [K|V]). % Key-Value pair format
%%-define(kv(K,V), {K,V}). % Key-Value pair format
@@ -81,8 +84,7 @@ new() ->
#dict{empty=Empty,segs={Empty}}.
-spec is_key(Key, Dict) -> boolean() when
- Key :: term(),
- Dict :: dict().
+ Dict :: dict(Key, Value :: term()).
is_key(Key, D) ->
Slot = get_slot(D, Key),
@@ -94,15 +96,15 @@ find_key(K, [_|Bkt]) -> find_key(K, Bkt);
find_key(_, []) -> false.
-spec to_list(Dict) -> List when
- Dict :: dict(),
- List :: [{Key :: term(), Value :: term()}].
+ Dict :: dict(Key, Value),
+ List :: [{Key, Value}].
to_list(D) ->
fold(fun (Key, Val, List) -> [{Key,Val}|List] end, [], D).
-spec from_list(List) -> Dict when
- List :: [{Key :: term(), Value :: term()}],
- Dict :: dict().
+ Dict :: dict(Key, Value),
+ List :: [{Key, Value}].
from_list(L) ->
lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), L).
@@ -118,9 +120,7 @@ size(#dict{size=N}) when is_integer(N), N >= 0 -> N.
is_empty(#dict{size=N}) -> N =:= 0.
-spec fetch(Key, Dict) -> Value when
- Key :: term(),
- Dict :: dict(),
- Value :: term().
+ Dict :: dict(Key, Value).
fetch(Key, D) ->
Slot = get_slot(D, Key),
@@ -135,9 +135,7 @@ fetch_val(K, [_|Bkt]) -> fetch_val(K, Bkt);
fetch_val(_, []) -> throw(badarg).
-spec find(Key, Dict) -> {'ok', Value} | 'error' when
- Key :: term(),
- Dict :: dict(),
- Value :: term().
+ Dict :: dict(Key, Value).
find(Key, D) ->
Slot = get_slot(D, Key),
@@ -149,16 +147,16 @@ find_val(K, [_|Bkt]) -> find_val(K, Bkt);
find_val(_, []) -> error.
-spec fetch_keys(Dict) -> Keys when
- Dict :: dict(),
- Keys :: [term()].
+ Dict :: dict(Key, Value :: term()),
+ Keys :: [Key].
fetch_keys(D) ->
fold(fun (Key, _Val, Keys) -> [Key|Keys] end, [], D).
-spec erase(Key, Dict1) -> Dict2 when
- Key :: term(),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value).
+
%% Erase all elements with key Key.
erase(Key, D0) ->
@@ -174,10 +172,8 @@ erase_key(Key, [E|Bkt0]) ->
erase_key(_, []) -> {[],0}.
-spec store(Key, Value, Dict1) -> Dict2 when
- Key :: term(),
- Value :: term(),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value).
store(Key, Val, D0) ->
Slot = get_slot(D0, Key),
@@ -194,10 +190,8 @@ store_bkt_val(Key, New, [Other|Bkt0]) ->
store_bkt_val(Key, New, []) -> {[?kv(Key,New)],1}.
-spec append(Key, Value, Dict1) -> Dict2 when
- Key :: term(),
- Value :: term(),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value).
append(Key, Val, D0) ->
Slot = get_slot(D0, Key),
@@ -214,10 +208,9 @@ append_bkt(Key, Val, [Other|Bkt0]) ->
append_bkt(Key, Val, []) -> {[?kv(Key,[Val])],1}.
-spec append_list(Key, ValList, Dict1) -> Dict2 when
- Key :: term(),
- ValList :: [Value :: term()],
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value),
+ ValList :: [Value].
append_list(Key, L, D0) ->
Slot = get_slot(D0, Key),
@@ -288,10 +281,9 @@ app_list_bkt(Key, L, []) -> {[?kv(Key,L)],1}.
%% {[Other|Bkt1],Dc}.
-spec update(Key, Fun, Dict1) -> Dict2 when
- Key :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value),
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value).
update(Key, F, D0) ->
Slot = get_slot(D0, Key),
@@ -311,11 +303,10 @@ update_bkt(_Key, _F, []) ->
throw(badarg).
-spec update(Key, Fun, Initial, Dict1) -> Dict2 when
- Key :: term(),
- Initial :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value),
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Initial :: Value.
update(Key, F, Init, D0) ->
Slot = get_slot(D0, Key),
@@ -331,10 +322,9 @@ update_bkt(Key, F, I, [Other|Bkt0]) ->
update_bkt(Key, F, I, []) when is_function(F, 1) -> {[?kv(Key,I)],1}.
-spec update_counter(Key, Increment, Dict1) -> Dict2 when
- Key :: term(),
- Increment :: number(),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value),
+ Increment :: number().
update_counter(Key, Incr, D0) when is_number(Incr) ->
Slot = get_slot(D0, Key),
@@ -351,36 +341,35 @@ counter_bkt(Key, I, []) -> {[?kv(Key,I)],1}.
-spec fold(Fun, Acc0, Dict) -> Acc1 when
Fun :: fun((Key, Value, AccIn) -> AccOut),
- Key :: term(),
- Value :: term(),
- Acc0 :: term(),
- Acc1 :: term(),
- AccIn :: term(),
- AccOut :: term(),
- Dict :: dict().
+ Dict :: dict(Key, Value),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc.
+
%% Fold function Fun over all "bags" in Table and return Accumulator.
fold(F, Acc, D) -> fold_dict(F, Acc, D).
-spec map(Fun, Dict1) -> Dict2 when
- Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Fun :: fun((Key, Value1) -> Value2),
+ Dict1 :: dict(Key, Value1),
+ Dict2 :: dict(Key, Value2).
map(F, D) -> map_dict(F, D).
-spec filter(Pred, Dict1) -> Dict2 when
- Pred :: fun((Key :: term(), Value :: term()) -> boolean()),
- Dict1 :: dict(),
- Dict2 :: dict().
+ Pred :: fun((Key , Value) -> boolean()),
+ Dict1 :: dict(Key, Value),
+ Dict2 :: dict(Key, Value).
filter(F, D) -> filter_dict(F, D).
-spec merge(Fun, Dict1, Dict2) -> Dict3 when
- Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()),
- Dict1 :: dict(),
- Dict2 :: dict(),
- Dict3 :: dict().
+ Fun :: fun((Key, Value1, Value2) -> Value),
+ Dict1 :: dict(Key, Value1),
+ Dict2 :: dict(Key, Value2),
+ Dict3 :: dict(Key, Value).
merge(F, D1, D2) when D1#dict.size < D2#dict.size ->
fold_dict(fun (K, V1, D) ->
diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl
index 78f74631dc..0c21271529 100644
--- a/lib/stdlib/src/digraph.erl
+++ b/lib/stdlib/src/digraph.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -36,18 +36,14 @@
-export([get_short_path/3, get_short_cycle/2]).
--export_type([digraph/0, d_type/0, vertex/0, edge/0]).
+-export_type([graph/0, d_type/0, vertex/0, edge/0]).
-record(digraph, {vtab = notable :: ets:tab(),
etab = notable :: ets:tab(),
ntab = notable :: ets:tab(),
cyclic = true :: boolean()}).
-%% A declaration equivalent to the following one is hard-coded in erl_types.
-%% That declaration contains hard-coded information about the #digraph{}
-%% record and the types of its fields. So, please make sure that any
-%% changes to its structure are also propagated to erl_types.erl.
-%%
-%% -opaque digraph() :: #digraph{}.
+
+-opaque graph() :: #digraph{}.
-type edge() :: term().
-type label() :: term().
@@ -67,11 +63,11 @@
-type d_cyclicity() :: 'acyclic' | 'cyclic'.
-type d_type() :: d_cyclicity() | d_protection().
--spec new() -> digraph().
+-spec new() -> graph().
new() -> new([]).
--spec new(Type) -> digraph() when
+-spec new(Type) -> graph() when
Type :: [d_type()].
new(Type) ->
@@ -106,7 +102,7 @@ check_type(_, _, _) -> error.
%%
%% Set graph type
%%
--spec set_type([{'cyclic', boolean()}], digraph()) -> digraph().
+-spec set_type([{'cyclic', boolean()}], graph()) -> graph().
set_type([{cyclic,V} | Ks], G) ->
set_type(Ks, G#digraph{cyclic = V});
@@ -116,7 +112,7 @@ set_type([], G) -> G.
%% Data access functions
-spec delete(G) -> 'true' when
- G :: digraph().
+ G :: graph().
delete(G) ->
ets:delete(G#digraph.vtab),
@@ -124,7 +120,7 @@ delete(G) ->
ets:delete(G#digraph.ntab).
-spec info(G) -> InfoList when
- G :: digraph(),
+ G :: graph(),
InfoList :: [{'cyclicity', Cyclicity :: d_cyclicity()} |
{'memory', NoWords :: non_neg_integer()} |
{'protection', Protection :: d_protection()}].
@@ -142,20 +138,20 @@ info(G) ->
[{cyclicity, Cyclicity}, {memory, Memory}, {protection, Protection}].
-spec add_vertex(G) -> vertex() when
- G :: digraph().
+ G :: graph().
add_vertex(G) ->
do_add_vertex({new_vertex_id(G), []}, G).
-spec add_vertex(G, V) -> vertex() when
- G :: digraph(),
+ G :: graph(),
V :: vertex().
add_vertex(G, V) ->
do_add_vertex({V, []}, G).
-spec add_vertex(G, V, Label) -> vertex() when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Label :: label().
@@ -163,21 +159,21 @@ add_vertex(G, V, D) ->
do_add_vertex({V, D}, G).
-spec del_vertex(G, V) -> 'true' when
- G :: digraph(),
+ G :: graph(),
V :: vertex().
del_vertex(G, V) ->
do_del_vertex(V, G).
-spec del_vertices(G, Vertices) -> 'true' when
- G :: digraph(),
+ G :: graph(),
Vertices :: [vertex()].
del_vertices(G, Vs) ->
do_del_vertices(Vs, G).
-spec vertex(G, V) -> {V, Label} | 'false' when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Label :: label().
@@ -188,37 +184,37 @@ vertex(G, V) ->
end.
-spec no_vertices(G) -> non_neg_integer() when
- G :: digraph().
+ G :: graph().
no_vertices(G) ->
ets:info(G#digraph.vtab, size).
-spec vertices(G) -> Vertices when
- G :: digraph(),
+ G :: graph(),
Vertices :: [vertex()].
vertices(G) ->
ets:select(G#digraph.vtab, [{{'$1', '_'}, [], ['$1']}]).
--spec source_vertices(digraph()) -> [vertex()].
+-spec source_vertices(graph()) -> [vertex()].
source_vertices(G) ->
collect_vertices(G, in).
--spec sink_vertices(digraph()) -> [vertex()].
+-spec sink_vertices(graph()) -> [vertex()].
sink_vertices(G) ->
collect_vertices(G, out).
-spec in_degree(G, V) -> non_neg_integer() when
- G :: digraph(),
+ G :: graph(),
V :: vertex().
in_degree(G, V) ->
length(ets:lookup(G#digraph.ntab, {in, V})).
-spec in_neighbours(G, V) -> Vertex when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Vertex :: [vertex()].
@@ -228,7 +224,7 @@ in_neighbours(G, V) ->
collect_elems(ets:lookup(NT, {in, V}), ET, 2).
-spec in_edges(G, V) -> Edges when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Edges :: [edge()].
@@ -236,14 +232,14 @@ in_edges(G, V) ->
ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]).
-spec out_degree(G, V) -> non_neg_integer() when
- G :: digraph(),
+ G :: graph(),
V :: vertex().
out_degree(G, V) ->
length(ets:lookup(G#digraph.ntab, {out, V})).
-spec out_neighbours(G, V) -> Vertices when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Vertices :: [vertex()].
@@ -253,7 +249,7 @@ out_neighbours(G, V) ->
collect_elems(ets:lookup(NT, {out, V}), ET, 3).
-spec out_edges(G, V) -> Edges when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Edges :: [edge()].
@@ -261,7 +257,7 @@ out_edges(G, V) ->
ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]).
-spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when
- G :: digraph(),
+ G :: graph(),
V1 :: vertex(),
V2 :: vertex().
@@ -269,7 +265,7 @@ add_edge(G, V1, V2) ->
do_add_edge({new_edge_id(G), V1, V2, []}, G).
-spec add_edge(G, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when
- G :: digraph(),
+ G :: graph(),
V1 :: vertex(),
V2 :: vertex(),
Label :: label().
@@ -278,7 +274,7 @@ add_edge(G, V1, V2, D) ->
do_add_edge({new_edge_id(G), V1, V2, D}, G).
-spec add_edge(G, E, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when
- G :: digraph(),
+ G :: graph(),
E :: edge(),
V1 :: vertex(),
V2 :: vertex(),
@@ -288,34 +284,34 @@ add_edge(G, E, V1, V2, D) ->
do_add_edge({E, V1, V2, D}, G).
-spec del_edge(G, E) -> 'true' when
- G :: digraph(),
+ G :: graph(),
E :: edge().
del_edge(G, E) ->
do_del_edges([E], G).
-spec del_edges(G, Edges) -> 'true' when
- G :: digraph(),
+ G :: graph(),
Edges :: [edge()].
del_edges(G, Es) ->
do_del_edges(Es, G).
-spec no_edges(G) -> non_neg_integer() when
- G :: digraph().
+ G :: graph().
no_edges(G) ->
ets:info(G#digraph.etab, size).
-spec edges(G) -> Edges when
- G :: digraph(),
+ G :: graph(),
Edges :: [edge()].
edges(G) ->
ets:select(G#digraph.etab, [{{'$1', '_', '_', '_'}, [], ['$1']}]).
-spec edges(G, V) -> Edges when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Edges :: [edge()].
@@ -324,7 +320,7 @@ edges(G, V) ->
{{{in, V}, '$1'}, [], ['$1']}]).
-spec edge(G, E) -> {E, V1, V2, Label} | 'false' when
- G :: digraph(),
+ G :: graph(),
E :: edge(),
V1 :: vertex(),
V2 :: vertex(),
@@ -339,7 +335,7 @@ edge(G, E) ->
%%
%% Generate a "unique" edge identifier (relative to this graph)
%%
--spec new_edge_id(digraph()) -> edge().
+-spec new_edge_id(graph()) -> edge().
new_edge_id(G) ->
NT = G#digraph.ntab,
@@ -351,7 +347,7 @@ new_edge_id(G) ->
%%
%% Generate a "unique" vertex identifier (relative to this graph)
%%
--spec new_vertex_id(digraph()) -> vertex().
+-spec new_vertex_id(graph()) -> vertex().
new_vertex_id(G) ->
NT = G#digraph.ntab,
@@ -371,7 +367,7 @@ collect_elems([{_,Key}|Keys], Table, Index, Acc) ->
[ets:lookup_element(Table, Key, Index)|Acc]);
collect_elems([], _, _, Acc) -> Acc.
--spec do_add_vertex({vertex(), label()}, digraph()) -> vertex().
+-spec do_add_vertex({vertex(), label()}, graph()) -> vertex().
do_add_vertex({V, _Label} = VL, G) ->
ets:insert(G#digraph.vtab, VL),
@@ -430,14 +426,14 @@ do_del_edge(E, V1, V2, G) ->
{{{out,V1}, E}, [], [true]}]),
ets:delete(G#digraph.etab, E).
--spec rm_edges([vertex(),...], digraph()) -> 'true'.
+-spec rm_edges([vertex(),...], graph()) -> 'true'.
rm_edges([V1, V2|Vs], G) ->
rm_edge(V1, V2, G),
rm_edges([V2|Vs], G);
rm_edges(_, _) -> true.
--spec rm_edge(vertex(), vertex(), digraph()) -> 'ok'.
+-spec rm_edge(vertex(), vertex(), graph()) -> 'ok'.
rm_edge(V1, V2, G) ->
Es = out_edges(G, V1),
@@ -456,7 +452,7 @@ rm_edge_0([], _, _, #digraph{}) -> ok.
%%
%% Check that endpoints exist
%%
--spec do_add_edge({edge(), vertex(), vertex(), label()}, digraph()) ->
+-spec do_add_edge({edge(), vertex(), vertex(), label()}, graph()) ->
edge() | {'error', add_edge_err_rsn()}.
do_add_edge({E, V1, V2, Label}, G) ->
@@ -484,14 +480,14 @@ other_edge_exists(#digraph{etab = ET}, E, V1, V2) ->
false
end.
--spec do_insert_edge(edge(), vertex(), vertex(), label(), digraph()) -> edge().
+-spec do_insert_edge(edge(), vertex(), vertex(), label(), graph()) -> edge().
do_insert_edge(E, V1, V2, Label, #digraph{ntab=NT, etab=ET}) ->
ets:insert(NT, [{{out, V1}, E}, {{in, V2}, E}]),
ets:insert(ET, {E, V1, V2, Label}),
E.
--spec acyclic_add_edge(edge(), vertex(), vertex(), label(), digraph()) ->
+-spec acyclic_add_edge(edge(), vertex(), vertex(), label(), graph()) ->
edge() | {'error', {'bad_edge', [vertex()]}}.
acyclic_add_edge(_E, V1, V2, _L, _G) when V1 =:= V2 ->
@@ -507,7 +503,7 @@ acyclic_add_edge(E, V1, V2, Label, G) ->
%%
-spec del_path(G, V1, V2) -> 'true' when
- G :: digraph(),
+ G :: graph(),
V1 :: vertex(),
V2 :: vertex().
@@ -529,7 +525,7 @@ del_path(G, V1, V2) ->
%%
-spec get_cycle(G, V) -> Vertices | 'false' when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Vertices :: [vertex(),...].
@@ -550,7 +546,7 @@ get_cycle(G, V) ->
%%
-spec get_path(G, V1, V2) -> Vertices | 'false' when
- G :: digraph(),
+ G :: graph(),
V1 :: vertex(),
V2 :: vertex(),
Vertices :: [vertex(),...].
@@ -589,7 +585,7 @@ one_path([], _, [], _, _, _, _, _Counter) -> false.
%%
-spec get_short_cycle(G, V) -> Vertices | 'false' when
- G :: digraph(),
+ G :: graph(),
V :: vertex(),
Vertices :: [vertex(),...].
@@ -602,7 +598,7 @@ get_short_cycle(G, V) ->
%%
-spec get_short_path(G, V1, V2) -> Vertices | 'false' when
- G :: digraph(),
+ G :: graph(),
V1 :: vertex(),
V2 :: vertex(),
Vertices :: [vertex(),...].
diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl
index 0e248df453..011bcd0260 100644
--- a/lib/stdlib/src/digraph_utils.erl
+++ b/lib/stdlib/src/digraph_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -43,28 +43,28 @@
%%
-spec components(Digraph) -> [Component] when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Component :: [digraph:vertex()].
components(G) ->
forest(G, fun inout/3).
-spec strong_components(Digraph) -> [StrongComponent] when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
StrongComponent :: [digraph:vertex()].
strong_components(G) ->
forest(G, fun in/3, revpostorder(G)).
-spec cyclic_strong_components(Digraph) -> [StrongComponent] when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
StrongComponent :: [digraph:vertex()].
cyclic_strong_components(G) ->
remove_singletons(strong_components(G), G, []).
-spec reachable(Vertices, Digraph) -> Reachable when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()],
Reachable :: [digraph:vertex()].
@@ -72,7 +72,7 @@ reachable(Vs, G) when is_list(Vs) ->
lists:append(forest(G, fun out/3, Vs, first)).
-spec reachable_neighbours(Vertices, Digraph) -> Reachable when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()],
Reachable :: [digraph:vertex()].
@@ -80,7 +80,7 @@ reachable_neighbours(Vs, G) when is_list(Vs) ->
lists:append(forest(G, fun out/3, Vs, not_first)).
-spec reaching(Vertices, Digraph) -> Reaching when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()],
Reaching :: [digraph:vertex()].
@@ -88,7 +88,7 @@ reaching(Vs, G) when is_list(Vs) ->
lists:append(forest(G, fun in/3, Vs, first)).
-spec reaching_neighbours(Vertices, Digraph) -> Reaching when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()],
Reaching :: [digraph:vertex()].
@@ -96,7 +96,7 @@ reaching_neighbours(Vs, G) when is_list(Vs) ->
lists:append(forest(G, fun in/3, Vs, not_first)).
-spec topsort(Digraph) -> Vertices | 'false' when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()].
topsort(G) ->
@@ -107,13 +107,13 @@ topsort(G) ->
end.
-spec is_acyclic(Digraph) -> boolean() when
- Digraph :: digraph().
+ Digraph :: digraph:graph().
is_acyclic(G) ->
loop_vertices(G) =:= [] andalso topsort(G) =/= false.
-spec arborescence_root(Digraph) -> 'no' | {'yes', Root} when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Root :: digraph:vertex().
arborescence_root(G) ->
@@ -136,29 +136,29 @@ arborescence_root(G) ->
end.
-spec is_arborescence(Digraph) -> boolean() when
- Digraph :: digraph().
+ Digraph :: digraph:graph().
is_arborescence(G) ->
arborescence_root(G) =/= no.
-spec is_tree(Digraph) -> boolean() when
- Digraph :: digraph().
+ Digraph :: digraph:graph().
is_tree(G) ->
(digraph:no_edges(G) =:= digraph:no_vertices(G) - 1)
andalso (length(components(G)) =:= 1).
-spec loop_vertices(Digraph) -> Vertices when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()].
loop_vertices(G) ->
[V || V <- digraph:vertices(G), is_reflexive_vertex(V, G)].
-spec subgraph(Digraph, Vertices) -> SubGraph when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()],
- SubGraph :: digraph().
+ SubGraph :: digraph:graph().
subgraph(G, Vs) ->
try
@@ -169,8 +169,8 @@ subgraph(G, Vs) ->
end.
-spec subgraph(Digraph, Vertices, Options) -> SubGraph when
- Digraph :: digraph(),
- SubGraph :: digraph(),
+ Digraph :: digraph:graph(),
+ SubGraph :: digraph:graph(),
Vertices :: [digraph:vertex()],
Options :: [{'type', SubgraphType} | {'keep_labels', boolean()}],
SubgraphType :: 'inherit' | [digraph:d_type()].
@@ -184,8 +184,8 @@ subgraph(G, Vs, Opts) ->
end.
-spec condensation(Digraph) -> CondensedDigraph when
- Digraph :: digraph(),
- CondensedDigraph :: digraph().
+ Digraph :: digraph:graph(),
+ CondensedDigraph :: digraph:graph().
condensation(G) ->
SCs = strong_components(G),
@@ -209,14 +209,14 @@ condensation(G) ->
SCG.
-spec preorder(Digraph) -> Vertices when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()].
preorder(G) ->
lists:reverse(revpreorder(G)).
-spec postorder(Digraph) -> Vertices when
- Digraph :: digraph(),
+ Digraph :: digraph:graph(),
Vertices :: [digraph:vertex()].
postorder(G) ->
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index 516c0aa30b..a2b4663219 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -73,7 +73,7 @@ to_atom(Str) ->
error
end.
-match(Prefix, Alts, Extra) ->
+match(Prefix, Alts, Extra0) ->
Len = length(Prefix),
Matches = lists:sort(
[{S, A} || {H, A} <- Alts,
@@ -89,7 +89,11 @@ match(Prefix, Alts, Extra) ->
{yes, Remain, []}
end;
{complete, Str} ->
- {yes, nthtail(Len, Str) ++ Extra, []};
+ Extra = case {Extra0,Matches} of
+ {"(",[{Str,0}]} -> "()";
+ {_,_} -> Extra0
+ end,
+ {yes, nthtail(Len, Str) ++ Extra, []};
no ->
{no, [], []}
end.
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 4fd302e612..9b506b0a44 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -20,12 +20,12 @@
%% An Erlang code preprocessor.
--export([open/2,open/3,open/5,close/1,format_error/1]).
+-export([open/1, open/2,open/3,open/5,close/1,format_error/1]).
-export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]).
--export([parse_file/1, parse_file/3]).
+-export([parse_file/1, parse_file/2, parse_file/3]).
-export([default_encoding/0, encoding_to_string/1,
read_encoding_from_binary/1, read_encoding_from_binary/2,
- set_encoding/1, read_encoding/1, read_encoding/2]).
+ set_encoding/1, set_encoding/2, read_encoding/1, read_encoding/2]).
-export([interpret_file_attribute/1]).
-export([normalize_typed_record_fields/1,restore_typed_record_fields/1]).
@@ -33,21 +33,34 @@
-export_type([source_encoding/0]).
--type macros() :: [{atom(), term()}].
+-type macros() :: [atom() | {atom(), term()}].
-type epp_handle() :: pid().
-type source_encoding() :: latin1 | utf8.
+-type ifdef() :: 'ifdef' | 'ifndef' | 'else'.
+
+-type name() :: {'atom', atom()}.
+-type argspec() :: 'none' %No arguments
+ | non_neg_integer(). %Number of arguments
+-type tokens() :: [erl_scan:token()].
+-type used() :: {name(), argspec()}.
+
+-define(DEFAULT_ENCODING, utf8).
+
%% Epp state record.
--record(epp, {file, %Current file
- location, %Current location
- delta, %Offset from Location (-file)
- name="", %Current file name
- name2="", %-"-, modified by -file
- istk=[], %Ifdef stack
- sstk=[], %State stack
- path=[], %Include-path
- macs = dict:new() :: dict(), %Macros (don't care locations)
- uses = dict:new() :: dict(), %Macro use structure
+-record(epp, {file :: file:io_device(), %Current file
+ location=1, %Current location
+ delta=0 :: non_neg_integer(), %Offset from Location (-file)
+ name="" :: file:name(), %Current file name
+ name2="" :: file:name(), %-"-, modified by -file
+ istk=[] :: [ifdef()], %Ifdef stack
+ sstk=[] :: [#epp{}], %State stack
+ path=[] :: [file:name()], %Include-path
+ macs = dict:new() %Macros (don't care locations)
+ :: dict:dict(name(), {argspec(), tokens()}),
+ uses = dict:new() %Macro use structure
+ :: dict:dict(name(), [{argspec(), [used()]}]),
+ default_encoding = ?DEFAULT_ENCODING :: source_encoding(),
pre_opened = false :: boolean()
}).
@@ -58,6 +71,7 @@
%%% distinction in the internal representation would simplify the code
%%% a little.
+%% open(Options)
%% open(FileName, IncludePath)
%% open(FileName, IncludePath, PreDefMacros)
%% open(FileName, IoDevice, StartLocation, IncludePath, PreDefMacros)
@@ -65,6 +79,7 @@
%% scan_erl_form(Epp)
%% parse_erl_form(Epp)
%% parse_file(Epp)
+%% parse_file(FileName, Options)
%% parse_file(FileName, IncludePath, PreDefMacros)
%% macro_defs(Epp)
@@ -87,14 +102,43 @@ open(Name, Path) ->
ErrorDescriptor :: term().
open(Name, Path, Pdm) ->
- Self = self(),
- Epp = spawn(fun() -> server(Self, Name, Path, Pdm) end),
- epp_request(Epp).
+ internal_open([{name, Name}, {includes, Path}, {macros, Pdm}], #epp{}).
open(Name, File, StartLocation, Path, Pdm) ->
- Self = self(),
- Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end),
- epp_request(Epp).
+ internal_open([{name, Name}, {includes, Path}, {macros, Pdm}],
+ #epp{file=File, pre_opened=true, location=StartLocation}).
+
+-spec open(Options) ->
+ {'ok', Epp} | {'ok', Epp, Extra} | {'error', ErrorDescriptor} when
+ Options :: [{'default_encoding', DefEncoding :: source_encoding()} |
+ {'includes', IncludePath :: [DirectoryName :: file:name()]} |
+ {'macros', PredefMacros :: macros()} |
+ {'name',FileName :: file:name()} |
+ 'extra'],
+ Epp :: epp_handle(),
+ Extra :: [{'encoding', source_encoding() | 'none'}],
+ ErrorDescriptor :: term().
+
+open(Options) ->
+ internal_open(Options, #epp{}).
+
+internal_open(Options, St) ->
+ case proplists:get_value(name, Options) of
+ undefined ->
+ erlang:error(badarg);
+ Name ->
+ Self = self(),
+ Epp = spawn(fun() -> server(Self, Name, Options, St) end),
+ case epp_request(Epp) of
+ {ok, Pid, Encoding} ->
+ case proplists:get_bool(extra, Options) of
+ true -> {ok, Pid, [{encoding, Encoding}]};
+ false -> {ok, Pid}
+ end;
+ Other ->
+ Other
+ end
+ end.
-spec close(Epp) -> 'ok' when
Epp :: epp_handle().
@@ -170,9 +214,6 @@ format_error({'NYI',What}) ->
io_lib:format("not yet implemented '~s'", [What]);
format_error(E) -> file:format_error(E).
-%% parse_file(FileName, IncludePath, [PreDefMacro]) ->
-%% {ok,[Form]} | {error,OpenError}
-
-spec parse_file(FileName, IncludePath, PredefMacros) ->
{'ok', [Form]} | {error, OpenError} when
FileName :: file:name(),
@@ -184,17 +225,40 @@ format_error(E) -> file:format_error(E).
OpenError :: file:posix() | badarg | system_limit.
parse_file(Ifile, Path, Predefs) ->
- case open(Ifile, Path, Predefs) of
+ parse_file(Ifile, [{includes, Path}, {macros, Predefs}]).
+
+-spec parse_file(FileName, Options) ->
+ {'ok', [Form]} | {'ok', [Form], Extra} | {error, OpenError} when
+ FileName :: file:name(),
+ Options :: [{'includes', IncludePath :: [DirectoryName :: file:name()]} |
+ {'macros', PredefMacros :: macros()} |
+ {'default_encoding', DefEncoding :: source_encoding()} |
+ 'extra'],
+ Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
+ Line :: erl_scan:line(),
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
+ Extra :: [{'encoding', source_encoding() | 'none'}],
+ OpenError :: file:posix() | badarg | system_limit.
+
+parse_file(Ifile, Options) ->
+ case internal_open([{name, Ifile} | Options], #epp{}) of
{ok,Epp} ->
Forms = parse_file(Epp),
close(Epp),
{ok,Forms};
+ {ok,Epp,Extra} ->
+ Forms = parse_file(Epp),
+ close(Epp),
+ {ok,Forms,Extra};
{error,E} ->
{error,E}
end.
-%% parse_file(Epp) ->
-%% [Form]
+-spec parse_file(Epp) -> [Form] when
+ Epp :: epp_handle(),
+ Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
+ Line :: erl_scan:line(),
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
parse_file(Epp) ->
case parse_erl_form(Epp) of
@@ -219,8 +283,6 @@ parse_file(Epp) ->
[{eof,Location}]
end.
--define(DEFAULT_ENCODING, utf8).
-
-spec default_encoding() -> source_encoding().
default_encoding() ->
@@ -258,9 +320,16 @@ read_encoding(Name, Options) ->
File :: io:device(). % pid(); raw files don't work
set_encoding(File) ->
+ set_encoding(File, ?DEFAULT_ENCODING).
+
+-spec set_encoding(File, Default) -> source_encoding() | none when
+ Default :: source_encoding(),
+ File :: io:device(). % pid(); raw files don't work
+
+set_encoding(File, Default) ->
Encoding = read_encoding_from_file(File, true),
Enc = case Encoding of
- none -> default_encoding();
+ none -> Default;
Encoding -> Encoding
end,
ok = io:setopts(File, [{encoding, Enc}]),
@@ -446,35 +515,37 @@ restore_typed_record_fields([{attribute,La,type,{{record,Record},Fields,[]}}|
restore_typed_record_fields([Form|Forms]) ->
[Form|restore_typed_record_fields(Forms)].
-%% server(StarterPid, FileName, Path, PreDefMacros)
-
-server(Pid, Name, Path, Pdm) ->
+server(Pid, Name, Options, #epp{pre_opened=PreOpened}=St) ->
process_flag(trap_exit, true),
- case file:open(Name, [read]) of
- {ok,File} ->
- Location = 1,
- init_server(Pid, Name, File, Location, Path, Pdm, false);
- {error,E} ->
- epp_reply(Pid, {error,E})
+ case PreOpened of
+ false ->
+ case file:open(Name, [read]) of
+ {ok,File} ->
+ init_server(Pid, Name, Options, St#epp{file = File});
+ {error,E} ->
+ epp_reply(Pid, {error,E})
+ end;
+ true ->
+ init_server(Pid, Name, Options, St)
end.
-%% server(StarterPid, FileName, IoDevice, Location, Path, PreDefMacros)
-server(Pid, Name, File, AtLocation, Path, Pdm) ->
- process_flag(trap_exit, true),
- init_server(Pid, Name, File, AtLocation, Path, Pdm, true).
-
-init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
+init_server(Pid, Name, Options, St0) ->
+ Pdm = proplists:get_value(macros, Options, []),
Ms0 = predef_macros(Name),
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
- _ = set_encoding(File),
- epp_reply(Pid, {ok,self()}),
+ #epp{file = File, location = AtLocation} = St0,
+ DefEncoding = proplists:get_value(default_encoding, Options,
+ ?DEFAULT_ENCODING),
+ Encoding = set_encoding(File, DefEncoding),
+ epp_reply(Pid, {ok,self(),Encoding}),
%% ensure directory of current source file is
%% first in path
- Path1 = [filename:dirname(Name) | Path],
- St = #epp{file=File, location=AtLocation, delta=0,
- name=Name, name2=Name, path=Path1, macs=Ms1,
- pre_opened = Pre},
+ Path = [filename:dirname(Name) |
+ proplists:get_value(includes, Options, [])],
+ St = St0#epp{delta=0, name=Name, name2=Name,
+ path=Path, macs=Ms1,
+ default_encoding=DefEncoding},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
@@ -600,9 +671,11 @@ enter_file2(NewF, Pname, From, St0, AtLocation) ->
%% the path) must be dropped, otherwise the path used within the current
%% file will depend on the order of file inclusions in the parent files
Path = [filename:dirname(Pname) | tl(St0#epp.path)],
- _ = set_encoding(NewF),
+ DefEncoding = St0#epp.default_encoding,
+ _ = set_encoding(NewF, DefEncoding),
#epp{file=NewF,location=Loc,name=Pname,name2=Pname,delta=0,
- sstk=[St0|St0#epp.sstk],path=Path,macs=Ms}.
+ sstk=[St0|St0#epp.sstk],path=Path,macs=Ms,
+ default_encoding=DefEncoding}.
enter_file_reply(From, Name, Location, AtLocation) ->
Attr = loc_attr(AtLocation),
@@ -640,7 +713,7 @@ leave_file(From, St) ->
Ms = dict:store({atom,'FILE'},
{none,[{string,CurrLoc,OldName2}]},
St#epp.macs),
- NextSt = OldSt#epp{sstk=Sts,macs=Ms},
+ NextSt = OldSt#epp{sstk=Sts,macs=Ms,uses=St#epp.uses},
enter_file_reply(From, OldName, CurrLoc, CurrLoc),
case OldName2 =:= OldName of
true ->
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index ed8fea5d78..caed4d41d6 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -66,7 +66,7 @@ my_halt(Reason) ->
compile(List) ->
process_flag(trap_exit, true),
- Pid = spawn_link(fun() -> compiler_runner(List) end),
+ Pid = spawn_link(compiler_runner(List)),
receive
{'EXIT', Pid, {compiler_result, Result}} ->
Result;
@@ -79,14 +79,16 @@ compile(List) ->
error
end.
--spec compiler_runner([cmd_line_arg()]) -> no_return().
+-spec compiler_runner([cmd_line_arg()]) -> fun(() -> no_return()).
compiler_runner(List) ->
- %% We don't want the current directory in the code path.
- %% Remove it.
- Path = [D || D <- code:get_path(), D =/= "."],
- true = code:set_path(Path),
- exit({compiler_result, compile1(List)}).
+ fun() ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ exit({compiler_result, compile1(List)})
+ end.
%% Parses the first part of the option list.
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 5f96795d92..acde3ad5d6 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -18,6 +18,9 @@
%%
-module(erl_eval).
+%% Guard is_map/1 is not yet supported in HiPE.
+-compile(no_native).
+
%% An evaluator for Erlang abstract syntax.
-export([exprs/2,exprs/3,exprs/4,expr/2,expr/3,expr/4,expr/5,
@@ -241,23 +244,22 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
%% map
-expr({map_field_assoc,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
- {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
- {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
- ret_expr({map_assoc,K,V}, merge_bindings(Bs1,Bs2), RBs);
-expr({map_field_exact,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
- {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
- {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
- ret_expr({map_exact,K,V}, merge_bindings(Bs1,Bs2), RBs);
expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) ->
{value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs),
- {Vs,Bs} = expr_list(Es, Bs1, Lf, Ef),
- ret_expr(lists:foldl(fun
- ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
- ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi)
- end, Map0, Vs), Bs, RBs);
+ case Map0 of
+ #{} ->
+ {Vs,Bs} = eval_map_fields(Es, Bs1, Lf, Ef),
+ Map1 = lists:foldl(fun ({map_assoc,K,V}, Mi) ->
+ maps:put(K, V, Mi);
+ ({map_exact,K,V}, Mi) ->
+ maps:update(K, V, Mi)
+ end, Map0, Vs),
+ ret_expr(Map1, Bs, RBs);
+ _ ->
+ erlang:raise(error, {badarg,Map0}, stacktrace())
+ end;
expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
- {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
+ {Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef),
ret_expr(lists:foldl(fun
({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi)
end, maps:new(), Vs), Bs, RBs);
@@ -749,6 +751,24 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, Acc) ->
end
end.
+%% eval_map_fields([Field], Bindings, LocalFunctionHandler,
+%% ExternalFuncHandler) ->
+%% {[{map_assoc | map_exact,Key,Value}],Bindings}
+
+eval_map_fields(Fs, Bs, Lf, Ef) ->
+ eval_map_fields(Fs, Bs, Lf, Ef, []).
+
+eval_map_fields([{map_field_assoc,_,K0,V0}|Fs], Bs0, Lf, Ef, Acc) ->
+ {value,K1,Bs1} = expr(K0, Bs0, Lf, Ef, none),
+ {value,V1,Bs2} = expr(V0, Bs1, Lf, Ef, none),
+ eval_map_fields(Fs, Bs2, Lf, Ef, [{map_assoc,K1,V1}|Acc]);
+eval_map_fields([{map_field_exact,_,K0,V0}|Fs], Bs0, Lf, Ef, Acc) ->
+ {value,K1,Bs1} = expr(K0, Bs0, Lf, Ef, none),
+ {value,V1,Bs2} = expr(V0, Bs1, Lf, Ef, none),
+ eval_map_fields(Fs, Bs2, Lf, Ef, [{map_exact,K1,V1}|Acc]);
+eval_map_fields([], Bs, _Lf, _Ef, Acc) ->
+ {lists:reverse(Acc),Bs}.
+
%% RBs is the bindings to return when the evalution of a function
%% (fun) has finished. If RBs =:= none, then the evalution took place
@@ -996,12 +1016,16 @@ guard0([], _Bs, _Lf, _Ef) -> true.
guard_test({call,L,{atom,Ln,F},As0}, Bs0, Lf, Ef) ->
TT = type_test(F),
G = {call,L,{atom,Ln,TT},As0},
- try {value,true,_} = expr(G, Bs0, Lf, Ef, none)
- catch error:_ -> {value,false,Bs0} end;
-guard_test({call,L,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,_F}=T},As0},
+ expr_guard_test(G, Bs0, Lf, Ef);
+guard_test({call,L,{remote,Lr,{atom,Lm,erlang},{atom,Lf,F}},As0},
Bs0, Lf, Ef) ->
- guard_test({call,L,T,As0}, Bs0, Lf, Ef);
+ TT = type_test(F),
+ G = {call,L,{remote,Lr,{atom,Lm,erlang},{atom,Lf,TT}},As0},
+ expr_guard_test(G, Bs0, Lf, Ef);
guard_test(G, Bs0, Lf, Ef) ->
+ expr_guard_test(G, Bs0, Lf, Ef).
+
+expr_guard_test(G, Bs0, Lf, Ef) ->
try {value,true,_} = expr(G, Bs0, Lf, Ef, none)
catch error:_ -> {value,false,Bs0} end.
@@ -1099,9 +1123,10 @@ match1({tuple,_,Elts}, Tuple, Bs, BBs)
match_tuple(Elts, Tuple, 1, Bs, BBs);
match1({tuple,_,_}, _, _Bs, _BBs) ->
throw(nomatch);
-match1({map,_,Fs}, Map, Bs, BBs) ->
+match1({map,_,Fs}, #{}=Map, Bs, BBs) ->
match_map(Fs, Map, Bs, BBs);
-
+match1({map,_,_}, _, _Bs, _BBs) ->
+ throw(nomatch);
match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) ->
eval_bits:match_bits(Fs, B, Bs0, BBs,
match_fun(BBs),
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 4741bef6b9..f53c6e1278 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -135,9 +135,10 @@ pattern({tuple,Line,Ps}, St0) ->
pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
-pattern({map_field_exact,Line,Key,V0}, St0) ->
- {V,St1} = pattern(V0, St0),
- {{map_field_exact,Line,Key,V},St1};
+pattern({map_field_exact,Line,Key0,V0}, St0) ->
+ {Key,St1} = pattern(Key0, St0),
+ {V,St2} = pattern(V0, St1),
+ {{map_field_exact,Line,Key,V},St2};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{struct,Line,Tag,TPs},TPsvs,St1};
@@ -310,9 +311,10 @@ expr({tuple,Line,Es0}, St0) ->
expr({map,Line,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Es1},St1};
-expr({map,Line,Var,Es0}, St0) ->
- {Es1,St1} = expr_list(Es0, St0),
- {{map,Line,Var,Es1},St1};
+expr({map,Line,Arg0,Es0}, St0) ->
+ {Arg1,St1} = expr(Arg0, St0),
+ {Es1,St2} = expr_list(Es0, St1),
+ {{map,Line,Arg1,Es1},St2};
expr({map_field_assoc,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index f630db6032..c4c94fbee4 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -80,13 +80,17 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-type fa() :: {atom(), arity()}. % function+arity
-type ta() :: {atom(), arity()}. % type+arity
+-record(typeinfo, {attr, line}).
+
%% Usage of records, functions, and imports. The variable table, which
%% is passed on as an argument, holds the usage of variables.
-record(usage, {
calls = dict:new(), %Who calls who
imported = [], %Actually imported functions
- used_records=sets:new() :: set(), %Used record definitions
- used_types = dict:new() :: dict() %Used type definitions
+ used_records = sets:new() %Used record definitions
+ :: sets:set(atom()),
+ used_types = dict:new() %Used type definitions
+ :: dict:dict(ta(), line())
}).
%% Define the lint state record.
@@ -95,13 +99,17 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-record(lint, {state=start :: 'start' | 'attribute' | 'function',
module=[], %Module
behaviour=[], %Behaviour
- exports=gb_sets:empty() :: gb_set(), %Exports
- imports=[], %Imports
+ exports=gb_sets:empty() :: gb_sets:set(fa()),%Exports
+ imports=[] :: [fa()], %Imports, an orddict()
compile=[], %Compile flags
- records=dict:new() :: dict(), %Record definitions
- locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned)
- no_auto=gb_sets:empty() :: gb_set(), %Functions explicitly not autoimported
- defined=gb_sets:empty() :: gb_set(), %Defined fuctions
+ records=dict:new() %Record definitions
+ :: dict:dict(atom(), {line(),Fields :: term()}),
+ locals=gb_sets:empty() %All defined functions (prescanned)
+ :: gb_sets:set(fa()),
+ no_auto=gb_sets:empty() %Functions explicitly not autoimported
+ :: gb_sets:set(fa()) | 'all',
+ defined=gb_sets:empty() %Defined fuctions
+ :: gb_sets:set(fa()),
on_load=[] :: [fa()], %On-load function
on_load_line=0 :: line(), %Line for on_load
clashes=[], %Exported functions named as BIFs
@@ -116,12 +124,16 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%outside any fun or lc
xqlc= false :: boolean(), %true if qlc.hrl included
new = false :: boolean(), %Has user-defined 'new/N'
- called= [] :: [{fa(),line()}], %Called functions
+ called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
- specs = dict:new() :: dict(), %Type specifications
- callbacks = dict:new() :: dict(), %Callback types
- types = dict:new() :: dict(), %Type definitions
- exp_types=gb_sets:empty():: gb_set() %Exported types
+ specs = dict:new() %Type specifications
+ :: dict:dict(mfa(), line()),
+ callbacks = dict:new() %Callback types
+ :: dict:dict(mfa(), line()),
+ types = dict:new() %Type definitions
+ :: dict:dict(ta(), #typeinfo{}),
+ exp_types=gb_sets:empty() %Exported types
+ :: gb_sets:set(ta())
}).
-type lint_state() :: #lint{}.
@@ -225,6 +237,8 @@ format_error({too_many_arguments,Arity}) ->
"maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
%% --- patterns and guards ---
format_error(illegal_pattern) -> "illegal pattern";
+format_error(illegal_map_key) ->
+ "illegal map key";
format_error({illegal_map_key_variable,K}) ->
io_lib:format("illegal use of variable ~w in map",[K]);
format_error(illegal_bin_pattern) ->
@@ -317,10 +331,14 @@ format_error({undefined_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]);
format_error({unused_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]);
-format_error({new_builtin_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s is a new builtin type; "
+%% format_error({new_builtin_type, {TypeName, Arity}}) ->
+%% io_lib:format("type ~w~s is a new builtin type; "
+%% "its (re)definition is allowed only until the next release",
+%% [TypeName, gen_type_paren(Arity)]);
+format_error({new_var_arity_type, TypeName}) ->
+ io_lib:format("type ~w is a new builtin type; "
"its (re)definition is allowed only until the next release",
- [TypeName, gen_type_paren(Arity)]);
+ [TypeName]);
format_error({builtin_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
[TypeName, gen_type_paren(Arity)]);
@@ -344,9 +362,19 @@ format_error(spec_wrong_arity) ->
"spec has the wrong arity";
format_error(callback_wrong_arity) ->
"callback has the wrong arity";
-format_error({imported_predefined_type, Name}) ->
- io_lib:format("referring to built-in type ~w as a remote type; "
- "please take out the module name", [Name]);
+format_error({deprecated_builtin_type, {Name, Arity},
+ Replacement, Rel}) ->
+ UseS = case Replacement of
+ {Mod, NewName} ->
+ io_lib:format("use ~w:~w/~w", [Mod, NewName, Arity]);
+ {Mod, NewName, NewArity} ->
+ io_lib:format("use ~w:~w/~w or preferably ~w:~w/~w",
+ [Mod, NewName, Arity,
+ Mod, NewName, NewArity])
+ end,
+ io_lib:format("type ~w/~w is deprecated and will be "
+ "removed in ~s; use ~s",
+ [Name, Arity, Rel, UseS]);
format_error({not_exported_opaque, {TypeName, Arity}}) ->
io_lib:format("opaque type ~w~s is not exported",
[TypeName, gen_type_paren(Arity)]);
@@ -498,6 +526,9 @@ start(File, Opts) ->
{deprecated_function,
bool_option(warn_deprecated_function, nowarn_deprecated_function,
true, Opts)},
+ {deprecated_type,
+ bool_option(warn_deprecated_type, nowarn_deprecated_type,
+ true, Opts)},
{obsolete_guard,
bool_option(warn_obsolete_guard, nowarn_obsolete_guard,
true, Opts)},
@@ -1155,7 +1186,7 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
add_error(Line, {bad_export_type, ETs}, St0)
end.
--spec exports(lint_state()) -> gb_set().
+-spec exports(lint_state()) -> gb_sets:set(fa()).
exports(#lint{compile = Opts, defined = Defs, exports = Es}) ->
case lists:member(export_all, Opts) of
@@ -1372,18 +1403,20 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) ->
pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
- pattern_list(Ps, Vt, Old, Bvt, St);
-pattern({map_field_assoc,Line,_,_}, _, _, _, St) ->
- {[],[],add_error(Line, illegal_pattern, St)};
-pattern({map_field_exact,Line,KP,VP}, Vt, Old, Bvt0, St0) ->
- %% if the key pattern has variables we should fail
- case expr(KP,[],St0) of
- {[],_} ->
- pattern(VP, Vt, Old, Bvt0, St0);
- {[Var|_],_} ->
- %% found variables in key expression
- {Vt,Old,add_error(Line,{illegal_map_key_variable,element(1,Var)},St0)}
- end;
+ foldl(fun
+ ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
+ {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
+ ({map_field_exact,L,KP,VP}, {Psvt,Bvt0,St0}) ->
+ case is_valid_map_key(KP, St0) of
+ true ->
+ {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0),
+ {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), St1};
+ false ->
+ {Psvt,Bvt0,add_error(L, illegal_map_key, St0)};
+ {false,variable,Var} ->
+ {Psvt,Bvt0,add_error(L, {illegal_map_key_variable,Var}, St0)}
+ end
+ end, {[],[],St}, Ps);
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
@@ -1772,13 +1805,11 @@ gexpr({cons,_Line,H,T}, Vt, St) ->
gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
gexpr({map,_Line,Es}, Vt, St) ->
- gexpr_list(Es, Vt, St);
+ map_fields(Es, Vt, check_assoc_fields(Es, St), fun gexpr_list/3);
gexpr({map,_Line,Src,Es}, Vt, St) ->
- gexpr_list([Src|Es], Vt, St);
-gexpr({map_field_assoc,_Line,K,V}, Vt, St) ->
- gexpr_list([K,V], Vt, St);
-gexpr({map_field_exact,_Line,K,V}, Vt, St) ->
- gexpr_list([K,V], Vt, St);
+ {Svt,St1} = gexpr(Src, Vt, St),
+ {Fvt,St2} = map_fields(Es, Vt, St1, fun gexpr_list/3),
+ {vtmerge(Svt, Fvt),St2};
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
@@ -1851,6 +1882,10 @@ gexpr({op,Line,Op,A}, Vt, St0) ->
true -> {Avt,St1};
false -> {Avt,add_error(Line, illegal_guard_expr, St1)}
end;
+gexpr({op,_,'andalso',L,R}, Vt, St) ->
+ gexpr_list([L,R], Vt, St);
+gexpr({op,_,'orelse',L,R}, Vt, St) ->
+ gexpr_list([L,R], Vt, St);
gexpr({op,Line,Op,L,R}, Vt, St0) ->
{Avt,St1} = gexpr_list([L,R], Vt, St0),
case is_gexpr_op(Op, 2) of
@@ -1888,7 +1923,7 @@ is_guard_test(Expression, Forms) ->
end, start(), RecordAttributes),
is_guard_test2(zip_file_and_line(Expression, "nofile"), St0#lint.records).
-%% is_guard_test2(Expression, RecordDefs :: dict()) -> boolean().
+%% is_guard_test2(Expression, RecordDefs :: dict:dict()) -> boolean().
is_guard_test2({call,Line,{atom,Lr,record},[E,A]}, RDs) ->
is_gexpr({call,Line,{atom,Lr,is_record},[E,A]}, RDs);
is_guard_test2({call,_Line,{atom,_La,Test},As}=Call, RDs) ->
@@ -1937,12 +1972,14 @@ is_gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, RDs) ->
is_gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, RDs);
is_gexpr({op,_L,Op,A}, RDs) ->
is_gexpr_op(Op, 1) andalso is_gexpr(A, RDs);
+is_gexpr({op,_L,'andalso',A1,A2}, RDs) ->
+ is_gexpr_list([A1,A2], RDs);
+is_gexpr({op,_L,'orelse',A1,A2}, RDs) ->
+ is_gexpr_list([A1,A2], RDs);
is_gexpr({op,_L,Op,A1,A2}, RDs) ->
is_gexpr_op(Op, 2) andalso is_gexpr_list([A1,A2], RDs);
is_gexpr(_Other, _RDs) -> false.
-is_gexpr_op('andalso', 2) -> true;
-is_gexpr_op('orelse', 2) -> true;
is_gexpr_op(Op, A) ->
try erl_internal:op_type(Op, A) of
arith -> true;
@@ -1996,24 +2033,12 @@ expr({bc,_Line,E,Qs}, Vt, St) ->
handle_comprehension(E, Qs, Vt, St);
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
-expr({map,Line,Es}, Vt, St) ->
- {Rvt,St1} = expr_list(Es,Vt,St),
- case is_valid_map_construction(Es) of
- true -> {Rvt,St1};
- false -> {[],add_error(Line,illegal_map_construction,St1)}
- end;
+expr({map,_Line,Es}, Vt, St) ->
+ map_fields(Es, Vt, check_assoc_fields(Es, St), fun expr_list/3);
expr({map,_Line,Src,Es}, Vt, St) ->
- expr_list([Src|Es], Vt, St);
-expr({map_field_assoc,Line,K,V}, Vt, St) ->
- case is_valid_map_key(K,St) of
- true -> expr_list([K,V], Vt, St);
- {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
- end;
-expr({map_field_exact,Line,K,V}, Vt, St) ->
- case is_valid_map_key(K,St) of
- true -> expr_list([K,V], Vt, St);
- {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
- end;
+ {Svt,St1} = expr(Src, Vt, St),
+ {Fvt,St2} = map_fields(Es, Vt, St1, fun expr_list/3),
+ {vtupdate(Svt, Fvt),St2};
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
@@ -2221,6 +2246,26 @@ record_expr(Line, Rec, Vt, St0) ->
St1 = warn_invalid_record(Line, Rec, St0),
expr(Rec, Vt, St1).
+check_assoc_fields([{map_field_exact,Line,_,_}|Fs], St) ->
+ check_assoc_fields(Fs, add_error(Line, illegal_map_construction, St));
+check_assoc_fields([{map_field_assoc,_,_,_}|Fs], St) ->
+ check_assoc_fields(Fs, St);
+check_assoc_fields([], St) ->
+ St.
+
+map_fields([{Tag,Line,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ St1 = case is_valid_map_key(K, St) of
+ true -> St;
+ false -> add_error(Line, illegal_map_key, St);
+ {false,variable,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St)
+ end,
+ {Pvt,St2} = F([K,V], Vt, St1),
+ {Vts,St3} = map_fields(Fs, Vt, St2, F),
+ {vtupdate(Pvt, Vts),St3};
+map_fields([], Vt, St, _) ->
+ {Vt,St}.
+
%% warn_invalid_record(Line, Record, State0) -> State
%% Adds warning if the record is invalid.
@@ -2273,18 +2318,64 @@ is_valid_call(Call) ->
_ -> true
end.
-%% check_map_construction
-%% Only #{ K => V }, i.e. assoc is a valid construction
-is_valid_map_construction([{map_field_assoc,_,_,_}|Es]) ->
- is_valid_map_construction(Es);
-is_valid_map_construction([]) -> true;
-is_valid_map_construction(_) -> false.
+%% is_valid_map_key(K,St) -> true | false | {false, Var::atom()}
+%% check for value expression without variables
is_valid_map_key(K,St) ->
case expr(K,[],St) of
- {[],_} -> true;
+ {[],_} ->
+ is_valid_map_key_value(K);
{[Var|_],_} ->
- {false,element(1,Var)}
+ {false,variable,element(1,Var)}
+ end.
+
+is_valid_map_key_value(K) ->
+ case K of
+ {char,_,_} -> true;
+ {integer,_,_} -> true;
+ {float,_,_} -> true;
+ {string,_,_} -> true;
+ {nil,_} -> true;
+ {atom,_,_} -> true;
+ {cons,_,H,T} ->
+ is_valid_map_key_value(H) andalso
+ is_valid_map_key_value(T);
+ {tuple,_,Es} ->
+ foldl(fun(E,B) ->
+ B andalso is_valid_map_key_value(E)
+ end,true,Es);
+ {map,_,Arg,Ps} ->
+ % only check for value expressions to be valid
+ % invalid map expressions are later checked in
+ % core and kernel
+ is_valid_map_key_value(Arg) andalso foldl(fun
+ ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
+ end,true,Ps);
+ {map,_,Ps} ->
+ foldl(fun
+ ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
+ end, true, Ps);
+ {record,_,_,Fs} ->
+ foldl(fun
+ ({record_field,_,Ke,Ve},B) ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
+ end,true,Fs);
+ {bin,_,Es} ->
+ % only check for value expressions to be valid
+ % invalid binary expressions are later checked in
+ % core and kernel
+ foldl(fun
+ ({bin_element,_,E,_,_},B) ->
+ B andalso is_valid_map_key_value(E)
+ end,true,Es);
+ _ -> false
end.
%% record_def(Line, RecordName, [RecField], State) -> State.
@@ -2499,8 +2590,6 @@ find_field(_F, []) -> error.
%% Attr :: 'type' | 'opaque'
%% Checks that a type definition is valid.
--record(typeinfo, {attr, line}).
-
type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) ->
%% The record field names and such are checked in the record format.
%% We only need to check the types.
@@ -2517,32 +2606,46 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
CheckType = {type, -1, product, [ProtoType|Args]},
check_type(CheckType, St#lint{types=NewDefs})
end,
- case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of
- true ->
- case is_default_type(TypePair) of
- true ->
- case is_newly_introduced_builtin_type(TypePair) of
- %% allow some types just for bootstrapping
- true ->
- Warn = {new_builtin_type, TypePair},
- St1 = add_warning(Line, Warn, St0),
- StoreType(St1);
- false ->
- add_error(Line, {builtin_type, TypePair}, St0)
- end;
- false -> add_error(Line, {redefine_type, TypePair}, St0)
- end;
- false ->
- St1 = case
- Attr =:= opaque andalso
- is_underspecified(ProtoType, Arity)
- of
- true ->
- Warn = {underspecified_opaque, TypePair},
- add_warning(Line, Warn, St0);
- false -> St0
- end,
- StoreType(St1)
+ case is_default_type(TypePair) of
+ true ->
+ case is_obsolete_builtin_type(TypePair) of
+ true -> StoreType(St0);
+ false -> add_error(Line, {builtin_type, TypePair}, St0)
+%% case is_newly_introduced_builtin_type(TypePair) of
+%% %% allow some types just for bootstrapping
+%% true ->
+%% Warn = {new_builtin_type, TypePair},
+%% St1 = add_warning(Line, Warn, St0),
+%% StoreType(St1);
+%% false ->
+%% add_error(Line, {builtin_type, TypePair}, St0)
+%% end
+ end;
+ false ->
+ case
+ dict:is_key(TypePair, TypeDefs) orelse
+ is_var_arity_type(TypeName)
+ of
+ true ->
+ case is_newly_introduced_var_arity_type(TypeName) of
+ true ->
+ Warn = {new_var_arity_type, TypeName},
+ add_warning(Line, Warn, St0);
+ false ->
+ add_error(Line, {redefine_type, TypePair}, St0)
+ end;
+ false ->
+ St1 = case
+ Attr =:= opaque andalso
+ is_underspecified(ProtoType, Arity)
+ of
+ true ->
+ Warn = {underspecified_opaque, TypePair},
+ add_warning(Line, Warn, St0);
+ false -> St0
+ end,
+ StoreType(St1)
+ end
end.
is_underspecified({type,_,term,[]}, 0) -> true;
@@ -2566,18 +2669,12 @@ check_type({paren_type, _L, [Type]}, SeenVars, St) ->
check_type(Type, SeenVars, St);
check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
SeenVars, #lint{module=CurrentMod} = St) ->
- St1 =
- case is_default_type({Name, length(Args)})
- orelse is_var_arity_type(Name) of
- true -> add_error(L, {imported_predefined_type, Name}, St);
- false -> St
- end,
case Mod =:= CurrentMod of
- true -> check_type({type, L, Name, Args}, SeenVars, St1);
+ true -> check_type({type, L, Name, Args}, SeenVars, St);
false ->
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
check_type(T, AccSeenVars, AccSt)
- end, {SeenVars, St1}, Args)
+ end, {SeenVars, St}, Args)
end;
check_type({integer, _L, _}, SeenVars, St) -> {SeenVars, St};
check_type({atom, _L, _}, SeenVars, St) -> {SeenVars, St};
@@ -2635,14 +2732,35 @@ check_type({type, _L, product, Args}, SeenVars, St) ->
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
check_type(T, AccSeenVars, AccSt)
end, {SeenVars, St}, Args);
-check_type({type, La, TypeName, Args}, SeenVars, #lint{usage=Usage} = St) ->
+check_type({type, La, TypeName, Args}, SeenVars, St) ->
+ #lint{usage=Usage, module = Module, types=Types} = St,
Arity = length(Args),
+ TypePair = {TypeName, Arity},
St1 = case is_var_arity_type(TypeName) of
true -> St;
false ->
- OldUsed = Usage#usage.used_types,
- UsedTypes = dict:store({TypeName, Arity}, La, OldUsed),
- St#lint{usage=Usage#usage{used_types=UsedTypes}}
+ Obsolete = (is_warn_enabled(deprecated_type, St)
+ andalso obsolete_builtin_type(TypePair)),
+ IsObsolete =
+ case Obsolete of
+ {deprecated, Repl, _} when element(1, Repl) =/= Module ->
+ case dict:find(TypePair, Types) of
+ {ok, _} -> false;
+ error -> true
+ end;
+ _ -> false
+ end,
+ case IsObsolete of
+ true ->
+ {deprecated, Replacement, Rel} = Obsolete,
+ Tag = deprecated_builtin_type,
+ W = {Tag, TypePair, Replacement, Rel},
+ add_warning(La, W, St);
+ false ->
+ OldUsed = Usage#usage.used_types,
+ UsedTypes = dict:store(TypePair, La, OldUsed),
+ St#lint{usage=Usage#usage{used_types=UsedTypes}}
+ end
end,
check_type({type, -1, product, Args}, SeenVars, St1);
check_type(I, SeenVars, St) ->
@@ -2687,6 +2805,7 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
{SeenVars, St}.
is_var_arity_type(tuple) -> true;
+is_var_arity_type(map) -> true;
is_var_arity_type(product) -> true;
is_var_arity_type(union) -> true;
is_var_arity_type(record) -> true;
@@ -2719,7 +2838,6 @@ is_default_type({iodata, 0}) -> true;
is_default_type({iolist, 0}) -> true;
is_default_type({list, 0}) -> true;
is_default_type({list, 1}) -> true;
-is_default_type({map, 0}) -> true;
is_default_type({maybe_improper_list, 0}) -> true;
is_default_type({maybe_improper_list, 2}) -> true;
is_default_type({mfa, 0}) -> true;
@@ -2750,20 +2868,32 @@ is_default_type({timeout, 0}) -> true;
is_default_type({var, 1}) -> true;
is_default_type(_) -> false.
-%% R13
-is_newly_introduced_builtin_type({arity, 0}) -> true;
-is_newly_introduced_builtin_type({array, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({bitstring, 0}) -> true;
-is_newly_introduced_builtin_type({dict, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({digraph, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({gb_set, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({gb_tree, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({iodata, 0}) -> true;
-is_newly_introduced_builtin_type({queue, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({set, 0}) -> true; % opaque
-%% R13B01
-is_newly_introduced_builtin_type({boolean, 0}) -> true;
-is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
+is_newly_introduced_var_arity_type(map) -> true;
+is_newly_introduced_var_arity_type(_) -> false.
+
+%% is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
+
+is_obsolete_builtin_type(TypePair) ->
+ obsolete_builtin_type(TypePair) =/= no.
+
+%% Obsolete in OTP 17.0.
+obsolete_builtin_type({array, 0}) ->
+ {deprecated, {array, array, 1}, "OTP 18.0"};
+obsolete_builtin_type({dict, 0}) ->
+ {deprecated, {dict, dict, 2}, "OTP 18.0"};
+obsolete_builtin_type({digraph, 0}) ->
+ {deprecated, {digraph, graph}, "OTP 18.0"};
+obsolete_builtin_type({gb_set, 0}) ->
+ {deprecated, {gb_sets, set, 1}, "OTP 18.0"};
+obsolete_builtin_type({gb_tree, 0}) ->
+ {deprecated, {gb_trees, tree, 2}, "OTP 18.0"};
+obsolete_builtin_type({queue, 0}) ->
+ {deprecated, {queue, queue, 1}, "OTP 18.0"};
+obsolete_builtin_type({set, 0}) ->
+ {deprecated, {sets, set, 1}, "OTP 18.0"};
+obsolete_builtin_type({tid, 0}) ->
+ {deprecated, {ets, tid}, "OTP 18.0"};
+obsolete_builtin_type({Name, A}) when is_atom(Name), is_integer(A) -> no.
%% spec_decl(Line, Fun, Types, State) -> State.
@@ -3648,15 +3778,22 @@ is_imported_from_erlang(ImportSet,{Func,Arity}) ->
{ok,erlang} -> true;
_ -> false
end.
-%% Build set of functions where auto-import is explicitly supressed
+%% Build set of functions where auto-import is explicitly suppressed
auto_import_suppressed(CompileFlags) ->
- L0 = [ X || {no_auto_import,X} <- CompileFlags ],
- L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ],
- gb_sets:from_list(L1).
-%% Predicate to find out if autoimport is explicitly supressed for a function
+ case lists:member(no_auto_import, CompileFlags) of
+ true ->
+ all;
+ false ->
+ L0 = [ X || {no_auto_import,X} <- CompileFlags ],
+ L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ],
+ gb_sets:from_list(L1)
+ end.
+%% Predicate to find out if autoimport is explicitly suppressed for a function
+is_autoimport_suppressed(all,{_Func,_Arity}) ->
+ true;
is_autoimport_suppressed(NoAutoSet,{Func,Arity}) ->
gb_sets:is_element({Func,Arity},NoAutoSet).
-%% Predicate to find out if a function specific bif-clash supression (old deprecated) is present
+%% Predicate to find out if a function specific bif-clash suppression (old deprecated) is present
bif_clash_specifically_disabled(St,{F,A}) ->
Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile),
lists:member({F,A},Nowarn).
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 6316db7054..1dc5fc52a7 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -919,59 +919,63 @@ normalise_list([]) ->
Data :: term(),
AbsTerm :: abstract_expr().
abstract(T) ->
- abstract(T, 0, epp:default_encoding()).
+ abstract(T, 0, enc_func(epp:default_encoding())).
+
+-type encoding_func() :: fun((non_neg_integer()) -> boolean()).
%%% abstract/2 takes line and encoding options
-spec abstract(Data, Options) -> AbsTerm when
Data :: term(),
Options :: Line | [Option],
Option :: {line, Line} | {encoding, Encoding},
- Encoding :: latin1 | unicode | utf8,
+ Encoding :: 'latin1' | 'unicode' | 'utf8' | 'none' | encoding_func(),
Line :: erl_scan:line(),
AbsTerm :: abstract_expr().
abstract(T, Line) when is_integer(Line) ->
- abstract(T, Line, epp:default_encoding());
+ abstract(T, Line, enc_func(epp:default_encoding()));
abstract(T, Options) when is_list(Options) ->
Line = proplists:get_value(line, Options, 0),
Encoding = proplists:get_value(encoding, Options,epp:default_encoding()),
- abstract(T, Line, Encoding).
+ EncFunc = enc_func(Encoding),
+ abstract(T, Line, EncFunc).
-define(UNICODE(C),
- is_integer(C) andalso
- (C >= 0 andalso C < 16#D800 orelse
+ (C < 16#D800 orelse
C > 16#DFFF andalso C < 16#FFFE orelse
C > 16#FFFF andalso C =< 16#10FFFF)).
+enc_func(latin1) -> fun(C) -> C < 256 end;
+enc_func(unicode) -> fun(C) -> ?UNICODE(C) end;
+enc_func(utf8) -> fun(C) -> ?UNICODE(C) end;
+enc_func(none) -> none;
+enc_func(Fun) when is_function(Fun, 1) -> Fun;
+enc_func(Term) -> erlang:error({badarg, Term}).
+
abstract(T, L, _E) when is_integer(T) -> {integer,L,T};
abstract(T, L, _E) when is_float(T) -> {float,L,T};
abstract(T, L, _E) when is_atom(T) -> {atom,L,T};
abstract([], L, _E) -> {nil,L};
abstract(B, L, _E) when is_bitstring(B) ->
{bin, L, [abstract_byte(Byte, L) || Byte <- bitstring_to_list(B)]};
-abstract([C|T], L, unicode=E) when ?UNICODE(C) ->
- abstract_unicode_string(T, [C], L, E);
-abstract([C|T], L, utf8=E) when ?UNICODE(C) ->
- abstract_unicode_string(T, [C], L, E);
-abstract([C|T], L, latin1=E) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C], L, E);
-abstract([H|T], L, E) ->
+abstract([H|T], L, none=E) ->
{cons,L,abstract(H, L, E),abstract(T, L, E)};
+abstract(List, L, E) when is_list(List) ->
+ abstract_list(List, [], L, E);
abstract(Tuple, L, E) when is_tuple(Tuple) ->
- {tuple,L,abstract_list(tuple_to_list(Tuple), L, E)}.
-
-abstract_string([C|T], String, L, E) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C|String], L, E);
-abstract_string([], String, L, _E) ->
- {string, L, lists:reverse(String)};
-abstract_string(T, String, L, E) ->
- not_string(String, abstract(T, L, E), L, E).
-
-abstract_unicode_string([C|T], String, L, E) when ?UNICODE(C) ->
- abstract_unicode_string(T, [C|String], L, E);
-abstract_unicode_string([], String, L, _E) ->
+ {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)}.
+
+abstract_list([H|T], String, L, E) ->
+ case is_integer(H) andalso H >= 0 andalso E(H) of
+ true ->
+ abstract_list(T, [H|String], L, E);
+ false ->
+ AbstrList = {cons,L,abstract(H, L, E),abstract(T, L, E)},
+ not_string(String, AbstrList, L, E)
+ end;
+abstract_list([], String, L, _E) ->
{string, L, lists:reverse(String)};
-abstract_unicode_string(T, String, L, E) ->
+abstract_list(T, String, L, E) ->
not_string(String, abstract(T, L, E), L, E).
not_string([C|T], Result, L, E) ->
@@ -979,9 +983,9 @@ not_string([C|T], Result, L, E) ->
not_string([], Result, _L, _E) ->
Result.
-abstract_list([H|T], L, E) ->
- [abstract(H, L, E)|abstract_list(T, L, E)];
-abstract_list([], _L, _E) ->
+abstract_tuple_list([H|T], L, E) ->
+ [abstract(H, L, E)|abstract_tuple_list(T, L, E)];
+abstract_tuple_list([], _L, _E) ->
[].
abstract_byte(Byte, L) when is_integer(Byte) ->
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 8a1d8e0440..82bc2c1460 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -256,6 +256,10 @@ ltype({type,_Line,nonempty_list,[T]}) ->
{seq,$[,$],[$,],[ltype(T),leaf("...")]};
ltype({type,Line,nil,[]}) ->
lexpr({nil,Line}, 0, options(none));
+ltype({type,Line,map,any}) ->
+ simple_type({atom,Line,map}, []);
+ltype({type,_Line,map,Pairs}) ->
+ map_type(Pairs);
ltype({type,Line,tuple,any}) ->
simple_type({atom,Line,tuple}, []);
ltype({type,_Line,tuple,Ts}) ->
@@ -289,6 +293,15 @@ binary_type(I1, I2) ->
E2 = [[leaf("_:_*"),lexpr(I2, P, options(none))] || U],
{seq,'<<','>>',[$,],E1++E2}.
+map_type(Fs) ->
+ {first,[$#],map_pair_types(Fs)}.
+
+map_pair_types(Fs) ->
+ tuple_type(Fs, fun map_pair_type/1).
+
+map_pair_type({type,_Line,map_field_assoc,Ktype,Vtype}) ->
+ {seq,[],[]," =>",[ltype(Ktype),ltype(Vtype)]}.
+
record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
@@ -479,6 +492,15 @@ lexpr({record_field, _, Rec, F}, Prec, Opts) ->
{L,P,R} = inop_prec('.'),
El = [lexpr(Rec, L, Opts),$.,lexpr(F, R, Opts)],
maybe_paren(P, Prec, El);
+lexpr({map, _, Fs}, Prec, Opts) ->
+ {P,_R} = preop_prec('#'),
+ El = {first,leaf("#"),map_fields(Fs, Opts)},
+ maybe_paren(P, Prec, El);
+lexpr({map, _, Map, Fs}, Prec, Opts) ->
+ {L,P,_R} = inop_prec('#'),
+ Rl = lexpr(Map, L, Opts),
+ El = {first,[Rl,leaf("#")],map_fields(Fs, Opts)},
+ maybe_paren(P, Prec, El);
lexpr({block,_,Es}, _, Opts) ->
{list,[{step,'begin',body(Es, Opts)},'end']};
lexpr({'if',_,Cs}, _, Opts) ->
@@ -671,6 +693,16 @@ record_field({typed_record_field,Field,Type}, Opts) ->
record_field({record_field,_,F}, Opts) ->
lexpr(F, 0, Opts).
+map_fields(Fs, Opts) ->
+ tuple(Fs, fun map_field/2, Opts).
+
+map_field({map_field_assoc,_,K,V}, Opts) ->
+ Pl = lexpr(K, 0, Opts),
+ {list,[{step,[Pl,leaf(" =>")],lexpr(V, 0, Opts)}]};
+map_field({map_field_exact,_,K,V}, Opts) ->
+ Pl = lexpr(K, 0, Opts),
+ {list,[{step,[Pl,leaf(" :=")],lexpr(V, 0, Opts)}]}.
+
list({cons,_,H,T}, Es, Opts) ->
list(T, [H|Es], Opts);
list({nil,_}, Es, Opts) ->
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 35f6dff57e..6bd0eb8a22 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. 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
@@ -59,7 +59,6 @@
file:filename()
| {file:filename(), binary()}
| {file:filename(), binary(), file:file_info()}.
--type zip_create_option() :: term().
-type section() ::
shebang
| {shebang, shebang() | default | undefined}
@@ -68,8 +67,8 @@
| {emu_args, emu_args() | undefined}
| {source, file:filename() | binary()}
| {beam, file:filename() | binary()}
- | {archive, file:filename() | binary()}
- | {archive, [zip_file()], [zip_create_option()]}.
+ | {archive, zip:filename() | binary()}
+ | {archive, [zip_file()], [zip:create_option()]}.
%%-----------------------------------------------------------------------
@@ -289,6 +288,8 @@ start(EscriptOptions) ->
my_halt(127)
end.
+-spec parse_and_run(_, _, _) -> no_return().
+
parse_and_run(File, Args, Options) ->
CheckOnly = lists:member("s", Options),
{Source, Module, FormsOrBin, HasRecs, Mode} =
@@ -727,6 +728,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
%% Evaluate script
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec debug(_, _, _) -> no_return().
+
debug(Module, AbsMod, Args) ->
case hidden_apply(debugger, debugger, start, []) of
{ok, _} ->
@@ -742,6 +745,8 @@ debug(Module, AbsMod, Args) ->
fatal("Cannot start the debugger")
end.
+-spec run(_, _) -> no_return().
+
run(Module, Args) ->
try
Module:main(Args),
@@ -751,6 +756,8 @@ run(Module, Args) ->
fatal(format_exception(Class, Reason))
end.
+-spec interpret(_, _, _, _) -> no_return().
+
interpret(Forms, HasRecs, File, Args) ->
%% Basic validation before execution
case erl_lint:module(Forms) of
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index cc5e69f574..42b11a97e2 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -53,10 +53,8 @@
| {tab(),integer(),integer(),binary(),list(),integer()}
| {tab(),_,_,integer(),binary(),list(),integer(),integer()}.
-%% a similar definition is also in erl_types
-opaque tid() :: integer().
-%% these ones are also defined in erl_bif_types
-type match_pattern() :: atom() | tuple().
-type match_spec() :: [{match_pattern(), [_], [_]}].
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index 66e54ef221..e6bde5673c 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -516,8 +516,10 @@ pathtype(Atom) when is_atom(Atom) ->
pathtype(atom_to_list(Atom));
pathtype(Name) when is_list(Name) or is_binary(Name) ->
case os:type() of
- {unix, _} -> unix_pathtype(Name);
- {win32, _} -> win32_pathtype(Name)
+ {win32, _} ->
+ win32_pathtype(Name);
+ {_, _} ->
+ unix_pathtype(Name)
end.
unix_pathtype(<<$/,_/binary>>) ->
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 237317ac94..0a26d0182d 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -196,31 +196,32 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Some types.
--export_type([iter/0]).
+-export_type([set/0, set/1, iter/0, iter/1]).
--type gb_set_node() :: 'nil' | {term(), _, _}.
+-type gb_set_node(Element) :: 'nil' | {Element, _, _}.
+-type gb_set_node() :: gb_set_node(_).
+-opaque set(Element) :: {non_neg_integer(), gb_set_node(Element)}.
+-opaque set() :: set(_).
+-opaque iter(Element) :: [gb_set_node(Element)].
-opaque iter() :: [gb_set_node()].
-%% A declaration equivalent to the following is currently hard-coded
-%% in erl_types.erl
-%%
-%% -opaque gb_set() :: {non_neg_integer(), gb_set_node()}.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% gb_sets:set() in OTP 17 only.
+
-spec empty() -> Set when
- Set :: gb_set().
+ Set :: gb_sets:set().
empty() ->
{0, nil}.
-spec new() -> Set when
- Set :: gb_set().
+ Set :: gb_sets:set().
new() -> empty().
-spec is_empty(Set) -> boolean() when
- Set :: gb_set().
+ Set :: gb_sets:set().
is_empty({0, nil}) ->
true;
@@ -228,27 +229,24 @@ is_empty(_) ->
false.
-spec size(Set) -> non_neg_integer() when
- Set :: gb_set().
+ Set :: gb_sets:set().
size({Size, _}) ->
Size.
--spec singleton(Element) -> gb_set() when
- Element :: term().
+-spec singleton(Element) -> set(Element).
singleton(Key) ->
{1, {Key, nil, nil}}.
-spec is_element(Element, Set) -> boolean() when
- Element :: term(),
- Set :: gb_set().
+ Set :: set(Element).
is_element(Key, S) ->
is_member(Key, S).
-spec is_member(Element, Set) -> boolean() when
- Element :: term(),
- Set :: gb_set().
+ Set :: set(Element).
is_member(Key, {_, T}) ->
is_member_1(Key, T).
@@ -263,9 +261,8 @@ is_member_1(_, nil) ->
false.
-spec insert(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
insert(Key, {S, T}) ->
S1 = S + 1,
@@ -322,8 +319,8 @@ count(nil) ->
{1, 0}.
-spec balance(Set1) -> Set2 when
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
balance({S, T}) ->
{S, balance(T, S)}.
@@ -349,17 +346,15 @@ balance_list_1(L, 0) ->
{nil, L}.
-spec add_element(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
add_element(X, S) ->
add(X, S).
-spec add(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
add(X, S) ->
case is_member(X, S) of
@@ -370,32 +365,30 @@ add(X, S) ->
end.
-spec from_list(List) -> Set when
- List :: [term()],
- Set :: gb_set().
+ List :: [Element],
+ Set :: set(Element).
from_list(L) ->
from_ordset(ordsets:from_list(L)).
-spec from_ordset(List) -> Set when
- List :: [term()],
- Set :: gb_set().
+ List :: [Element],
+ Set :: set(Element).
from_ordset(L) ->
S = length(L),
{S, balance_list(L, S)}.
-spec del_element(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
del_element(Key, S) ->
delete_any(Key, S).
-spec delete_any(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
delete_any(Key, S) ->
case is_member(Key, S) of
@@ -406,9 +399,8 @@ delete_any(Key, S) ->
end.
-spec delete(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
delete(Key, {S, T}) ->
{S - 1, delete_1(Key, T)}.
@@ -431,9 +423,8 @@ merge(Smaller, Larger) ->
{Key, Smaller, Larger1}.
-spec take_smallest(Set1) -> {Element, Set2} when
- Set1 :: gb_set(),
- Set2 :: gb_set(),
- Element :: term().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
take_smallest({S, T}) ->
{Key, Larger} = take_smallest1(T),
@@ -445,8 +436,8 @@ take_smallest1({Key, Smaller, Larger}) ->
{Key1, Smaller1} = take_smallest1(Smaller),
{Key1, {Key, Smaller1, Larger}}.
--spec smallest(Set) -> term() when
- Set :: gb_set().
+-spec smallest(Set) -> Element when
+ Set :: set(Element).
smallest({_, T}) ->
smallest_1(T).
@@ -457,9 +448,8 @@ smallest_1({_Key, Smaller, _Larger}) ->
smallest_1(Smaller).
-spec take_largest(Set1) -> {Element, Set2} when
- Set1 :: gb_set(),
- Set2 :: gb_set(),
- Element :: term().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
take_largest({S, T}) ->
{Key, Smaller} = take_largest1(T),
@@ -471,8 +461,8 @@ take_largest1({Key, Smaller, Larger}) ->
{Key1, Larger1} = take_largest1(Larger),
{Key1, {Key, Smaller, Larger1}}.
--spec largest(Set) -> term() when
- Set :: gb_set().
+-spec largest(Set) -> Element when
+ Set :: set(Element).
largest({_, T}) ->
largest_1(T).
@@ -483,8 +473,8 @@ largest_1({_Key, _Smaller, Larger}) ->
largest_1(Larger).
-spec to_list(Set) -> List when
- Set :: gb_set(),
- List :: [term()].
+ Set :: set(Element),
+ List :: [Element].
to_list({_, T}) ->
to_list(T, []).
@@ -496,8 +486,8 @@ to_list({Key, Small, Big}, L) ->
to_list(nil, L) -> L.
-spec iterator(Set) -> Iter when
- Set :: gb_set(),
- Iter :: iter().
+ Set :: set(Element),
+ Iter :: iter(Element).
iterator({_, T}) ->
iterator(T, []).
@@ -513,9 +503,8 @@ iterator(nil, As) ->
As.
-spec next(Iter1) -> {Element, Iter2} | 'none' when
- Iter1 :: iter(),
- Iter2 :: iter(),
- Element :: term().
+ Iter1 :: iter(Element),
+ Iter2 :: iter(Element).
next([{X, _, T} | As]) ->
{X, iterator(T, As)};
@@ -546,9 +535,9 @@ next([]) ->
%% overhead.
-spec union(Set1, Set2) -> Set3 when
- Set1 :: gb_set(),
- Set2 :: gb_set(),
- Set3 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
union({N1, T1}, {N2, T2}) when N2 < N1 ->
union(to_list_1(T2), N2, T1, N1);
@@ -570,7 +559,7 @@ union(L, N1, T2, N2) ->
union_1(L, mk_set(N2, T2))
end.
--spec mk_set(non_neg_integer(), gb_set_node()) -> gb_set().
+-spec mk_set(non_neg_integer(), gb_set_node(T)) -> set(T).
mk_set(N, T) ->
{N, T}.
@@ -651,8 +640,8 @@ balance_revlist_1(L, 0) ->
{nil, L}.
-spec union(SetList) -> Set when
- SetList :: [gb_set(),...],
- Set :: gb_set().
+ SetList :: [set(Element),...],
+ Set :: set(Element).
union([S | Ss]) ->
union_list(S, Ss);
@@ -666,9 +655,9 @@ union_list(S, []) -> S.
%% The rest is modelled on the above.
-spec intersection(Set1, Set2) -> Set3 when
- Set1 :: gb_set(),
- Set2 :: gb_set(),
- Set3 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
intersection({N1, T1}, {N2, T2}) when N2 < N1 ->
intersection(to_list_1(T2), N2, T1, N1);
@@ -717,8 +706,8 @@ intersection_2(_, [], As, S) ->
{S, balance_revlist(As, S)}.
-spec intersection(SetList) -> Set when
- SetList :: [gb_set(),...],
- Set :: gb_set().
+ SetList :: [set(Element),...],
+ Set :: set(Element).
intersection([S | Ss]) ->
intersection_list(S, Ss).
@@ -728,8 +717,8 @@ intersection_list(S, [S1 | Ss]) ->
intersection_list(S, []) -> S.
-spec is_disjoint(Set1, Set2) -> boolean() when
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
is_disjoint({N1, T1}, {N2, T2}) when N1 < N2 ->
is_disjoint_1(T1, T2);
@@ -758,17 +747,17 @@ is_disjoint_1(_, nil) ->
%% traverse the whole element list of the left operand.
-spec subtract(Set1, Set2) -> Set3 when
- Set1 :: gb_set(),
- Set2 :: gb_set(),
- Set3 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
subtract(S1, S2) ->
difference(S1, S2).
-spec difference(Set1, Set2) -> Set3 when
- Set1 :: gb_set(),
- Set2 :: gb_set(),
- Set3 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
difference({N1, T1}, {N2, T2}) ->
difference(to_list_1(T1), N1, T2, N2).
@@ -817,8 +806,8 @@ difference_2(Xs, [], As, S) ->
%% without the construction of a new set.
-spec is_subset(Set1, Set2) -> boolean() when
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
is_subset({N1, T1}, {N2, T2}) ->
is_subset(to_list_1(T1), N1, T2, N2).
@@ -867,20 +856,20 @@ is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true;
is_set(_) -> false.
-spec filter(Pred, Set1) -> Set2 when
- Pred :: fun((E :: term()) -> boolean()),
- Set1 :: gb_set(),
- Set2 :: gb_set().
+ Pred :: fun((Element) -> boolean()),
+ Set1 :: set(Element),
+ Set2 :: set(Element).
filter(F, S) ->
from_ordset([X || X <- to_list(S), F(X)]).
-spec fold(Function, Acc0, Set) -> Acc1 when
- Function :: fun((E :: term(), AccIn) -> AccOut),
- Acc0 :: term(),
- Acc1 :: term(),
- AccIn :: term(),
- AccOut :: term(),
- Set :: gb_set().
+ Function :: fun((Element, AccIn) -> AccOut),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc,
+ Set :: set(Element).
fold(F, A, {_, T}) when is_function(F, 2) ->
fold_1(F, A, T).
diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl
index 7a4dfe1a0b..7069b61873 100644
--- a/lib/stdlib/src/gb_trees.erl
+++ b/lib/stdlib/src/gb_trees.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -152,25 +152,25 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Some types.
--export_type([iter/0]).
+-export_type([tree/0, tree/2, iter/0, iter/2]).
--type gb_tree_node() :: 'nil' | {_, _, _, _}.
+-type gb_tree_node(K, V) :: 'nil'
+ | {K, V, gb_tree_node(K, V), gb_tree_node(K, V)}.
+-type gb_tree_node() :: gb_tree_node(_, _).
+-opaque tree(Key, Value) :: {non_neg_integer(), gb_tree_node(Key, Value)}.
+-opaque tree() :: tree(_, _).
+-opaque iter(Key, Value) :: [gb_tree_node(Key, Value)].
-opaque iter() :: [gb_tree_node()].
-%% A declaration equivalent to the following is currently hard-coded
-%% in erl_types.erl
-%%
-%% -opaque gb_tree() :: {non_neg_integer(), gb_tree_node()}.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec empty() -> gb_tree().
+-spec empty() -> tree().
empty() ->
{0, nil}.
-spec is_empty(Tree) -> boolean() when
- Tree :: gb_tree().
+ Tree :: tree().
is_empty({0, nil}) ->
true;
@@ -178,17 +178,15 @@ is_empty(_) ->
false.
-spec size(Tree) -> non_neg_integer() when
- Tree :: gb_tree().
+ Tree :: tree().
size({Size, _}) when is_integer(Size), Size >= 0 ->
Size.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec lookup(Key, Tree) -> 'none' | {'value', Val} when
- Key :: term(),
- Val :: term(),
- Tree :: gb_tree().
+-spec lookup(Key, Tree) -> 'none' | {'value', Value} when
+ Tree :: tree(Key, Value).
lookup(Key, {_, T}) ->
lookup_1(Key, T).
@@ -214,8 +212,7 @@ lookup_1(_, nil) ->
%% This is a specialized version of `lookup'.
-spec is_defined(Key, Tree) -> boolean() when
- Key :: term(),
- Tree :: gb_tree().
+ Tree :: tree(Key, Value :: term()).
is_defined(Key, {_, T}) ->
is_defined_1(Key, T).
@@ -233,10 +230,8 @@ is_defined_1(_, nil) ->
%% This is a specialized version of `lookup'.
--spec get(Key, Tree) -> Val when
- Key :: term(),
- Tree :: gb_tree(),
- Val :: term().
+-spec get(Key, Tree) -> Value when
+ Tree :: tree(Key, Value).
get(Key, {_, T}) ->
get_1(Key, T).
@@ -250,11 +245,9 @@ get_1(_, {_, Value, _, _}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec update(Key, Val, Tree1) -> Tree2 when
- Key :: term(),
- Val :: term(),
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+-spec update(Key, Value, Tree1) -> Tree2 when
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
update(Key, Val, {S, T}) ->
T1 = update_1(Key, Val, T),
@@ -271,11 +264,9 @@ update_1(Key, Value, {_, _, Smaller, Bigger}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec insert(Key, Val, Tree1) -> Tree2 when
- Key :: term(),
- Val :: term(),
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+-spec insert(Key, Value, Tree1) -> Tree2 when
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
insert(Key, Val, {S, T}) when is_integer(S) ->
S1 = S+1,
@@ -324,11 +315,9 @@ insert_1(Key, _, _, _) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec enter(Key, Val, Tree1) -> Tree2 when
- Key :: term(),
- Val :: term(),
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+-spec enter(Key, Value, Tree1) -> Tree2 when
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
enter(Key, Val, T) ->
case is_defined(Key, T) of
@@ -352,8 +341,8 @@ count(nil) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec balance(Tree1) -> Tree2 when
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
balance({S, T}) ->
{S, balance(T, S)}.
@@ -379,8 +368,8 @@ balance_list_1(L, 0) ->
{nil, L}.
-spec from_orddict(List) -> Tree when
- List :: [{Key :: term(), Val :: term()}],
- Tree :: gb_tree().
+ List :: [{Key, Value}],
+ Tree :: tree(Key, Value).
from_orddict(L) ->
S = length(L),
@@ -389,9 +378,8 @@ from_orddict(L) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec delete_any(Key, Tree1) -> Tree2 when
- Key :: term(),
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
delete_any(Key, T) ->
case is_defined(Key, T) of
@@ -404,9 +392,8 @@ delete_any(Key, T) ->
%%% delete. Assumes that key is present.
-spec delete(Key, Tree1) -> Tree2 when
- Key :: term(),
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
delete(Key, {S, T}) when is_integer(S), S >= 0 ->
{S - 1, delete_1(Key, T)}.
@@ -432,11 +419,9 @@ merge(Smaller, Larger) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec take_smallest(Tree1) -> {Key, Val, Tree2} when
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree(),
- Key :: term(),
- Val :: term().
+-spec take_smallest(Tree1) -> {Key, Value, Tree2} when
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
take_smallest({Size, Tree}) when is_integer(Size), Size >= 0 ->
{Key, Value, Larger} = take_smallest1(Tree),
@@ -448,10 +433,8 @@ take_smallest1({Key, Value, Smaller, Larger}) ->
{Key1, Value1, Smaller1} = take_smallest1(Smaller),
{Key1, Value1, {Key, Value, Smaller1, Larger}}.
--spec smallest(Tree) -> {Key, Val} when
- Tree :: gb_tree(),
- Key :: term(),
- Val :: term().
+-spec smallest(Tree) -> {Key, Value} when
+ Tree :: tree(Key, Value).
smallest({_, Tree}) ->
smallest_1(Tree).
@@ -461,11 +444,9 @@ smallest_1({Key, Value, nil, _Larger}) ->
smallest_1({_Key, _Value, Smaller, _Larger}) ->
smallest_1(Smaller).
--spec take_largest(Tree1) -> {Key, Val, Tree2} when
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree(),
- Key :: term(),
- Val :: term().
+-spec take_largest(Tree1) -> {Key, Value, Tree2} when
+ Tree1 :: tree(Key, Value),
+ Tree2 :: tree(Key, Value).
take_largest({Size, Tree}) when is_integer(Size), Size >= 0 ->
{Key, Value, Smaller} = take_largest1(Tree),
@@ -477,10 +458,8 @@ take_largest1({Key, Value, Smaller, Larger}) ->
{Key1, Value1, Larger1} = take_largest1(Larger),
{Key1, Value1, {Key, Value, Smaller, Larger1}}.
--spec largest(Tree) -> {Key, Val} when
- Tree :: gb_tree(),
- Key :: term(),
- Val :: term().
+-spec largest(Tree) -> {Key, Value} when
+ Tree :: tree(Key, Value).
largest({_, Tree}) ->
largest_1(Tree).
@@ -492,10 +471,8 @@ largest_1({_Key, _Value, _Smaller, Larger}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec to_list(Tree) -> [{Key, Val}] when
- Tree :: gb_tree(),
- Key :: term(),
- Val :: term().
+-spec to_list(Tree) -> [{Key, Value}] when
+ Tree :: tree(Key, Value).
to_list({_, T}) ->
to_list(T, []).
@@ -509,8 +486,7 @@ to_list(nil, L) -> L.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec keys(Tree) -> [Key] when
- Tree :: gb_tree(),
- Key :: term().
+ Tree :: tree(Key, Value :: term()).
keys({_, T}) ->
keys(T, []).
@@ -521,9 +497,8 @@ keys(nil, L) -> L.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec values(Tree) -> [Val] when
- Tree :: gb_tree(),
- Val :: term().
+-spec values(Tree) -> [Value] when
+ Tree :: tree(Key :: term(), Value).
values({_, T}) ->
values(T, []).
@@ -535,8 +510,8 @@ values(nil, L) -> L.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec iterator(Tree) -> Iter when
- Tree :: gb_tree(),
- Iter :: iter().
+ Tree :: tree(Key, Value),
+ Iter :: iter(Key, Value).
iterator({_, T}) ->
iterator_1(T).
@@ -554,11 +529,9 @@ iterator({_, _, L, _} = T, As) ->
iterator(nil, As) ->
As.
--spec next(Iter1) -> 'none' | {Key, Val, Iter2} when
- Iter1 :: iter(),
- Iter2 :: iter(),
- Key :: term(),
- Val :: term().
+-spec next(Iter1) -> 'none' | {Key, Value, Iter2} when
+ Iter1 :: iter(Key, Value),
+ Iter2 :: iter(Key, Value).
next([{X, V, _, T} | As]) ->
{X, V, iterator(T, As)};
@@ -568,9 +541,9 @@ next([]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec map(Function, Tree1) -> Tree2 when
- Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()),
- Tree1 :: gb_tree(),
- Tree2 :: gb_tree().
+ Function :: fun((K :: Key, V1 :: Value1) -> V2 :: Value2),
+ Tree1 :: tree(Key, Value1),
+ Tree2 :: tree(Key, Value2).
map(F, {Size, Tree}) when is_function(F, 2) ->
{Size, map_1(F, Tree)}.
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 7281549ea7..63116fa16e 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -37,7 +37,9 @@
%%-----------------------------------------------------------------
-type linkage() :: 'link' | 'nolink'.
--type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}.
+-type emgr_name() :: {'local', atom()}
+ | {'global', term()}
+ | {'via', Module :: module(), Name :: term()}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 7629e88fbf..d39dd89d3a 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -40,6 +40,8 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
-export_type([handler/0, handler_args/0, add_handler_ret/0,
@@ -229,24 +231,6 @@ wake_hib(Parent, ServerName, MSL, Debug) ->
fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
receive
- {system, From, get_state} ->
- States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {States, [ServerName, MSL, Hib]}, Hib);
- {system, From, {replace_state, StateFun}} ->
- {NMSL, NStates} =
- lists:unzip([begin
- Cur = {Mod,Id,State},
- try
- NState = {Mod,Id,NS} = StateFun(Cur),
- {HS#handler{state=NS}, NState}
- catch
- _:_ ->
- {HS, Cur}
- end
- end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NStates, [ServerName, NMSL, Hib]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[ServerName, MSL, Hib],Hib);
@@ -383,6 +367,23 @@ system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
MSL),
{ok, [ServerName, MSL1, Hib]}.
+system_get_state([_ServerName, MSL, _Hib]) ->
+ {ok, [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL]}.
+
+system_replace_state(StateFun, [ServerName, MSL, Hib]) ->
+ {NMSL, NStates} =
+ lists:unzip([begin
+ Cur = {Mod,Id,State},
+ try
+ NState = {Mod,Id,NS} = StateFun(Cur),
+ {HS#handler{state=NS}, NState}
+ catch
+ _:_ ->
+ {HS, Cur}
+ end
+ end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
+ {ok, NStates, [ServerName, NMSL, Hib]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index e9654322f1..e914f7d0b2 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -118,6 +118,8 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
-import(error_logger, [format/2]).
@@ -422,17 +424,6 @@ wake_hib(Parent, Name, StateName, StateData, Mod, Debug) ->
decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
case Msg of
- {system, From, get_state} ->
- Misc = [Name, StateName, StateData, Mod, Time],
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {{StateName, StateData}, Misc}, Hib);
- {system, From, {replace_state, StateFun}} ->
- State = {StateName, StateData},
- NState = {NStateName, NStateData} = try StateFun(State)
- catch _:_ -> State end,
- NMisc = [Name, NStateName, NStateData, Mod, Time],
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NState, NMisc}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, StateName, StateData, Mod, Time], Hib);
@@ -467,6 +458,13 @@ system_code_change([Name, StateName, StateData, Mod, Time],
Else -> Else
end.
+system_get_state([_Name, StateName, StateData, _Mod, _Time]) ->
+ {ok, {StateName, StateData}}.
+
+system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time]) ->
+ Result = {NStateName, NStateData} = StateFun({StateName, StateData}),
+ {ok, Result, [Name, NStateName, NStateData, Mod, Time]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 5f14e48b0a..202a931fae 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -98,6 +98,8 @@
-export([system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
%% Internal exports
@@ -372,13 +374,6 @@ wake_hib(Parent, Name, State, Mod, Debug) ->
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
- {system, From, get_state} ->
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {State, [Name, State, Mod, Time]}, Hib);
- {system, From, {replace_state, StateFun}} ->
- NState = try StateFun(State) catch _:_ -> State end,
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NState, [Name, NState, Mod, Time]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
@@ -687,6 +682,13 @@ system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
Else -> Else
end.
+system_get_state([_Name, State, _Mod, _Time]) ->
+ {ok, State}.
+
+system_replace_state(StateFun, [Name, State, Mod, Time]) ->
+ NState = StateFun(State),
+ {ok, NState, [Name, NState, Mod, Time]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index b11d41e2eb..27e2a82b41 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -177,13 +177,15 @@ get_password(Io) ->
| {'expand_fun', expand_fun()}
| {'encoding', encoding()}.
--spec getopts() -> [opt_pair()].
+-spec getopts() -> [opt_pair()] | {'error', Reason} when
+ Reason :: term().
getopts() ->
getopts(default_input()).
--spec getopts(IoDevice) -> [opt_pair()] when
- IoDevice :: device().
+-spec getopts(IoDevice) -> [opt_pair()] | {'error', Reason} when
+ IoDevice :: device(),
+ Reason :: term().
getopts(Io) ->
request(Io, getopts).
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index f02a7921f8..aece06afa6 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -64,13 +64,13 @@ print(Term) ->
(term(), options()) -> chars().
print(Term, Options) when is_list(Options) ->
- Col = proplists:get_value(column, Options, 1),
- Ll = proplists:get_value(line_length, Options, 80),
- D = proplists:get_value(depth, Options, -1),
- M = proplists:get_value(max_chars, Options, -1),
- RecDefFun = proplists:get_value(record_print_fun, Options, no_fun),
- Encoding = proplists:get_value(encoding, Options, epp:default_encoding()),
- Strings = proplists:get_value(strings, Options, true),
+ Col = get_option(column, Options, 1),
+ Ll = get_option(line_length, Options, 80),
+ D = get_option(depth, Options, -1),
+ M = get_option(max_chars, Options, -1),
+ RecDefFun = get_option(record_print_fun, Options, no_fun),
+ Encoding = get_option(encoding, Options, epp:default_encoding()),
+ Strings = get_option(strings, Options, true),
print(Term, Col, Ll, D, M, RecDefFun, Encoding, Strings);
print(Term, RecDefFun) ->
print(Term, -1, RecDefFun).
@@ -759,3 +759,10 @@ chars(C, N) when (N band 1) =:= 0 ->
chars(C, N) ->
S = chars(C, N bsr 1),
[C, S | S].
+
+get_option(Key, TupleList, Default) ->
+ case lists:keyfind(Key, 1, TupleList) of
+ false -> Default;
+ {Key, Value} -> Value;
+ _ -> Default
+ end.
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index d6a9f4645d..6303465d3d 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -22,7 +22,7 @@
-compile({no_auto_import,[min/2]}).
-export([append/2, append/1, subtract/2, reverse/1,
- nth/2, nthtail/2, prefix/2, suffix/2, last/1,
+ nth/2, nthtail/2, prefix/2, suffix/2, droplast/1, last/1,
seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
delete/2,
unzip/1, unzip3/1, zip/2, zip3/3, zipwith/3, zipwith3/4,
@@ -203,6 +203,19 @@ suffix(Suffix, List) ->
Delta = length(List) - length(Suffix),
Delta >= 0 andalso nthtail(Delta, List) =:= Suffix.
+%% droplast(List) returns the list dropping its last element
+
+-spec droplast(List) -> InitList when
+ List :: [T, ...],
+ InitList :: [T],
+ T :: term().
+
+%% This is the simple recursive implementation
+%% reverse(tl(reverse(L))) is faster on average,
+%% but creates more garbage.
+droplast([_T]) -> [];
+droplast([H|T]) -> [H|droplast(T)].
+
%% last(List) returns the last element in a list.
-spec last(List) -> Last when
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 41de174e7d..fd6d56fa47 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -43,7 +43,6 @@
values/1
]).
-%% Shadowed by erl_bif_types: maps:get/3
-spec get(Key,Map) -> Value when
Key :: term(),
Map :: map(),
@@ -52,7 +51,6 @@
get(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:find/3
-spec find(Key,Map) -> {ok, Value} | error when
Key :: term(),
Map :: map(),
@@ -61,8 +59,8 @@ get(_,_) -> erlang:nif_error(undef).
find(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:from_list/1
--spec from_list([{Key,Value}]) -> Map when
+-spec from_list(List) -> Map when
+ List :: [{Key,Value}],
Key :: term(),
Value :: term(),
Map :: map().
@@ -70,7 +68,6 @@ find(_,_) -> erlang:nif_error(undef).
from_list(_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:is_key/2
-spec is_key(Key,Map) -> boolean() when
Key :: term(),
Map :: map().
@@ -78,7 +75,6 @@ from_list(_) -> erlang:nif_error(undef).
is_key(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:keys/1
-spec keys(Map) -> Keys when
Map :: map(),
Keys :: [Key],
@@ -87,7 +83,6 @@ is_key(_,_) -> erlang:nif_error(undef).
keys(_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:merge/2
-spec merge(Map1,Map2) -> Map3 when
Map1 :: map(),
Map2 :: map(),
@@ -97,14 +92,12 @@ merge(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:new/0
-spec new() -> Map when
Map :: map().
new() -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:put/3
-spec put(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -114,7 +107,6 @@ new() -> erlang:nif_error(undef).
put(_,_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:put/3
-spec remove(Key,Map1) -> Map2 when
Key :: term(),
Map1 :: map(),
@@ -123,7 +115,6 @@ put(_,_,_) -> erlang:nif_error(undef).
remove(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:to_list/1
-spec to_list(Map) -> [{Key,Value}] when
Map :: map(),
Key :: term(),
@@ -132,7 +123,6 @@ remove(_,_) -> erlang:nif_error(undef).
to_list(_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:update/3
-spec update(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -142,7 +132,6 @@ to_list(_) -> erlang:nif_error(undef).
update(_,_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:values/1
-spec values(Map) -> Keys when
Map :: map(),
Keys :: [Key],
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index cebc9c91bd..971a2e2baa 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -250,12 +250,12 @@ obsolete_1(snmp, N, A) ->
false ->
no;
true ->
- {deprecated, "Deprecated (will be removed in R17B); use snmpa:"++atom_to_list(N)++"/"++
+ {deprecated, "Deprecated (will be removed in OTP 18); use snmpa:"++atom_to_list(N)++"/"++
integer_to_list(A)++" instead"}
end;
obsolete_1(snmpa, old_info_format, 1) ->
- {deprecated, "Deprecated; (will be removed in R17B); use \"new\" format instead"};
+ {deprecated, "Deprecated; (will be removed in OTP 18); use \"new\" format instead"};
obsolete_1(snmpm, agent_info, 3) ->
{removed, {snmpm, agent_info, 2}, "R16B"};
obsolete_1(snmpm, update_agent_info, 5) ->
@@ -366,23 +366,6 @@ obsolete_1(auth, node_cookie, 1) ->
obsolete_1(auth, node_cookie, 2) ->
{deprecated, "Deprecated; use erlang:set_cookie/2 and net_adm:ping/1 instead"};
-obsolete_1(erlang, is_constant, 1) ->
- {removed, "Removed in R13B"};
-
-%% Added in R12B-0.
-obsolete_1(ssl, port, 1) ->
- {removed, {ssl, sockname, 1}, "R13B"};
-obsolete_1(ssl, accept, A) when A =:= 1; A =:= 2 ->
- {removed, "deprecated; use ssl:transport_accept/1,2 and ssl:ssl_accept/1,2"};
-obsolete_1(erlang, fault, 1) ->
- {removed, {erlang,error,1}, "R13B"};
-obsolete_1(erlang, fault, 2) ->
- {removed, {erlang,error,2}, "R13B"};
-
-%% Added in R12B-2.
-obsolete_1(file, rawopen, 2) ->
- {removed, "deprecated (will be removed in R13B); use file:open/2 with the raw option"};
-
obsolete_1(http, request, 1) -> {removed,{httpc,request,1},"R15B"};
obsolete_1(http, request, 2) -> {removed,{httpc,request,2},"R15B"};
obsolete_1(http, request, 4) -> {removed,{httpc,request,4},"R15B"};
@@ -524,7 +507,7 @@ obsolete_1(docb_xml_check, _, _) ->
%% Added in R15B
obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver ->
- {deprecated,"deprecated (will be removed in R16A); has no effect as drivers are no longer used."};
+ {deprecated,"deprecated (will be removed in OTP 18); has no effect as drivers are no longer used"};
obsolete_1(ssl, pid, 1) ->
{removed,"was removed in R16; is no longer needed"};
obsolete_1(inviso, _, _) ->
@@ -532,7 +515,7 @@ obsolete_1(inviso, _, _) ->
%% Added in R15B01.
obsolete_1(gs, _, _) ->
- {deprecated,"the gs application has been deprecated and will be removed in R17; use the wx application instead"};
+ {deprecated,"the gs application has been deprecated and will be removed in OTP 18; use the wx application instead"};
obsolete_1(ssh, sign_data, 2) ->
{deprecated,"deprecated (will be removed in R16A); use public_key:pem_decode/1, public_key:pem_entry_decode/1 "
"and public_key:sign/3 instead"};
@@ -577,6 +560,24 @@ obsolete_1(wxCursor, new, 3) ->
obsolete_1(wxCursor, new, 4) ->
{deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+%% Added in OTP 17.
+obsolete_1(asn1ct, decode,3) ->
+ {deprecated,"deprecated; use Mod:decode/2 instead"};
+obsolete_1(asn1ct, encode, 3) ->
+ {deprecated,"deprecated; use Mod:encode/2 instead"};
+obsolete_1(asn1rt, decode,3) ->
+ {deprecated,"deprecated; use Mod:decode/2 instead"};
+obsolete_1(asn1rt, encode, 2) ->
+ {deprecated,"deprecated; use Mod:encode/2 instead"};
+obsolete_1(asn1rt, encode, 3) ->
+ {deprecated,"deprecated; use Mod:encode/2 instead"};
+obsolete_1(asn1rt, info, 1) ->
+ {deprecated,"deprecated; use Mod:info/0 instead"};
+obsolete_1(asn1rt, utf8_binary_to_list, 1) ->
+ {deprecated,{unicode,characters_to_list,1}};
+obsolete_1(asn1rt, utf8_list_to_binary, 1) ->
+ {deprecated,{unicode,characters_to_binary,1}};
+
obsolete_1(_, _, _) ->
no.
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index c26764eb18..b6bb758dfb 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -1218,13 +1218,14 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
%% column, the filter will not be skipped.
%% (an example: {X=1} <- ..., X =:= 1).
length(D = Cols -- PatternColumns) =:= 1,
- Frame <- SFs,
- begin
+ {{_,Col} = Column, Constants} <- D,
+ %% Check that the following holds for all frames.
+ lists:all(
+ fun(Frame) ->
%% The column is compared/matched against a constant.
%% If there are no more comparisons/matches then
%% the filter can be replaced by the lookup of
%% the constant.
- [{{_,Col} = Column, Constants}] = D,
{VarI, FrameI} = unify_column(Frame, PV, Col, BindFun,
Imported),
VarValues = deref_skip(VarI, FrameI, LookupOp, Imported),
@@ -1253,7 +1254,7 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
length(VarValues) =< 1 andalso
(Constants -- LookedUpConstants =:= []) andalso
bindings_is_subset(Frame, F2, Imported)
- end],
+ end, SFs)],
ColFils = family_list(ColFil),
%% The skip tag 'all' means that all filters are covered by the lookup.
%% It does not imply that there is only one generator as is the case
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index 4bbf5de8a5..472d503b99 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -32,6 +32,8 @@
-export([cons/2,head/1,tail/1,
snoc/2,last/1,daeh/1,init/1,liat/1,lait/1]).
+-export_type([queue/0, queue/1]).
+
%%--------------------------------------------------------------------------
%% Efficient implementation of double ended fifo queues
%%
@@ -44,10 +46,9 @@
%% that is; the RearList is reversed.
%%
-%% A declaration equivalent to the following is currently hard-coded
-%% in erl_types.erl
-%%
-%% -opaque queue() :: {list(), list()}.
+-opaque queue(Item) :: {list(Item), list(Item)}.
+
+-opaque queue() :: queue(_).
%% Creation, inspection and conversion
@@ -79,7 +80,7 @@ len(Q) ->
erlang:error(badarg, [Q]).
%% O(len(Q))
--spec to_list(Q :: queue()) -> list().
+-spec to_list(Q :: queue(Item)) -> list(Item).
to_list({In,Out}) when is_list(In), is_list(Out) ->
Out++lists:reverse(In, []);
to_list(Q) ->
@@ -88,7 +89,7 @@ to_list(Q) ->
%% Create queue from list
%%
%% O(length(L))
--spec from_list(L :: list()) -> queue().
+-spec from_list(L :: list(Item)) -> queue(Item).
from_list(L) when is_list(L) ->
f2r(L);
from_list(L) ->
@@ -97,7 +98,7 @@ from_list(L) ->
%% Return true or false depending on if element is in queue
%%
%% O(length(Q)) worst case
--spec member(Item :: term(), Q :: queue()) -> boolean().
+-spec member(Item, Q :: queue(Item)) -> boolean().
member(X, {R,F}) when is_list(R), is_list(F) ->
lists:member(X, R) orelse lists:member(X, F);
member(X, Q) ->
@@ -110,7 +111,7 @@ member(X, Q) ->
%% Put at least one element in each list, if it is cheap
%%
%% O(1)
--spec in(Item :: term(), Q1 :: queue()) -> Q2 :: queue().
+-spec in(Item, Q1 :: queue(Item)) -> Q2 :: queue(Item).
in(X, {[_]=In,[]}) ->
{[X], In};
in(X, {In,Out}) when is_list(In), is_list(Out) ->
@@ -122,7 +123,7 @@ in(X, Q) ->
%% Put at least one element in each list, if it is cheap
%%
%% O(1)
--spec in_r(Item :: term(), Q1 :: queue()) -> Q2 :: queue().
+-spec in_r(Item, Q1 :: queue(Item)) -> Q2 :: queue(Item).
in_r(X, {[],[_]=F}) ->
{F,[X]};
in_r(X, {R,F}) when is_list(R), is_list(F) ->
@@ -133,9 +134,9 @@ in_r(X, Q) ->
%% Take from head/front
%%
%% O(1) amortized, O(len(Q)) worst case
--spec out(Q1 :: queue()) ->
- {{value, Item :: term()}, Q2 :: queue()} |
- {empty, Q1 :: queue()}.
+-spec out(Q1 :: queue(Item)) ->
+ {{value, Item}, Q2 :: queue(Item)} |
+ {empty, Q1 :: queue(Item)}.
out({[],[]}=Q) ->
{empty,Q};
out({[V],[]}) ->
@@ -153,9 +154,9 @@ out(Q) ->
%% Take from tail/rear
%%
%% O(1) amortized, O(len(Q)) worst case
--spec out_r(Q1 :: queue()) ->
- {{value, Item :: term()}, Q2 :: queue()} |
- {empty, Q1 :: queue()}.
+-spec out_r(Q1 :: queue(Item)) ->
+ {{value, Item}, Q2 :: queue(Item)} |
+ {empty, Q1 :: queue(Item)}.
out_r({[],[]}=Q) ->
{empty,Q};
out_r({[],[V]}) ->
@@ -176,7 +177,7 @@ out_r(Q) ->
%% Return the first element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec get(Q :: queue()) -> Item :: term().
+-spec get(Q :: queue(Item)) -> Item.
get({[],[]}=Q) ->
erlang:error(empty, [Q]);
get({R,F}) when is_list(R), is_list(F) ->
@@ -195,7 +196,7 @@ get([_|R], []) -> % malformed queue -> O(len(Q))
%% Return the last element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec get_r(Q :: queue()) -> Item :: term().
+-spec get_r(Q :: queue(Item)) -> Item.
get_r({[],[]}=Q) ->
erlang:error(empty, [Q]);
get_r({[H|_],F}) when is_list(F) ->
@@ -210,7 +211,7 @@ get_r(Q) ->
%% Return the first element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec peek(Q :: queue()) -> empty | {value,Item :: term()}.
+-spec peek(Q :: queue(Item)) -> empty | {value, Item}.
peek({[],[]}) ->
empty;
peek({R,[H|_]}) when is_list(R) ->
@@ -225,7 +226,7 @@ peek(Q) ->
%% Return the last element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec peek_r(Q :: queue()) -> empty | {value,Item :: term()}.
+-spec peek_r(Q :: queue(Item)) -> empty | {value, Item}.
peek_r({[],[]}) ->
empty;
peek_r({[H|_],F}) when is_list(F) ->
@@ -240,7 +241,7 @@ peek_r(Q) ->
%% Remove the first element and return resulting queue
%%
%% O(1) amortized
--spec drop(Q1 :: queue()) -> Q2 :: queue().
+-spec drop(Q1 :: queue(Item)) -> Q2 :: queue(Item).
drop({[],[]}=Q) ->
erlang:error(empty, [Q]);
drop({[_],[]}) ->
@@ -258,7 +259,7 @@ drop(Q) ->
%% Remove the last element and return resulting queue
%%
%% O(1) amortized
--spec drop_r(Q1 :: queue()) -> Q2 :: queue().
+-spec drop_r(Q1 :: queue(Item)) -> Q2 :: queue(Item).
drop_r({[],[]}=Q) ->
erlang:error(empty, [Q]);
drop_r({[],[_]}) ->
@@ -279,7 +280,7 @@ drop_r(Q) ->
%% Return reversed queue
%%
%% O(1)
--spec reverse(Q1 :: queue()) -> Q2 :: queue().
+-spec reverse(Q1 :: queue(Item)) -> Q2 :: queue(Item).
reverse({R,F}) when is_list(R), is_list(F) ->
{F,R};
reverse(Q) ->
@@ -289,7 +290,7 @@ reverse(Q) ->
%%
%% Q2 empty: O(1)
%% else: O(len(Q1))
--spec join(Q1 :: queue(), Q2 :: queue()) -> Q3 :: queue().
+-spec join(Q1 :: queue(Item), Q2 :: queue(Item)) -> Q3 :: queue(Item).
join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) ->
Q;
join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) ->
@@ -303,8 +304,8 @@ join(Q1, Q2) ->
%%
%% N = 0..len(Q)
%% O(max(N, len(Q)))
--spec split(N :: non_neg_integer(), Q1 :: queue()) ->
- {Q2 :: queue(),Q3 :: queue()}.
+-spec split(N :: non_neg_integer(), Q1 :: queue(Item)) ->
+ {Q2 :: queue(Item),Q3 :: queue(Item)}.
split(0, {R,F}=Q) when is_list(R), is_list(F) ->
{{[],[]},Q};
split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) ->
@@ -345,8 +346,8 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) ->
%%
%% Fun(_) -> List: O(length(List) * len(Q))
%% else: O(len(Q)
--spec filter(Fun, Q1 :: queue()) -> Q2 :: queue() when
- Fun :: fun((Item :: term()) -> boolean() | list()).
+-spec filter(Fun, Q1 :: queue(Item)) -> Q2 :: queue(Item) when
+ Fun :: fun((Item) -> boolean() | list(Item)).
filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) ->
F = filter_f(Fun, F0),
R = filter_r(Fun, R0),
@@ -422,7 +423,7 @@ filter_r(Fun, [X|R0]) ->
%% Cons to head
%%
--spec cons(Item :: term(), Q1 :: queue()) -> Q2 :: queue().
+-spec cons(Item, Q1 :: queue(Item)) -> Q2 :: queue(Item).
cons(X, Q) ->
in_r(X, Q).
@@ -431,7 +432,7 @@ cons(X, Q) ->
%% Return the first element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec head(Q :: queue()) -> Item :: term().
+-spec head(Q :: queue(Item)) -> Item.
head({[],[]}=Q) ->
erlang:error(empty, [Q]);
head({R,F}) when is_list(R), is_list(F) ->
@@ -441,7 +442,7 @@ head(Q) ->
%% Remove head element and return resulting queue
%%
--spec tail(Q1 :: queue()) -> Q2 :: queue().
+-spec tail(Q1 :: queue(Item)) -> Q2 :: queue(Item).
tail(Q) ->
drop(Q).
@@ -449,22 +450,22 @@ tail(Q) ->
%% Cons to tail
%%
--spec snoc(Q1 :: queue(), Item :: term()) -> Q2 :: queue().
+-spec snoc(Q1 :: queue(Item), Item) -> Q2 :: queue(Item).
snoc(Q, X) ->
in(X, Q).
%% Return last element
--spec daeh(Q :: queue()) -> Item :: term().
+-spec daeh(Q :: queue(Item)) -> Item.
daeh(Q) -> get_r(Q).
--spec last(Q :: queue()) -> Item :: term().
+-spec last(Q :: queue(Item)) -> Item.
last(Q) -> get_r(Q).
%% Remove last element and return resulting queue
--spec liat(Q1 :: queue()) -> Q2 :: queue().
+-spec liat(Q1 :: queue(Item)) -> Q2 :: queue(Item).
liat(Q) -> drop_r(Q).
--spec lait(Q1 :: queue()) -> Q2 :: queue().
+-spec lait(Q1 :: queue(Item)) -> Q2 :: queue(Item).
lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one.
--spec init(Q1 :: queue()) -> Q2 :: queue().
+-spec init(Q1 :: queue(Item)) -> Q2 :: queue(Item).
init(Q) -> drop_r(Q).
%%--------------------------------------------------------------------------
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index afc63496d0..7f3cd8f592 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -19,7 +19,6 @@
-module(re).
-export([grun/3,urun/3,ucompile/2,replace/3,replace/4,split/2,split/3]).
-%-opaque mp() :: {re_pattern, _, _, _, _}.
-type mp() :: {re_pattern, _, _, _, _}.
-type nl_spec() :: cr | crlf | lf | anycrlf | any.
diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl
index ebf011a7d9..167a676281 100644
--- a/lib/stdlib/src/sets.erl
+++ b/lib/stdlib/src/sets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. 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
@@ -43,6 +43,8 @@
-export([subtract/2,is_subset/2]).
-export([fold/3,filter/2]).
+-export_type([set/0, set/1]).
+
%% Note: mk_seg/1 must be changed too if seg_size is changed.
-define(seg_size, 16).
-define(max_seg, 32).
@@ -53,8 +55,8 @@
%%------------------------------------------------------------------------------
--type seg() :: tuple().
--type segs() :: tuple().
+-type seg() :: tuple().
+-type segs(_Element) :: tuple().
%% Define a hash set. The default values are the standard ones.
-record(set,
@@ -65,14 +67,12 @@
exp_size=?exp_size :: non_neg_integer(), % Size to expand at
con_size=?con_size :: non_neg_integer(), % Size to contract at
empty :: seg(), % Empty segment
- segs :: segs() % Segments
+ segs :: segs(_) % Segments
}).
-%% A declaration equivalent to the following one is hard-coded in erl_types.
-%% That declaration contains hard-coded information about the #set{}
-%% record and the types of its fields. So, please make sure that any
-%% changes to its structure are also propagated to erl_types.erl.
-%%
-%% -opaque set() :: #set{}.
+
+-opaque set() :: set(_).
+
+-opaque set(Element) :: #set{segs :: segs(Element)}.
%%------------------------------------------------------------------------------
@@ -98,24 +98,23 @@ size(S) -> S#set.size.
%% to_list(Set) -> [Elem].
%% Return the elements in Set as a list.
-spec to_list(Set) -> List when
- Set :: set(),
- List :: [term()].
+ Set :: set(Element),
+ List :: [Element].
to_list(S) ->
fold(fun (Elem, List) -> [Elem|List] end, [], S).
%% from_list([Elem]) -> Set.
%% Build a set from the elements in List.
-spec from_list(List) -> Set when
- List :: [term()],
- Set :: set().
+ List :: [Element],
+ Set :: set(Element).
from_list(L) ->
lists:foldl(fun (E, S) -> add_element(E, S) end, new(), L).
%% is_element(Element, Set) -> boolean().
%% Return 'true' if Element is an element of Set, else 'false'.
-spec is_element(Element, Set) -> boolean() when
- Element :: term(),
- Set :: set().
+ Set :: set(Element).
is_element(E, S) ->
Slot = get_slot(S, E),
Bkt = get_bucket(S, Slot),
@@ -124,9 +123,8 @@ is_element(E, S) ->
%% add_element(Element, Set) -> Set.
%% Return Set with Element inserted in it.
-spec add_element(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: set(),
- Set2 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
add_element(E, S0) ->
Slot = get_slot(S0, E),
{S1,Ic} = on_bucket(fun (B0) -> add_bkt_el(E, B0, B0) end, S0, Slot),
@@ -141,9 +139,8 @@ add_bkt_el(E, [], Bkt) -> {[E|Bkt],1}.
%% del_element(Element, Set) -> Set.
%% Return Set but with Element removed.
-spec del_element(Element, Set1) -> Set2 when
- Element :: term(),
- Set1 :: set(),
- Set2 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
del_element(E, S0) ->
Slot = get_slot(S0, E),
{S1,Dc} = on_bucket(fun (B0) -> del_bkt_el(E, B0) end, S0, Slot),
@@ -159,9 +156,9 @@ del_bkt_el(_, []) -> {[],0}.
%% union(Set1, Set2) -> Set
%% Return the union of Set1 and Set2.
-spec union(Set1, Set2) -> Set3 when
- Set1 :: set(),
- Set2 :: set(),
- Set3 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
union(S1, S2) when S1#set.size < S2#set.size ->
fold(fun (E, S) -> add_element(E, S) end, S2, S1);
union(S1, S2) ->
@@ -170,14 +167,14 @@ union(S1, S2) ->
%% union([Set]) -> Set
%% Return the union of the list of sets.
-spec union(SetList) -> Set when
- SetList :: [set()],
- Set :: set().
+ SetList :: [set(Element)],
+ Set :: set(Element).
union([S1,S2|Ss]) ->
union1(union(S1, S2), Ss);
union([S]) -> S;
union([]) -> new().
--spec union1(set(), [set()]) -> set().
+-spec union1(set(E), [set(E)]) -> set(E).
union1(S1, [S2|Ss]) ->
union1(union(S1, S2), Ss);
union1(S1, []) -> S1.
@@ -185,9 +182,9 @@ union1(S1, []) -> S1.
%% intersection(Set1, Set2) -> Set.
%% Return the intersection of Set1 and Set2.
-spec intersection(Set1, Set2) -> Set3 when
- Set1 :: set(),
- Set2 :: set(),
- Set3 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
intersection(S1, S2) when S1#set.size < S2#set.size ->
filter(fun (E) -> is_element(E, S2) end, S1);
intersection(S1, S2) ->
@@ -196,13 +193,13 @@ intersection(S1, S2) ->
%% intersection([Set]) -> Set.
%% Return the intersection of the list of sets.
-spec intersection(SetList) -> Set when
- SetList :: [set(),...],
- Set :: set().
+ SetList :: [set(Element),...],
+ Set :: set(Element).
intersection([S1,S2|Ss]) ->
intersection1(intersection(S1, S2), Ss);
intersection([S]) -> S.
--spec intersection1(set(), [set()]) -> set().
+-spec intersection1(set(E), [set(E)]) -> set(E).
intersection1(S1, [S2|Ss]) ->
intersection1(intersection(S1, S2), Ss);
intersection1(S1, []) -> S1.
@@ -210,8 +207,8 @@ intersection1(S1, []) -> S1.
%% is_disjoint(Set1, Set2) -> boolean().
%% Check whether Set1 and Set2 are disjoint.
-spec is_disjoint(Set1, Set2) -> boolean() when
- Set1 :: set(),
- Set2 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
is_disjoint(S1, S2) when S1#set.size < S2#set.size ->
fold(fun (_, false) -> false;
(E, true) -> not is_element(E, S2)
@@ -225,9 +222,9 @@ is_disjoint(S1, S2) ->
%% Return all and only the elements of Set1 which are not also in
%% Set2.
-spec subtract(Set1, Set2) -> Set3 when
- Set1 :: set(),
- Set2 :: set(),
- Set3 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element),
+ Set3 :: set(Element).
subtract(S1, S2) ->
filter(fun (E) -> not is_element(E, S2) end, S1).
@@ -235,34 +232,34 @@ subtract(S1, S2) ->
%% Return 'true' when every element of Set1 is also a member of
%% Set2, else 'false'.
-spec is_subset(Set1, Set2) -> boolean() when
- Set1 :: set(),
- Set2 :: set().
+ Set1 :: set(Element),
+ Set2 :: set(Element).
is_subset(S1, S2) ->
fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1).
%% fold(Fun, Accumulator, Set) -> Accumulator.
%% Fold function Fun over all elements in Set and return Accumulator.
-spec fold(Function, Acc0, Set) -> Acc1 when
- Function :: fun((E :: term(),AccIn) -> AccOut),
- Set :: set(),
- Acc0 :: T,
- Acc1 :: T,
- AccIn :: T,
- AccOut :: T.
+ Function :: fun((Element, AccIn) -> AccOut),
+ Set :: set(Element),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc.
fold(F, Acc, D) -> fold_set(F, Acc, D).
%% filter(Fun, Set) -> Set.
%% Filter Set with Fun.
-spec filter(Pred, Set1) -> Set2 when
- Pred :: fun((E :: term()) -> boolean()),
- Set1 :: set(),
- Set2 :: set().
+ Pred :: fun((Element) -> boolean()),
+ Set1 :: set(Element),
+ Set2 :: set(Element).
filter(F, D) -> filter_set(F, D).
%% get_slot(Hashdb, Key) -> Slot.
%% Get the slot. First hash on the new range, if we hit a bucket
%% which has not been split use the unsplit buddy bucket.
--spec get_slot(set(), term()) -> non_neg_integer().
+-spec get_slot(set(E), E) -> non_neg_integer().
get_slot(T, Key) ->
H = erlang:phash(Key, T#set.maxn),
if
@@ -276,8 +273,8 @@ get_bucket(T, Slot) -> get_bucket_s(T#set.segs, Slot).
%% on_bucket(Fun, Hashdb, Slot) -> {NewHashDb,Result}.
%% Apply Fun to the bucket in Slot and replace the returned bucket.
--spec on_bucket(fun((_) -> {[_], 0 | 1}), set(), non_neg_integer()) ->
- {set(), 0 | 1}.
+-spec on_bucket(fun((_) -> {[_], 0 | 1}), set(E), non_neg_integer()) ->
+ {set(E), 0 | 1}.
on_bucket(F, T, Slot) ->
SegI = ((Slot-1) div ?seg_size) + 1,
BktI = ((Slot-1) rem ?seg_size) + 1,
@@ -351,7 +348,7 @@ put_bucket_s(Segs, Slot, Bkt) ->
Seg = setelement(BktI, element(SegI, Segs), Bkt),
setelement(SegI, Segs, Seg).
--spec maybe_expand(set(), 0 | 1) -> set().
+-spec maybe_expand(set(E), 0 | 1) -> set(E).
maybe_expand(T0, Ic) when T0#set.size + Ic > T0#set.exp_size ->
T = maybe_expand_segs(T0), %Do we need more segments.
N = T#set.n + 1, %Next slot to expand into
@@ -369,14 +366,14 @@ maybe_expand(T0, Ic) when T0#set.size + Ic > T0#set.exp_size ->
segs = Segs2};
maybe_expand(T, Ic) -> T#set{size = T#set.size + Ic}.
--spec maybe_expand_segs(set()) -> set().
+-spec maybe_expand_segs(set(E)) -> set(E).
maybe_expand_segs(T) when T#set.n =:= T#set.maxn ->
T#set{maxn = 2 * T#set.maxn,
bso = 2 * T#set.bso,
segs = expand_segs(T#set.segs, T#set.empty)};
maybe_expand_segs(T) -> T.
--spec maybe_contract(set(), non_neg_integer()) -> set().
+-spec maybe_contract(set(E), non_neg_integer()) -> set(E).
maybe_contract(T, Dc) when T#set.size - Dc < T#set.con_size,
T#set.n > ?seg_size ->
N = T#set.n,
@@ -395,7 +392,7 @@ maybe_contract(T, Dc) when T#set.size - Dc < T#set.con_size,
segs = Segs2});
maybe_contract(T, Dc) -> T#set{size = T#set.size - Dc}.
--spec maybe_contract_segs(set()) -> set().
+-spec maybe_contract_segs(set(E)) -> set(E).
maybe_contract_segs(T) when T#set.n =:= T#set.bso ->
T#set{maxn = T#set.maxn div 2,
bso = T#set.bso div 2,
@@ -422,7 +419,7 @@ mk_seg(16) -> {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}.
%% of segments. We special case the powers of 2 upto 32, this should
%% catch most case. N.B. the last element in the segments tuple is
%% an extra element containing a default empty segment.
--spec expand_segs(segs(), seg()) -> segs().
+-spec expand_segs(segs(E), seg()) -> segs(E).
expand_segs({B1}, Empty) ->
{B1,Empty};
expand_segs({B1,B2}, Empty) ->
@@ -440,7 +437,7 @@ expand_segs(Segs, Empty) ->
list_to_tuple(tuple_to_list(Segs)
++ lists:duplicate(tuple_size(Segs), Empty)).
--spec contract_segs(segs()) -> segs().
+-spec contract_segs(segs(E)) -> segs(E).
contract_segs({B1,_}) ->
{B1};
contract_segs({B1,B2,_,_}) ->
diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl
index 3e647635bc..1898dc8aba 100644
--- a/lib/stdlib/src/slave.erl
+++ b/lib/stdlib/src/slave.erl
@@ -290,7 +290,10 @@ register_unique_name(Number) ->
%% no need to use rsh.
mk_cmd(Host, Name, Args, Waiter, Prog0) ->
- Prog = quote_progname(Prog0),
+ Prog = case os:type() of
+ {ose,_} -> mk_ose_prog(Prog0);
+ _ -> quote_progname(Prog0)
+ end,
BasicCmd = lists:concat([Prog,
" -detached -noinput -master ", node(),
" ", long_or_short(), Name, "@", Host,
@@ -310,6 +313,24 @@ mk_cmd(Host, Name, Args, Waiter, Prog0) ->
end
end.
+%% On OSE we have to pass the beam arguments directory to the slave
+%% process. To find out what arguments that should be passed on we
+%% make an assumption. All arguments after the last "--" should be
+%% skipped. So given these arguments:
+%% -Muycs256 -A 1 -- -root /mst/ -progname beam.debug.smp -- -home /mst/ -- -kernel inetrc '"/mst/inetrc.conf"' -- -name test@localhost
+%% we send
+%% -Muycs256 -A 1 -- -root /mst/ -progname beam.debug.smp -- -home /mst/ -- -kernel inetrc '"/mst/inetrc.conf"' --
+%% to the slave with whatever other args that are added in mk_cmd.
+mk_ose_prog(Prog) ->
+ SkipTail = fun("--",[]) ->
+ ["--"];
+ (_,[]) ->
+ [];
+ (Arg,Args) ->
+ [Arg," "|Args]
+ end,
+ [Prog,tl(lists:foldr(SkipTail,[],erlang:system_info(emu_args)))].
+
%% This is an attempt to distinguish between spaces in the program
%% path and spaces that separate arguments. The program is quoted to
%% allow spaces in the path.
diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl
index 34eb224647..0bd67db100 100644
--- a/lib/stdlib/src/sofs.erl
+++ b/lib/stdlib/src/sofs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -1509,7 +1509,7 @@ family_projection(SetFun, F) when ?IS_SET(F) ->
%%%
-spec(family_to_digraph(Family) -> Graph when
- Graph :: digraph(),
+ Graph :: digraph:graph(),
Family :: family()).
family_to_digraph(F) when ?IS_SET(F) ->
case ?TYPE(F) of
@@ -1519,7 +1519,7 @@ family_to_digraph(F) when ?IS_SET(F) ->
end.
-spec(family_to_digraph(Family, GraphType) -> Graph when
- Graph :: digraph(),
+ Graph :: digraph:graph(),
Family :: family(),
GraphType :: [digraph:d_type()]).
family_to_digraph(F, Type) when ?IS_SET(F) ->
@@ -1541,7 +1541,7 @@ family_to_digraph(F, Type) when ?IS_SET(F) ->
end.
-spec(digraph_to_family(Graph) -> Family when
- Graph :: digraph(),
+ Graph :: digraph:graph(),
Family :: family()).
digraph_to_family(G) ->
case catch digraph_family(G) of
@@ -1550,7 +1550,7 @@ digraph_to_family(G) ->
end.
-spec(digraph_to_family(Graph, Type) -> Family when
- Graph :: digraph(),
+ Graph :: digraph:graph(),
Family :: family(),
Type :: type()).
digraph_to_family(G, T) ->
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index a64b8e13c0..d388410de0 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -102,5 +102,8 @@
{registered,[timer_server,rsh_starter,take_over_monitor,pool_master,
dets]},
{applications, [kernel]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["sasl-2.4","kernel-3.0","erts-6.0","crypto-3.3",
+ "compiler-5.0"]}
+]}.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 749a9a4201..22eefb2514 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -16,12 +16,10 @@
%%
%% %CopyrightEnd%
{"%VSN%",
- %% Up from - max two major revisions back
+ %% Up from - max one major revision back
[{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
- {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
- {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15
- %% Down to - max two major revisions back
+ {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16
+ %% Down to - max one major revision back
[{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
- {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
- {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15
+ {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16
}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index d18387568d..ede2742875 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -45,10 +45,13 @@
-type restart() :: 'permanent' | 'transient' | 'temporary'.
-type shutdown() :: 'brutal_kill' | timeout().
-type worker() :: 'worker' | 'supervisor'.
--type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}.
+-type sup_name() :: {'local', Name :: atom()}
+ | {'global', Name :: atom()}
+ | {'via', Module :: module(), Name :: any()}.
-type sup_ref() :: (Name :: atom())
| {Name :: atom(), Node :: node()}
| {'global', Name :: atom()}
+ | {'via', Module :: module(), Name :: any()}
| pid().
-type child_spec() :: {Id :: child_id(),
StartFunc :: mfargs(),
@@ -74,14 +77,15 @@
modules = [] :: modules()}).
-type child_rec() :: #child{}.
--define(DICT, dict).
+-define(DICTS, dict).
+-define(DICT, dict:dict).
-define(SETS, sets).
--define(SET, set).
+-define(SET, sets:set).
-record(state, {name,
strategy :: strategy(),
children = [] :: [child_rec()],
- dynamics :: ?DICT() | ?SET(),
+ dynamics :: ?DICT(pid(), list()) | ?SET(pid()),
intensity :: non_neg_integer(),
period :: pos_integer(),
restarts = [],
@@ -441,7 +445,7 @@ handle_call(which_children, _From, #state{children = [#child{restart_type = RTyp
State) when ?is_simple(State) ->
Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods};
({Pid, _}) -> {undefined, Pid, CT, Mods} end,
- ?DICT:to_list(dynamics_db(RType, State#state.dynamics))),
+ ?DICTS:to_list(dynamics_db(RType, State#state.dynamics))),
{reply, Reply, State};
handle_call(which_children, _From, State) ->
@@ -480,7 +484,7 @@ handle_call(count_children, _From, #state{children = [#child{restart_type = RTy
child_type = CT}]} = State)
when ?is_simple(State) ->
{Active, Count} =
- ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) ->
+ ?DICTS:fold(fun(Pid, _Val, {Alive, Tot}) ->
case is_pid(Pid) andalso is_process_alive(Pid) of
true ->
{Alive+1, Tot +1};
@@ -774,17 +778,17 @@ restart(Child, State) ->
restart(simple_one_for_one, Child, State) ->
#child{pid = OldPid, mfargs = {M, F, A}} = Child,
- Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type,
+ Dynamics = ?DICTS:erase(OldPid, dynamics_db(Child#child.restart_type,
State#state.dynamics)),
case do_start_child_i(M, F, A) of
{ok, Pid} ->
- NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
+ NState = State#state{dynamics = ?DICTS:store(Pid, A, Dynamics)},
{ok, NState};
{ok, Pid, _Extra} ->
- NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
+ NState = State#state{dynamics = ?DICTS:store(Pid, A, Dynamics)},
{ok, NState};
{error, Error} ->
- NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A,
+ NState = State#state{dynamics = ?DICTS:store(restarting(OldPid), A,
Dynamics)},
report_error(start_error, Error, Child, State#state.name),
{try_again, NState}
@@ -977,7 +981,7 @@ terminate_dynamic_children(Child, Dynamics, SupName) ->
wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
end,
%% Unroll stacked errors and report them
- ?DICT:fold(fun(Reason, Ls, _) ->
+ ?DICTS:fold(fun(Reason, Ls, _) ->
report_error(shutdown_error, Reason,
Child#child{pid=Ls}, SupName)
end, ok, EStack).
@@ -991,22 +995,22 @@ monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) ->
{error, normal} ->
{Pids, EStack};
{error, Reason} ->
- {Pids, ?DICT:append(Reason, P, EStack)}
+ {Pids, ?DICTS:append(Reason, P, EStack)}
end
- end, {?SETS:new(), ?DICT:new()}, Dynamics);
+ end, {?SETS:new(), ?DICTS:new()}, Dynamics);
monitor_dynamic_children(#child{restart_type=RType}, Dynamics) ->
- ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) ->
+ ?DICTS:fold(fun(P, _, {Pids, EStack}) when is_pid(P) ->
case monitor_child(P) of
ok ->
{?SETS:add_element(P, Pids), EStack};
{error, normal} when RType =/= permanent ->
{Pids, EStack};
{error, Reason} ->
- {Pids, ?DICT:append(Reason, P, EStack)}
+ {Pids, ?DICTS:append(Reason, P, EStack)}
end;
(?restarting(_), _, {Pids, EStack}) ->
{Pids, EStack}
- end, {?SETS:new(), ?DICT:new()}, Dynamics).
+ end, {?SETS:new(), ?DICTS:new()}, Dynamics).
wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) ->
@@ -1030,7 +1034,7 @@ wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz,
{'DOWN', _MRef, process, Pid, Reason} ->
wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, ?DICT:append(Reason, Pid, EStack))
+ TRef, ?DICTS:append(Reason, Pid, EStack))
end;
wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
TRef, EStack) ->
@@ -1045,7 +1049,7 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
{'DOWN', _MRef, process, Pid, Reason} ->
wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, ?DICT:append(Reason, Pid, EStack));
+ TRef, ?DICTS:append(Reason, Pid, EStack));
{timeout, TRef, kill} ->
?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
@@ -1070,12 +1074,12 @@ save_child(Child, #state{children = Children} = State) ->
save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) ->
State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))};
save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) ->
- State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}.
+ State#state{dynamics = ?DICTS:store(Pid, Args, dynamics_db(RestartType, Dynamics))}.
dynamics_db(temporary, undefined) ->
?SETS:new();
dynamics_db(_, undefined) ->
- ?DICT:new();
+ ?DICTS:new();
dynamics_db(_,Dynamics) ->
Dynamics.
@@ -1084,14 +1088,14 @@ dynamic_child_args(Pid, Dynamics) ->
true ->
{ok, undefined};
false ->
- ?DICT:find(Pid, Dynamics)
+ ?DICTS:find(Pid, Dynamics)
end.
state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) ->
NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)),
State#state{dynamics = NDynamics};
state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) ->
- NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)),
+ NDynamics = ?DICTS:erase(Pid, dynamics_db(RType, State#state.dynamics)),
State#state{dynamics = NDynamics};
state_del_child(Child, State) ->
NChildren = del_child(Child#child.name, State#state.children),
@@ -1154,7 +1158,7 @@ is_dynamic_pid(Pid, Dynamics) ->
true ->
?SETS:is_element(Pid, Dynamics);
false ->
- ?DICT:is_key(Pid, Dynamics)
+ ?DICTS:is_key(Pid, Dynamics)
end.
replace_child(Child, State) ->
diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl
index e8405ab9a4..ff4502f0b9 100644
--- a/lib/stdlib/src/supervisor_bridge.erl
+++ b/lib/stdlib/src/supervisor_bridge.erl
@@ -101,7 +101,16 @@ handle_cast(_, State) ->
{noreply, State}.
handle_info({'EXIT', Pid, Reason}, State) when State#state.pid =:= Pid ->
- report_error(child_terminated, Reason, State),
+ case Reason of
+ normal ->
+ ok;
+ shutdown ->
+ ok;
+ {shutdown, _Term} ->
+ ok;
+ _ ->
+ report_error(child_terminated, Reason, State)
+ end,
{stop, Reason, State#state{pid = undefined}};
handle_info(_, State) ->
{noreply, State}.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 04f8dfb61b..e25cc25f57 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -102,20 +102,31 @@ get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
-spec get_state(Name) -> State when
Name :: name(),
State :: term().
-get_state(Name) -> send_system_msg(Name, get_state).
+get_state(Name) ->
+ case send_system_msg(Name, get_state) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec get_state(Name, Timeout) -> State when
Name :: name(),
Timeout :: timeout(),
State :: term().
-get_state(Name, Timeout) -> send_system_msg(Name, get_state, Timeout).
+get_state(Name, Timeout) ->
+ case send_system_msg(Name, get_state, Timeout) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec replace_state(Name, StateFun) -> NewState when
Name :: name(),
StateFun :: fun((State :: term()) -> NewState :: term()),
NewState :: term().
replace_state(Name, StateFun) ->
- send_system_msg(Name, {replace_state, StateFun}).
+ case send_system_msg(Name, {replace_state, StateFun}) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec replace_state(Name, StateFun, Timeout) -> NewState when
Name :: name(),
@@ -123,7 +134,10 @@ replace_state(Name, StateFun) ->
Timeout :: timeout(),
NewState :: term().
replace_state(Name, StateFun, Timeout) ->
- send_system_msg(Name, {replace_state, StateFun}, Timeout).
+ case send_system_msg(Name, {replace_state, StateFun}, Timeout) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when
Name :: name(),
@@ -390,10 +404,11 @@ do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
{suspended, ok, Debug, Misc};
do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
{running, ok, Debug, Misc};
-do_cmd(SysState, get_state, _Parent, _Mod, Debug, {State, Misc}) ->
- {SysState, State, Debug, Misc};
-do_cmd(SysState, replace_state, _Parent, _Mod, Debug, {State, Misc}) ->
- {SysState, State, Debug, Misc};
+do_cmd(SysState, get_state, _Parent, Mod, Debug, Misc) ->
+ {SysState, do_get_state(Mod, Misc), Debug, Misc};
+do_cmd(SysState, {replace_state, StateFun}, _Parent, Mod, Debug, Misc) ->
+ {Res, NMisc} = do_replace_state(StateFun, Mod, Misc),
+ {SysState, Res, Debug, NMisc};
do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
Res = get_status(SysState, Parent, Mod, Debug, Misc),
{SysState, Res, Debug, Misc};
@@ -407,6 +422,40 @@ do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) ->
{SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}.
+do_get_state(Mod, Misc) ->
+ case erlang:function_exported(Mod, system_get_state, 1) of
+ true ->
+ try
+ {ok, State} = Mod:system_get_state(Misc),
+ State
+ catch
+ Cl:Exc ->
+ {error, {callback_failed,{Mod,system_get_state},{Cl,Exc}}}
+ end;
+ false ->
+ Misc
+ end.
+
+do_replace_state(StateFun, Mod, Misc) ->
+ case erlang:function_exported(Mod, system_replace_state, 2) of
+ true ->
+ try
+ {ok, State, NMisc} = Mod:system_replace_state(StateFun, Misc),
+ {State, NMisc}
+ catch
+ Cl:Exc ->
+ {{error, {callback_failed,{Mod,system_replace_state},{Cl,Exc}}}, Misc}
+ end;
+ false ->
+ try
+ NMisc = StateFun(Misc),
+ {NMisc, NMisc}
+ catch
+ Cl:Exc ->
+ {{error, {callback_failed,StateFun,{Cl,Exc}}}, Misc}
+ end
+ end.
+
get_status(SysState, Parent, Mod, Debug, Misc) ->
PDict = get(),
FmtMisc =
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index c40ce8e203..b768c6d0b9 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -203,8 +203,18 @@
zip_comment_length}).
--type zip_file() :: #zip_file{}.
+-type create_option() :: memory | cooked | verbose | {comment, string()}
+ | {cwd, file:filename()}
+ | {compress, extension_spec()}
+ | {uncompress, extension_spec()}.
+-type extension() :: string().
+-type extension_spec() :: all | [extension()] | {add, [extension()]} | {del, [extension()]}.
+-type filename() :: file:filename().
+
-type zip_comment() :: #zip_comment{}.
+-type zip_file() :: #zip_file{}.
+
+-export_type([create_option/0, filename/0]).
%% Open a zip archive with options
%%
@@ -340,13 +350,13 @@ unzip(F) -> unzip(F, []).
-spec(unzip(Archive, Options) -> RetValue when
Archive :: file:name() | binary(),
Options :: [Option],
- Option :: {file_list, FileList}
+ Option :: {file_list, FileList} | cooked
| keep_old_files | verbose | memory |
{file_filter, FileFilter} | {cwd, CWD},
FileList :: [file:name()],
FileBinList :: [{file:name(),binary()}],
FileFilter :: fun((ZipFile) -> boolean()),
- CWD :: string(),
+ CWD :: file:filename(),
ZipFile :: zip_file(),
RetValue :: {ok, FileList}
| {ok, FileBinList}
@@ -430,7 +440,7 @@ zip(F, Files) -> zip(F, Files, []).
What :: all | [Extension] | {add, [Extension]} | {del, [Extension]},
Extension :: string(),
Comment :: string(),
- CWD :: string(),
+ CWD :: file:filename(),
RetValue :: {ok, FileName :: file:name()}
| {ok, {FileName :: file:name(), binary()}}
| {error, Reason :: term()}).
@@ -712,8 +722,8 @@ table(F, O) -> list_dir(F, O).
FileList :: [FileSpec],
FileSpec :: file:name() | {file:name(), binary()}
| {file:name(), binary(), file:file_info()},
- RetValue :: {ok, FileName :: file:name()}
- | {ok, {FileName :: file:name(), binary()}}
+ RetValue :: {ok, FileName :: filename()}
+ | {ok, {FileName :: filename(), binary()}}
| {error, Reason :: term()}).
create(F, Fs) -> zip(F, Fs).
@@ -724,14 +734,9 @@ create(F, Fs) -> zip(F, Fs).
FileSpec :: file:name() | {file:name(), binary()}
| {file:name(), binary(), file:file_info()},
Options :: [Option],
- Option :: memory | cooked | verbose | {comment, Comment}
- | {cwd, CWD} | {compress, What} | {uncompress, What},
- What :: all | [Extension] | {add, [Extension]} | {del, [Extension]},
- Extension :: string(),
- Comment :: string(),
- CWD :: string(),
- RetValue :: {ok, FileName :: file:name()}
- | {ok, {FileName :: file:name(), binary()}}
+ Option :: create_option(),
+ RetValue :: {ok, FileName :: filename()}
+ | {ok, {FileName :: filename(), binary()}}
| {error, Reason :: term()}).
create(F, Fs, O) -> zip(F, Fs, O).
@@ -755,7 +760,7 @@ extract(F) -> unzip(F).
FileList :: [file:name()],
FileBinList :: [{file:name(),binary()}],
FileFilter :: fun((ZipFile) -> boolean()),
- CWD :: string(),
+ CWD :: file:filename(),
ZipFile :: zip_file(),
RetValue :: {ok, FileList}
| {ok, FileBinList}
@@ -1153,7 +1158,7 @@ zip_open(Archive) -> zip_open(Archive, []).
Archive :: file:name() | binary(),
ZipHandle :: pid(),
Options :: [Option],
- Option :: cooked | memory | {cwd, CWD :: string()},
+ Option :: cooked | memory | {cwd, CWD :: file:filename()},
Reason :: term()).
zip_open(Archive, Options) ->
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index af82f22b21..39f6ce423a 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -73,6 +73,8 @@ MODULES= \
supervisor_SUITE \
supervisor_bridge_SUITE \
sys_SUITE \
+ sys_sp1 \
+ sys_sp2 \
tar_SUITE \
timer_SUITE \
timer_simple_SUITE \
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index d5a0fe21b4..32cec0db6f 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -40,7 +40,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
% Some of these testcases are really heavy...
--define(default_timeout, ?t:minutes(20)).
+-define(default_timeout, ?t:minutes(30)).
-endif.
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 059d553b00..6be37cbecf 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -52,7 +52,7 @@
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
- otp_8923/1, otp_9282/1, otp_11245/1]).
+ otp_8923/1, otp_9282/1, otp_11245/1, otp_11709/1]).
-export([dets_dirty_loop/0]).
@@ -109,7 +109,7 @@ all() ->
many_clients, otp_4906, otp_5402, simultaneous_open,
insert_new, repair_continuation, otp_5487, otp_6206,
otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282, otp_11245
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_11245, otp_11709
].
groups() ->
@@ -772,9 +772,9 @@ open_1(Config, V) ->
crash(Fname, TypePos),
{error, {invalid_type_code,Fname}} = dets:open_file(Fname),
truncate(Fname, HeadSize - 10),
- {error, {tooshort,Fname}} = dets:open_file(Fname),
- {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
- ok = dets:close(TabRef),
+ {error,{not_a_dets_file,Fname}} = dets:open_file(Fname),
+ {error,{not_a_dets_file,Fname}} =
+ dets:open_file(TabRef, [{file,Fname},{version,V}]),
file:delete(Fname),
{error,{file_error,{foo,bar},_}} = dets:is_dets_file({foo,bar}),
@@ -967,10 +967,12 @@ fast_init_table(Config) ->
{'EXIT', _} =
(catch dets:init_table(TabRef, fun(foo) -> bar end, {format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end,
{format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{'EXIT', {badarg, _}} =
(catch dets:init_table(TabRef, nofun, {format,bchunk})),
@@ -979,10 +981,12 @@ fast_init_table(Config) ->
away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end,
{format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{error, {init_fun, fopp}} =
dets:init_table(TabRef, fun(read) -> fopp end, {format,bchunk}),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
dets:safe_fixtable(TabRef, true),
{error, {fixed_table, TabRef}} =
@@ -1389,23 +1393,6 @@ repair(Config, V) ->
{ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
ok = ins(TabRef, 100),
ok = dets:close(TabRef),
- truncate(Fname, HeadSize - 10),
- %% a new file is created ('tooshort')
- {ok, TabRef} = dets:open_file(TabRef,
- [{file,Fname},{version,V},
- {min_no_slots,1000},
- {max_no_slots,1000000}]),
- case dets:info(TabRef, no_slots) of
- undefined -> ok;
- {Min1,Slot1,Max1} ->
- true = Min1 =< Slot1, true = Slot1 =< Max1,
- true = 1000 < Min1, true = 1000+256 > Min1,
- true = 1000000 < Max1, true = (1 bsl 20)+256 > Max1
- end,
- 0 = dets:info(TabRef, size),
- no_keys_test(TabRef),
- _ = histogram(TabRef, silent),
- ok = dets:close(TabRef),
file:delete(Fname),
%% version bump (v8)
@@ -3920,19 +3907,52 @@ otp_11245(Config) when is_list(Config) ->
file:delete(File),
ok.
+otp_11709(doc) ->
+ ["OTP-11709. Bugfixes."];
+otp_11709(suite) ->
+ [];
+otp_11709(Config) when is_list(Config) ->
+ Short = <<"foo">>,
+ Long = <<"a sufficiently long text">>,
+
+ %% Bug: leaking file descriptor
+ P0 = pps(),
+ File = filename(otp_11709, Config),
+ ok = file:write_file(File, Long),
+ false = dets:is_dets_file(File),
+ check_pps(P0),
+
+ %% Bug: deleting file
+ Args = [[{access, A}, {repair, R}] ||
+ A <- [read, read_write],
+ R <- [true, false, force]],
+ Fun1 = fun(S, As) ->
+ P1 = pps(),
+ ok = file:write_file(File, S),
+ {error,{not_a_dets_file,File}} = dets:open_file(File, As),
+ {ok, S} = file:read_file(File),
+ check_pps(P1)
+ end,
+ Fun2 = fun(S) ->
+ _ = [Fun1(S, As) || As <- Args],
+ ok
+ end,
+ ok = Fun2(Long), % no change here
+ ok = Fun2(Short), % mimic the behaviour for longer files
+
+ %% open_file/1
+ ok = file:write_file(File, Long),
+ {error,{not_a_dets_file,File}} = dets:open_file(File), % no change
+ ok = file:write_file(File, Short),
+ {error,{not_a_dets_file,File}} = dets:open_file(File), % mimic
+
+ _ = file:delete(File),
+ ok.
+
%%
%% Parts common to several test cases
%%
-start_node_rel(Name, Rel, How) ->
- Release = [{release, atom_to_list(Rel)}],
- Pa = filename:dirname(code:which(?MODULE)),
- test_server:start_node(Name, How,
- [{args,
- " -kernel net_setuptime 100 "
- " -pa " ++ Pa},
- {erl, Release}]).
-
crash(File, Where) ->
crash(File, Where, 10).
@@ -4323,7 +4343,7 @@ check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) ->
check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) ->
true = test_server:is_native(M) andalso length(Args) =:= A.
-check_pps(P0) ->
+check_pps({Ports0,Procs0} = P0) ->
case pps() of
P0 ->
ok;
@@ -4335,22 +4355,28 @@ check_pps(P0) ->
case pps() of
P0 ->
ok;
- P1 ->
- io:format("failure, got ~p~n, expected ~p\n", [P1, P0]),
- {Ports0,Procs0} = P0,
- {Ports1,Procs1} = P1,
- show("Old ports", Ports0 -- Ports1),
- show("New ports", Ports1 -- Ports0),
- show("Old procs", Procs0 -- Procs1),
- show("New procs", Procs1 -- Procs0),
- ?t:fail()
- end
+ {Ports1,Procs1} = P1 ->
+ case {Ports1 -- Ports0, Procs1 -- Procs0} of
+ {[], []} -> ok;
+ {PortsDiff,ProcsDiff} ->
+ io:format("failure, got ~p~n, expected ~p\n", [P1, P0]),
+ show("Old port", Ports0 -- Ports1),
+ show("New port", PortsDiff),
+ show("Old proc", Procs0 -- Procs1),
+ show("New proc", ProcsDiff),
+ ?t:fail()
+ end
+ end
end.
show(_S, []) ->
ok;
-show(S, L) ->
- io:format("~s: ~p~n", [S, L]).
+show(S, [Pid|Pids]) when is_pid(Pid) ->
+ io:format("~s: ~p~n", [S, erlang:process_info(Pid)]),
+ show(S, Pids);
+show(S, [Port|Ports]) when is_port(Port)->
+ io:format("~s: ~p~n", [S, erlang:port_info(Port)]),
+ show(S, Ports).
pps() ->
dets:start(),
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index 0cd2688e2e..43c980e994 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -26,11 +26,11 @@
-include_lib("test_server/include/test_server.hrl").
-% Default timetrap timeout (set in init_per_testcase).
+%% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
init_per_testcase(_Case, Config) ->
- ?line Dog = ?t:timetrap(?default_timeout),
+ Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
@@ -67,20 +67,21 @@ normal(doc) ->
normal(suite) ->
[];
normal(Config) when is_list(Config) ->
- ?line {module,expand_test} = c:l(expand_test),
- % These tests might fail if another module with the prefix "expand_" happens
- % to also be loaded.
- ?line {yes, "test:", []} = edlin_expand:expand(lists:reverse("expand_")),
- ?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")),
- ?line {no,[],
- [{"a_fun_name",1},
- {"a_less_fun_name",1},
- {"b_comes_after_a",1},
- {"module_info",0},
- {"module_info",1}]} = edlin_expand:expand(lists:reverse("expand_test:")),
- ?line {yes,[],[{"a_fun_name",1},
- {"a_less_fun_name",1}]} = edlin_expand:expand(
- lists:reverse("expand_test:a_")),
+ {module,expand_test} = c:l(expand_test),
+ %% These tests might fail if another module with the prefix
+ %% "expand_" happens to also be loaded.
+ {yes, "test:", []} = do_expand("expand_"),
+ {no, [], []} = do_expand("expandXX_"),
+ {no,[],
+ [{"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"expand0arity_entirely",0},
+ {"module_info",0},
+ {"module_info",1}]} = do_expand("expand_test:"),
+ {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = do_expand("expand_test:a_"),
+ {yes,"arity_entirely()",[]} = do_expand("expand_test:expand0"),
ok.
quoted_fun(doc) ->
@@ -88,38 +89,35 @@ quoted_fun(doc) ->
quoted_fun(suite) ->
[];
quoted_fun(Config) when is_list(Config) ->
- ?line {module,expand_test} = c:l(expand_test),
- ?line {module,expand_test1} = c:l(expand_test1),
+ {module,expand_test} = c:l(expand_test),
+ {module,expand_test1} = c:l(expand_test1),
%% should be no colon after test this time
- ?line {yes, "test", []} = edlin_expand:expand(lists:reverse("expand_")),
- ?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")),
- ?line {no,[],[{"'#weird-fun-name'",0},
- {"'Quoted_fun_name'",0},
- {"'Quoted_fun_too'",0},
- {"a_fun_name",1},
- {"a_less_fun_name",1},
- {"b_comes_after_a",1},
- {"module_info",0},
- {"module_info",1}]} = edlin_expand:expand(
- lists:reverse("expand_test1:")),
- ?line {yes,"_",[]} = edlin_expand:expand(
- lists:reverse("expand_test1:a")),
- ?line {yes,[],[{"a_fun_name",1},
- {"a_less_fun_name",1}]} = edlin_expand:expand(
- lists:reverse("expand_test1:a_")),
- ?line {yes,[],
- [{"'#weird-fun-name'",0},
+ {yes, "test", []} = do_expand("expand_"),
+ {no, [], []} = do_expand("expandXX_"),
+ {no,[],[{"'#weird-fun-name'",1},
{"'Quoted_fun_name'",0},
- {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
- lists:reverse("expand_test1:'")),
- ?line {yes,"uoted_fun_",[]} = edlin_expand:expand(
- lists:reverse("expand_test1:'Q")),
- ?line {yes,[],
- [{"'Quoted_fun_name'",0},
- {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
- lists:reverse("expand_test1:'Quoted_fun_")),
- ?line {yes,"weird-fun-name'(",[]} = edlin_expand:expand(
- lists:reverse("expand_test1:'#")),
+ {"'Quoted_fun_too'",0},
+ {"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = do_expand("expand_test1:"),
+ {yes,"_",[]} = do_expand("expand_test1:a"),
+ {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = do_expand("expand_test1:a_"),
+ {yes,[],
+ [{"'#weird-fun-name'",1},
+ {"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = do_expand("expand_test1:'"),
+ {yes,"uoted_fun_",[]} = do_expand("expand_test1:'Q"),
+ {yes,[],
+ [{"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = do_expand("expand_test1:'Quoted_fun_"),
+ {yes,"weird-fun-name'(",[]} = do_expand("expand_test1:'#"),
+
+ %% Since there is a module_info/1 as well as a module_info/0
+ %% there should not be a closing parenthesis added.
+ {yes,"(",[]} = do_expand("expand_test:module_info"),
ok.
quoted_module(doc) ->
@@ -127,51 +125,46 @@ quoted_module(doc) ->
quoted_module(suite) ->
[];
quoted_module(Config) when is_list(Config) ->
- ?line {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
- ?line {yes, "Caps':", []} = edlin_expand:expand(lists:reverse("'ExpandTest")),
- ?line {no,[],
- [{"a_fun_name",1},
- {"a_less_fun_name",1},
- {"b_comes_after_a",1},
- {"module_info",0},
- {"module_info",1}]} = edlin_expand:expand(lists:reverse("'ExpandTestCaps':")),
- ?line {yes,[],[{"a_fun_name",1},
- {"a_less_fun_name",1}]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps':a_")),
+ {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
+ {yes, "Caps':", []} = do_expand("'ExpandTest"),
+ {no,[],
+ [{"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = do_expand("'ExpandTestCaps':"),
+ {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = do_expand("'ExpandTestCaps':a_"),
ok.
quoted_both(suite) ->
[];
quoted_both(Config) when is_list(Config) ->
- ?line {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
- ?line {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'),
+ {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
+ {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'),
%% should be no colon (or quote) after test this time
- ?line {yes, "Caps", []} = edlin_expand:expand(lists:reverse("'ExpandTest")),
- ?line {no,[],[{"'#weird-fun-name'",0},
- {"'Quoted_fun_name'",0},
- {"'Quoted_fun_too'",0},
- {"a_fun_name",1},
- {"a_less_fun_name",1},
- {"b_comes_after_a",1},
- {"module_info",0},
- {"module_info",1}]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':")),
- ?line {yes,"_",[]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':a")),
- ?line {yes,[],[{"a_fun_name",1},
- {"a_less_fun_name",1}]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':a_")),
- ?line {yes,[],
- [{"'#weird-fun-name'",0},
+ {yes, "Caps", []} = do_expand("'ExpandTest"),
+ {no,[],[{"'#weird-fun-name'",0},
{"'Quoted_fun_name'",0},
- {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':'")),
- ?line {yes,"uoted_fun_",[]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':'Q")),
- ?line {yes,[],
- [{"'Quoted_fun_name'",0},
- {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':'Quoted_fun_")),
- ?line {yes,"weird-fun-name'(",[]} = edlin_expand:expand(
- lists:reverse("'ExpandTestCaps1':'#")),
+ {"'Quoted_fun_too'",0},
+ {"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = do_expand("'ExpandTestCaps1':"),
+ {yes,"_",[]} = do_expand("'ExpandTestCaps1':a"),
+ {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = do_expand("'ExpandTestCaps1':a_"),
+ {yes,[],
+ [{"'#weird-fun-name'",0},
+ {"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = do_expand("'ExpandTestCaps1':'"),
+ {yes,"uoted_fun_",[]} = do_expand("'ExpandTestCaps1':'Q"),
+ {yes,[],
+ [{"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = do_expand("'ExpandTestCaps1':'Quoted_fun_"),
+ {yes,"weird-fun-name'()",[]} = do_expand("'ExpandTestCaps1':'#"),
ok.
+
+do_expand(String) ->
+ edlin_expand:expand(lists:reverse(String)).
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 0cbdf76270..b17e8bd186 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -25,7 +25,8 @@
variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1,
- otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1]).
+ otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1,
+ otp_11728/1, encoding/1]).
-export([epp_parse_erl_form/2]).
@@ -67,7 +68,8 @@ all() ->
{group, variable}, otp_4870, otp_4871, otp_5362, pmod,
not_circular, skip_header, otp_6277, otp_7702, otp_8130,
overload_mac, otp_8388, otp_8470, otp_8503, otp_8562,
- otp_8665, otp_8911, otp_10302, otp_10820].
+ otp_8665, otp_8911, otp_10302, otp_10820, otp_11728,
+ encoding].
groups() ->
[{upcase_mac, [], [upcase_mac_1, upcase_mac_2]},
@@ -122,10 +124,22 @@ include_local(Config) when is_list(Config) ->
%%% regular epp:parse_file, the test case will time out, and then epp
%%% server will go on growing until we dump core.
epp_parse_file(File, Inc, Predef) ->
- {ok, Epp} = epp:open(File, Inc, Predef),
+ List = do_epp_parse_file(fun() ->
+ epp:open(File, Inc, Predef)
+ end),
+ List = do_epp_parse_file(fun() ->
+ Opts = [{name, File},
+ {includes, Inc},
+ {macros, Predef}],
+ epp:open(Opts)
+ end),
+ {ok, List}.
+
+do_epp_parse_file(Open) ->
+ {ok, Epp} = Open(),
List = collect_epp_forms(Epp),
epp:close(Epp),
- {ok, List}.
+ List.
collect_epp_forms(Epp) ->
Result = epp_parse_erl_form(Epp),
@@ -1387,6 +1401,88 @@ do_otp_10820(File, C, PC) ->
true = test_server:stop_node(Node),
ok.
+otp_11728(doc) ->
+ ["OTP-11728. Bugfix circular macro."];
+otp_11728(suite) ->
+ [];
+otp_11728(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+ H = <<"-define(MACRO,[[]++?MACRO]).">>,
+ HrlFile = filename:join(Dir, "otp_11728.hrl"),
+ ok = file:write_file(HrlFile, H),
+ C = <<"-module(otp_11728).
+ -compile(export_all).
+
+ -include(\"otp_11728.hrl\").
+
+ function_name()->
+ A=?MACRO, % line 7
+ ok">>,
+ ErlFile = filename:join(Dir, "otp_11728.erl"),
+ ok = file:write_file(ErlFile, C),
+ {ok, L} = epp:parse_file(ErlFile, [Dir], []),
+ true = lists:member({error,{7,epp,{circular,'MACRO',none}}}, L),
+ _ = file:delete(HrlFile),
+ _ = file:delete(ErlFile),
+ ok.
+
+%% Check the new API for setting the default encoding.
+encoding(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+ ErlFile = filename:join(Dir, "encoding.erl"),
+
+ %% Try a latin-1 file with no encoding given.
+ C1 = <<"-module(encoding).
+ %% ",246,"
+ ">>,
+ ok = file:write_file(ErlFile, C1),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {error,_},
+ {error,{2,epp,cannot_parse}},
+ {eof,2}]} = epp:parse_file(ErlFile, []),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,3}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1}]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,3}],[{encoding,none}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1},extra]),
+
+ %% Try a latin-1 file with encoding given in a comment.
+ C2 = <<"-module(encoding).
+ %% encoding: latin-1
+ %% ",246,"
+ ">>,
+ ok = file:write_file(ErlFile, C2),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}]} =
+ epp:parse_file(ErlFile, []),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1}]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}]} =
+ epp:parse_file(ErlFile, [{default_encoding,utf8}]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}],[{encoding,latin1}]} =
+ epp:parse_file(ErlFile, [extra]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}],[{encoding,latin1}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1},extra]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}],[{encoding,latin1}]} =
+ epp:parse_file(ErlFile, [{default_encoding,utf8},extra]),
+ ok.
+
+
check(Config, Tests) ->
eval_tests(Config, fun check_test/2, Tests).
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index c4b6b35e72..b91d14b5b8 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -25,7 +25,7 @@
match_bin/1,
string_plusplus/1,
pattern_expr/1,
- guard_3/1, guard_4/1,
+ guard_3/1, guard_4/1, guard_5/1,
lc/1,
simple_cases/1,
unary_plus/1,
@@ -42,7 +42,8 @@
try_catch/1,
eval_expr_5/1,
zero_width/1,
- eep37/1]).
+ eep37/1,
+ eep43/1]).
%%
%% Define to run outside of test server
@@ -78,11 +79,11 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[guard_1, guard_2, match_pattern, string_plusplus,
- pattern_expr, match_bin, guard_3, guard_4, lc,
+ pattern_expr, match_bin, guard_3, guard_4, guard_5, lc,
simple_cases, unary_plus, apply_atom, otp_5269,
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
otp_8133, otp_10622, funs, try_catch, eval_expr_5, zero_width,
- eep37].
+ eep37, eep43].
groups() ->
[].
@@ -247,6 +248,20 @@ guard_4(Config) when is_list(Config) ->
false),
ok.
+guard_5(doc) ->
+ ["Guards with erlang:'=='/2"];
+guard_5(suite) ->
+ [];
+guard_5(Config) when is_list(Config) ->
+ {ok,Tokens ,_} =
+ erl_scan:string("case 1 of A when erlang:'=='(A, 1) -> true end."),
+ {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ true = guard_5_compiled(),
+ {value, true, [{'A',1}]} = erl_eval:expr(Expr, []),
+ ok.
+
+guard_5_compiled() ->
+ case 1 of A when erlang:'=='(A, 1) -> true end.
lc(doc) ->
["OTP-4518."];
@@ -1424,6 +1439,22 @@ eep37(Config) when is_list(Config) ->
720),
ok.
+eep43(Config) when is_list(Config) ->
+ check(fun () -> #{} end, " #{}.", #{}),
+ check(fun () -> #{a => b} end, "#{a => b}.", #{a => b}),
+ check(fun () ->
+ Map = #{a => b},
+ {Map#{a := b},Map#{a => c},Map#{d => e}}
+ end,
+ "begin "
+ " Map = #{a => B=b}, "
+ " {Map#{a := B},Map#{a => c},Map#{d => e}} "
+ "end.",
+ {#{a => b},#{a => c},#{a => b,d => e}}),
+ error_check("[camembert]#{}.", {badarg,[camembert]}),
+ error_check("#{} = 1.", {badmatch,1}),
+ ok.
+
%% Check the string in different contexts: as is; in fun; from compiled code.
check(F, String, Result) ->
check1(F, String, Result),
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 94b4397a9c..43e679f7ed 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -38,7 +38,7 @@
-export([attributes/1, expr/1, guard/1,
init/1, pattern/1, strict/1, update/1,
otp_5915/1, otp_7931/1, otp_5990/1,
- otp_7078/1, otp_7101/1]).
+ otp_7078/1, otp_7101/1, maps/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -56,7 +56,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[attributes, expr, guard, init,
- pattern, strict, update, {group, tickets}].
+ pattern, strict, update, maps, {group, tickets}].
groups() ->
[{tickets, [],
@@ -402,7 +402,22 @@ update(Config) when is_list(Config) ->
],
?line run(Config, Ts),
ok.
-
+
+maps(Config) when is_list(Config) ->
+ Ts = [<<"-record(rr, {a,b,c}).
+ t() ->
+ R0 = id(#rr{a=1,b=2,c=3}),
+ R1 = id(#rr{a=4,b=5,c=6}),
+ [{R0,R1}] =
+ maps:to_list(#{#rr{a=1,b=2,c=3} => #rr{a=4,b=5,c=6}}),
+ #{#rr{a=1,b=2,c=3} := #rr{a=1,b=2,c=3}} =
+ #{#rr{a=1,b=2,c=3} => R1}#{#rr{a=1,b=2,c=3} := R0},
+ ok.
+
+ id(X) -> X.
+ ">>],
+ run(Config, Ts, [strict_record_tests]),
+ ok.
otp_5915(doc) ->
"Strict record tests in guards.";
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 6e9a9dd7bf..bb14de333d 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -52,6 +52,7 @@
guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1,
+ otp_11772/1, otp_11771/1,
export_all/1,
bif_clash/1,
behaviour_basic/1, behaviour_multiple/1,
@@ -60,7 +61,9 @@
format_warn/1,
on_load_successful/1, on_load_failing/1,
too_many_arguments/1,
- basic_errors/1,bin_syntax_errors/1
+ basic_errors/1,bin_syntax_errors/1,
+ predef/1,
+ maps/1,maps_type/1
]).
% Default timetrap timeout (set in init_per_testcase).
@@ -84,10 +87,12 @@ all() ->
unsized_binary_in_bin_gen_pattern,
otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,
- otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all,
+ otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,
+ otp_11772, otp_11771, export_all,
bif_clash, behaviour_basic, behaviour_multiple,
otp_7550, otp_8051, format_warn, {group, on_load},
- too_many_arguments, basic_errors, bin_syntax_errors].
+ too_many_arguments, basic_errors, bin_syntax_errors, predef,
+ maps, maps_type].
groups() ->
[{unused_vars_warn, [],
@@ -1274,10 +1279,9 @@ guard(Config) when is_list(Config) ->
tuple.
">>,
[nowarn_obsolete_guard],
- {error,
+ {errors,
[{6,erl_lint,illegal_guard_expr},{18,erl_lint,illegal_guard_expr}],
- [{18,erl_lint,{removed,{erlang,is_constant,1},
- "Removed in R13B"}}]}},
+ []}},
{guard2,
<<"-record(apa,{}).
t1(A) when atom(A), atom(A) ->
@@ -1336,14 +1340,11 @@ guard(Config) when is_list(Config) ->
tuple.
">>,
[nowarn_obsolete_guard],
- {error,[{6,erl_lint,illegal_guard_expr},
- {6,erl_lint,illegal_guard_expr},
- {18,erl_lint,illegal_guard_expr},
- {18,erl_lint,illegal_guard_expr}],
- [{18,erl_lint,{removed,{erlang,is_constant,1},
- "Removed in R13B"}},
- {18,erl_lint,{removed,{erlang,is_constant,1},
- "Removed in R13B"}}]}},
+ {errors,[{6,erl_lint,illegal_guard_expr},
+ {6,erl_lint,illegal_guard_expr},
+ {18,erl_lint,illegal_guard_expr},
+ {18,erl_lint,illegal_guard_expr}],
+ []}},
{guard3,
<<"-record(apa,{}).
t2(A) when atom(A); atom(A) ->
@@ -1493,7 +1494,15 @@ guard(Config) when is_list(Config) ->
[],
{errors,[{1,erl_lint,illegal_guard_expr},
{2,erl_lint,illegal_guard_expr},
- {3,erl_lint,illegal_guard_expr}],[]}}
+ {3,erl_lint,illegal_guard_expr}],[]}},
+ {guard9,
+ <<"t(X, Y) when erlang:'andalso'(X, Y) -> ok;
+ t(X, Y) when erlang:'orelse'(X, Y) -> ok.
+ ">>,
+ [],
+ {errors,[{1,erl_lint,illegal_guard_expr},
+ {2,erl_lint,illegal_guard_expr}],
+ []}}
],
?line [] = run(Config, Ts1),
ok.
@@ -2552,7 +2561,7 @@ otp_10436(Config) when is_list(Config) ->
ok.
otp_11254(doc) ->
- "OTP-11254. Warnings for opaque types.";
+ "OTP-11254. M:F/A could crash the linter.";
otp_11254(suite) -> [];
otp_11254(Config) when is_list(Config) ->
Ts = <<"-module(p2).
@@ -2565,6 +2574,62 @@ otp_11254(Config) when is_list(Config) ->
run_test2(Config, Ts, []),
ok.
+otp_11772(doc) ->
+ "OTP-11772. Reintroduce errors for redefined builtin types.";
+otp_11772(suite) -> [];
+otp_11772(Config) when is_list(Config) ->
+ Ts = <<"
+ -module(newly).
+
+ -compile(export_all).
+
+ %% Built-in:
+ -type node() :: node().
+ -type mfa() :: tuple().
+ -type gb_tree() :: mfa(). % Allowed since Erlang/OTP 17.0
+ -type digraph() :: [_]. % Allowed since Erlang/OTP 17.0
+
+ -type t() :: mfa() | digraph() | gb_tree() | node().
+
+ -spec t() -> t().
+
+ t() ->
+ 1.
+ ">>,
+ {errors,[{7,erl_lint,{builtin_type,{node,0}}},
+ {8,erl_lint,{builtin_type,{mfa,0}}}],
+ []} = run_test2(Config, Ts, []),
+ ok.
+
+otp_11771(doc) ->
+ "OTP-11771. Do not allow redefinition of the types arity(_) &c..";
+otp_11771(suite) -> [];
+otp_11771(Config) when is_list(Config) ->
+ Ts = <<"
+ -module(newly).
+
+ -compile(export_all).
+
+ %% No longer allowed in 17.0:
+ -type arity() :: atom().
+ -type bitstring() :: list().
+ -type iodata() :: integer().
+ -type boolean() :: iodata().
+
+ -type t() :: arity() | bitstring() | iodata() | boolean().
+
+ -spec t() -> t().
+
+ t() ->
+ 1.
+ ">>,
+ {errors,[{7,erl_lint,{builtin_type,{arity,0}}},
+ {8,erl_lint,{builtin_type,{bitstring,0}}},
+ {9,erl_lint,{builtin_type,{iodata,0}}},
+ {10,erl_lint,{builtin_type,{boolean,0}}}],
+ []} = run_test2(Config, Ts, []),
+ ok.
+
export_all(doc) ->
"OTP-7392. Warning for export_all.";
export_all(Config) when is_list(Config) ->
@@ -2827,7 +2892,24 @@ bif_clash(Config) when is_list(Config) ->
{6,erl_lint,{illegal_guard_local_call,{is_tuple,1}}},
{7,erl_lint,{illegal_guard_local_call,{is_list,1}}},
{8,erl_lint,{illegal_guard_local_call,{is_record,3}}},
- {9,erl_lint,{illegal_guard_local_call,{is_record,3}}}],[]}}
+ {9,erl_lint,{illegal_guard_local_call,{is_record,3}}}],[]}},
+ %% We can also suppress all auto imports at once
+ {clash22,
+ <<"-export([size/1, binary_part/2]).
+ -compile(no_auto_import).
+ size([]) ->
+ 0;
+ size({N,_}) ->
+ N;
+ size([_|T]) ->
+ 1+size(T).
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ []}
],
?line [] = run(Config, Ts),
@@ -3224,6 +3306,139 @@ bin_syntax_errors(Config) ->
[] = run(Config, Ts),
ok.
+predef(doc) ->
+ "OTP-10342: Predefined types: array(), digraph(), and so on";
+predef(suite) -> [];
+predef(Config) when is_list(Config) ->
+ W = get_compilation_warnings(Config, "predef", []),
+ [] = W,
+ W2 = get_compilation_warnings(Config, "predef2", []),
+ Tag = deprecated_builtin_type,
+ [{7,erl_lint,{Tag,{array,0},{array,array,1},"OTP 18.0"}},
+ {12,erl_lint,{Tag,{dict,0},{dict,dict,2},"OTP 18.0"}},
+ {17,erl_lint,{Tag,{digraph,0},{digraph,graph},"OTP 18.0"}},
+ {27,erl_lint,{Tag,{gb_set,0},{gb_sets,set,1},"OTP 18.0"}},
+ {32,erl_lint,{Tag,{gb_tree,0},{gb_trees,tree,2},"OTP 18.0"}},
+ {37,erl_lint,{Tag,{queue,0},{queue,queue,1},"OTP 18.0"}},
+ {42,erl_lint,{Tag,{set,0},{sets,set,1},"OTP 18.0"}},
+ {47,erl_lint,{Tag,{tid,0},{ets,tid},"OTP 18.0"}}] = W2,
+ Ts = [{otp_10342_1,
+ <<"-compile(nowarn_deprecated_type).
+
+ -spec t(dict()) -> non_neg_integer().
+
+ t(D) ->
+ erlang:phash2(D, 3000).
+ ">>,
+ {[nowarn_unused_function]},
+ []},
+ {otp_10342_2,
+ <<"-spec t(dict()) -> non_neg_integer().
+
+ t(D) ->
+ erlang:phash2(D, 3000).
+ ">>,
+ {[nowarn_unused_function]},
+ {warnings,[{1,erl_lint,
+ {deprecated_builtin_type,{dict,0},{dict,dict,2},
+ "OTP 18.0"}}]}}],
+ [] = run(Config, Ts),
+ ok.
+
+maps(Config) ->
+ %% TODO: test key patterns, not done because map patterns are going to be
+ %% changed a lot.
+ Ts = [{illegal_map_construction,
+ <<"t() ->
+ #{ a := b,
+ c => d,
+ e := f
+ }#{ a := b,
+ c => d,
+ e := f };
+ t() when is_map(#{ a := b,
+ c => d
+ }#{ a := b,
+ c => d,
+ e := f }) ->
+ ok.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,illegal_map_construction},
+ {4,erl_lint,illegal_map_construction},
+ {8,erl_lint,illegal_map_construction}],
+ []}},
+ {illegal_pattern,
+ <<"t(#{ a := A,
+ c => d,
+ e := F,
+ g := 1 + 1,
+ h := _,
+ i := (_X = _Y),
+ j := (x ! y) }) ->
+ {A,F}.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,illegal_pattern},
+ {7,erl_lint,illegal_pattern}],
+ []}},
+ {error_in_illegal_map_construction,
+ <<"t() -> #{ a := X }.">>,
+ [],
+ {errors,[{1,erl_lint,illegal_map_construction},
+ {1,erl_lint,{unbound_var,'X'}}],
+ []}},
+ {errors_in_map_keys,
+ <<"t(V) -> #{ a => 1,
+ #{a=>V} => 2,
+ #{ \"hi\" => wazzup, hi => ho } => yep,
+ [try a catch _:_ -> b end] => nope,
+ ok => 1.0,
+ [3+3] => nope,
+ 1.0 => yep,
+ {3.0+3} => nope,
+ {yep} => yep,
+ [case a of a -> a end] => nope
+ }.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,{illegal_map_key_variable,'V'}},
+ {4,erl_lint,illegal_map_key},
+ {6,erl_lint,illegal_map_key},
+ {8,erl_lint,illegal_map_key},
+ {10,erl_lint,illegal_map_key}],[]}}],
+ [] = run(Config, Ts),
+ ok.
+
+maps_type(Config) when is_list(Config) ->
+ Ts = [
+ {maps_type1,
+ <<"
+ -type m() :: #{a => integer()}.
+ -spec t1(#{k=>term()}) -> {term(), map()}.
+
+ t1(#{k:=V}=M) -> {V,M}.
+
+ -spec t2(m()) -> integer().
+
+ t2(#{a:=V}) -> V.
+ ">>,
+ [],
+ []},
+ {maps_type2,
+ <<"
+ %% Built-in var arity map type:
+ -type map() :: tuple().
+ -type a() :: map().
+
+ -spec t(a()) -> a().
+ t(M) -> M.
+ ">>,
+ [],
+ {warnings,[{3,erl_lint,{new_var_arity_type,map}}]}}],
+ [] = run(Config, Ts),
+ ok.
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of
@@ -3246,8 +3461,10 @@ get_compilation_warnings(Conf, Filename, Warnings) ->
FileS = binary_to_list(Bin),
{match,[{Start,Length}|_]} = re:run(FileS, "-module.*\\n"),
Test = lists:nthtail(Start+Length, FileS),
- {warnings, Ws} = run_test(Conf, Test, Warnings),
- Ws.
+ case run_test(Conf, Test, Warnings) of
+ {warnings, Ws} -> Ws;
+ [] -> []
+ end.
%% Compiles a test module and returns the list of errors and warnings.
diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
new file mode 100644
index 0000000000..ee9073aa67
--- /dev/null
+++ b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
@@ -0,0 +1,67 @@
+-module(predef).
+
+-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1,
+ queue/1, set/1, tid/0, tid2/0]).
+
+-export_type([array/0, digraph/0, gb_set/0]).
+
+%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque
+%% types were ignored but did not generate any warning.
+-opaque array() :: atom().
+-opaque digraph() :: atom().
+-opaque gb_set() :: atom().
+-type dict() :: atom().
+-type gb_tree() :: atom().
+-type queue() :: atom().
+-type set() :: atom().
+-type tid() :: atom().
+
+-spec array(array()) -> array:array().
+
+array(A) ->
+ array:relax(A).
+
+-spec dict(dict()) -> dict:dict().
+
+dict(D) ->
+ dict:store(1, a, D).
+
+-spec digraph(digraph()) -> [digraph:edge()].
+
+digraph(G) ->
+ digraph:edges(G).
+
+-spec digraph2(digraph:graph()) -> [digraph:edge()].
+
+digraph2(G) ->
+ digraph:edges(G).
+
+-spec gb_set(gb_set()) -> gb_sets:set().
+
+gb_set(S) ->
+ gb_sets:balance(S).
+
+-spec gb_tree(gb_tree()) -> gb_trees:tree().
+
+gb_tree(S) ->
+ gb_trees:balance(S).
+
+-spec queue(queue()) -> queue:queue().
+
+queue(Q) ->
+ queue:reverse(Q).
+
+-spec set(set()) -> sets:set().
+
+set(S) ->
+ sets:union([S]).
+
+-spec tid() -> tid().
+
+tid() ->
+ ets:new(tid, []).
+
+-spec tid2() -> ets:tid().
+
+tid2() ->
+ ets:new(tid, []).
diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef2.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef2.erl
new file mode 100644
index 0000000000..b1d941a49a
--- /dev/null
+++ b/lib/stdlib/test/erl_lint_SUITE_data/predef2.erl
@@ -0,0 +1,56 @@
+-module(predef2).
+
+-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1,
+ queue/1, set/1, tid/0, tid2/0]).
+
+-export_type([array/0, digraph/0, gb_set/0]).
+
+-spec array(array()) -> array:array().
+
+array(A) ->
+ array:relax(A).
+
+-spec dict(dict()) -> dict:dict().
+
+dict(D) ->
+ dict:store(1, a, D).
+
+-spec digraph(digraph()) -> [digraph:edge()].
+
+digraph(G) ->
+ digraph:edges(G).
+
+-spec digraph2(digraph:graph()) -> [digraph:edge()].
+
+digraph2(G) ->
+ digraph:edges(G).
+
+-spec gb_set(gb_set()) -> gb_sets:set().
+
+gb_set(S) ->
+ gb_sets:balance(S).
+
+-spec gb_tree(gb_tree()) -> gb_trees:tree().
+
+gb_tree(S) ->
+ gb_trees:balance(S).
+
+-spec queue(queue()) -> queue:queue().
+
+queue(Q) ->
+ queue:reverse(Q).
+
+-spec set(set()) -> sets:set().
+
+set(S) ->
+ sets:union([S]).
+
+-spec tid() -> tid().
+
+tid() ->
+ ets:new(tid, []).
+
+-spec tid2() -> ets:tid().
+
+tid2() ->
+ ets:new(tid, []).
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index cc744ee76b..babf3a49eb 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -46,6 +46,7 @@
import_export/1, misc_attrs/1, dialyzer_attrs/1,
hook/1,
neg_indent/1,
+ maps_syntax/1,
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1,
@@ -76,7 +77,8 @@ groups() ->
[{expr, [],
[func, call, recs, try_catch, if_then, receive_after,
bits, head_tail, cond1, block, case1, ops,
- messages, old_mnemosyne_syntax]},
+ messages, old_mnemosyne_syntax, maps_syntax
+ ]},
{attributes, [], [misc_attrs, import_export, dialyzer_attrs]},
{tickets, [],
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
@@ -975,6 +977,35 @@ count_atom(L, A) when is_list(L) ->
count_atom(_, _) ->
0.
+maps_syntax(doc) -> "Maps syntax";
+maps_syntax(suite) -> [];
+maps_syntax(Config) when is_list(Config) ->
+ Ts = [{map_fun_1,
+ <<"t() ->\n"
+ " M0 = #{ 1 => hi, hi => 42, 1.0 => {hi,world}},\n"
+ " M1 = M0#{ 1 := hello, new_val => 1337 },\n"
+ " map_fun_2:val(M1).\n">>},
+ {map_fun_2,
+ <<"val(#{ 1 := V1, hi := V2, new_val := V3}) -> {V1,V2,V3}.\n">>}],
+ compile(Config, Ts),
+
+ ok = pp_expr(<<"#{}">>),
+ ok = pp_expr(<<"#{ a => 1, <<\"hi\">> => \"world\", 33 => 1.0 }">>),
+ ok = pp_expr(<<"#{ a := V1, <<\"hi\">> := V2 } = M">>),
+ ok = pp_expr(<<"M#{ a => V1, <<\"hi\">> := V2 }">>),
+ F = <<"-module(maps_type_syntax).\n"
+ "-compile(export_all).\n"
+ "-type t1() :: map().\n"
+ "-type t2() :: #{ atom() => integer(), atom() => float() }.\n"
+ "-spec f1(t1()) -> 'true'.\n"
+ "f1(M) when is_map(M) -> true.\n"
+ "-spec f2(t2()) -> integer().\n"
+ "f2(#{a := V1,b := V2}) -> V1 + V2.\n"
+ "\n">>,
+ ok = pp_forms(F),
+ ok.
+
+
otp_8567(doc) ->
"OTP_8567. Avoid duplicated 'undefined' in record field types.";
otp_8567(suite) -> [];
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 447e159cd4..35067e8116 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -21,7 +21,7 @@
init_per_group/2,end_per_group/2]).
-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1,
- otp_10990/1, otp_10992/1]).
+ otp_10990/1, otp_10992/1, otp_11807/1]).
-import(lists, [nth/2,flatten/1]).
-import(io_lib, [print/1]).
@@ -60,7 +60,8 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992].
+ [{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992,
+ otp_11807].
groups() ->
[{error, [], [error_1, error_2]}].
@@ -1144,6 +1145,25 @@ otp_10992(Config) when is_list(Config) ->
erl_parse:abstract([$A,42.0], [{encoding,utf8}]),
ok.
+otp_11807(doc) ->
+ "OTP-11807. Generalize erl_parse:abstract/2.";
+otp_11807(suite) ->
+ [];
+otp_11807(Config) when is_list(Config) ->
+ {cons,0,{integer,0,97},{cons,0,{integer,0,98},{nil,0}}} =
+ erl_parse:abstract("ab", [{encoding,none}]),
+ {cons,0,{integer,0,-1},{nil,0}} =
+ erl_parse:abstract([-1], [{encoding,latin1}]),
+ ASCII = fun(I) -> I >= 0 andalso I < 128 end,
+ {string,0,"xyz"} = erl_parse:abstract("xyz", [{encoding,ASCII}]),
+ {cons,0,{integer,0,228},{nil,0}} =
+ erl_parse:abstract([228], [{encoding,ASCII}]),
+ {cons,0,{integer,0,97},{atom,0,a}} =
+ erl_parse:abstract("a"++a, [{encoding,latin1}]),
+ {'EXIT', {{badarg,bad},_}} = % minor backward incompatibility
+ (catch erl_parse:abstract("string", [{encoding,bad}])),
+ ok.
+
test_string(String, Expected) ->
{ok, Expected, _End} = erl_scan:string(String),
test(String).
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 82c3e7ecaf..8dc8b2c291 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -75,6 +75,7 @@
-export([otp_9932/1]).
-export([otp_9423/1]).
-export([otp_10182/1]).
+-export([ets_all/1]).
-export([memory_check_summary/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -151,6 +152,7 @@ all() ->
otp_10182,
otp_9932,
otp_9423,
+ ets_all,
memory_check_summary]. % MUST BE LAST
@@ -5565,7 +5567,19 @@ otp_10182(Config) when is_list(Config) ->
ets:delete(Db),
In = Out.
-
+%% Test that ets:all include/exclude tables that we know are created/deleted
+ets_all(Config) when is_list(Config) ->
+ Pids = [spawn_link(fun() -> ets_all_run() end) || _ <- [1,2]],
+ receive after 3*1000 -> ok end,
+ [begin unlink(P), exit(P,kill) end || P <- Pids],
+ ok.
+
+ets_all_run() ->
+ Table = ets:new(undefined, []),
+ true = lists:member(Table, ets:all()),
+ ets:delete(Table),
+ false = lists:member(Table, ets:all()),
+ ets_all_run().
%
diff --git a/lib/stdlib/test/expand_test.erl b/lib/stdlib/test/expand_test.erl
index 63e4bc3aa0..b9db32c352 100644
--- a/lib/stdlib/test/expand_test.erl
+++ b/lib/stdlib/test/expand_test.erl
@@ -20,7 +20,8 @@
-export([a_fun_name/1,
a_less_fun_name/1,
- b_comes_after_a/1]).
+ b_comes_after_a/1,
+ expand0arity_entirely/0]).
a_fun_name(X) ->
X.
@@ -30,3 +31,6 @@ a_less_fun_name(X) ->
b_comes_after_a(X) ->
X.
+
+expand0arity_entirely () ->
+ ok.
diff --git a/lib/stdlib/test/expand_test1.erl b/lib/stdlib/test/expand_test1.erl
index 11b6fec0f3..1d375e5677 100644
--- a/lib/stdlib/test/expand_test1.erl
+++ b/lib/stdlib/test/expand_test1.erl
@@ -23,7 +23,7 @@
b_comes_after_a/1,
'Quoted_fun_name'/0,
'Quoted_fun_too'/0,
- '#weird-fun-name'/0]).
+ '#weird-fun-name'/1]).
a_fun_name(X) ->
X.
@@ -40,5 +40,5 @@ b_comes_after_a(X) ->
'Quoted_fun_too'() ->
too.
-'#weird-fun-name'() ->
+'#weird-fun-name'(_) ->
weird.
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index 232df6a13f..ecd9cff9f9 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -96,11 +96,19 @@ absname(Config) when is_list(Config) ->
?line file:set_cwd(Cwd),
ok;
- {unix, _} ->
- ?line ok = file:set_cwd("/usr"),
- ?line "/usr/foo" = filename:absname(foo),
- ?line "/usr/foo" = filename:absname("foo"),
- ?line "/usr/../ebin" = filename:absname("../ebin"),
+ Type ->
+ case Type of
+ {unix, _} ->
+ ?line ok = file:set_cwd("/usr"),
+ ?line "/usr/foo" = filename:absname(foo),
+ ?line "/usr/foo" = filename:absname("foo"),
+ ?line "/usr/../ebin" = filename:absname("../ebin");
+ {ose, _} ->
+ ?line ok = file:set_cwd("/romfs"),
+ ?line "/romfs/foo" = filename:absname(foo),
+ ?line "/romfs/foo" = filename:absname("foo"),
+ ?line "/romfs/../ebin" = filename:absname("../ebin")
+ end,
?line file:set_cwd("/"),
?line "/foo" = filename:absname(foo),
@@ -155,7 +163,7 @@ absname_2(Config) when is_list(Config) ->
?line "a:/erlang" = filename:absname("a:erlang", [Drive|":/"]),
ok;
- {unix, _} ->
+ _ ->
?line "/usr/foo" = filename:absname(foo, "/usr"),
?line "/usr/foo" = filename:absname("foo", "/usr"),
?line "/usr/../ebin" = filename:absname("../ebin", "/usr"),
@@ -189,7 +197,7 @@ basename_1(Config) when is_list(Config) ->
?line "foo" = filename:basename(["usr\\foo\\"]),
?line "foo" = filename:basename("A:\\usr\\foo"),
?line "foo" = filename:basename("A:foo");
- {unix, _} ->
+ _ ->
?line "strange\\but\\true" =
filename:basename("strange\\but\\true")
end,
@@ -219,7 +227,7 @@ basename_2(Config) when is_list(Config) ->
?line "foo.erl" = filename:basename("c:\\usr.hrl\\foo.erl",
".hrl"),
?line "foo" = filename:basename("A:\\usr\\foo", ".hrl");
- {unix, _} ->
+ _ ->
?line "strange\\but\\true" =
filename:basename("strange\\but\\true.erl", ".erl"),
?line "strange\\but\\true" =
@@ -317,7 +325,7 @@ join(Config) when is_list(Config) ->
filename:join(["A:","C:usr","foo.erl"]),
?line "d:/foo" = filename:join([$D, $:, $/, []], "foo"),
ok;
- {unix, _} ->
+ _ ->
ok
end.
@@ -332,7 +340,7 @@ pathtype(Config) when is_list(Config) ->
?line volumerelative = filename:pathtype("/usr/local/bin"),
?line volumerelative = filename:pathtype("A:usr/local/bin"),
ok;
- {unix, _} ->
+ _ ->
?line absolute = filename:pathtype("/"),
?line absolute = filename:pathtype("/usr/local/bin"),
ok
@@ -450,10 +458,17 @@ absname_bin(Config) when is_list(Config) ->
?line file:set_cwd(Cwd),
ok;
- {unix, _} ->
- ?line ok = file:set_cwd(<<"/usr">>),
- ?line <<"/usr/foo">> = filename:absname(<<"foo">>),
- ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>),
+ Type ->
+ case Type of
+ {unix,_} ->
+ ?line ok = file:set_cwd(<<"/usr">>),
+ ?line <<"/usr/foo">> = filename:absname(<<"foo">>),
+ ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>);
+ {ose,_} ->
+ ?line ok = file:set_cwd(<<"/romfs">>),
+ ?line <<"/romfs/foo">> = filename:absname(<<"foo">>),
+ ?line <<"/romfs/../ebin">> = filename:absname(<<"../ebin">>)
+ end,
?line file:set_cwd(<<"/">>),
?line <<"/foo">> = filename:absname(<<"foo">>),
@@ -503,7 +518,7 @@ absname_bin_2(Config) when is_list(Config) ->
?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/">>),
ok;
- {unix, _} ->
+ _ ->
?line <<"/usr/foo">> = filename:absname(<<"foo">>, <<"/usr">>),
?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>, <<"/usr">>),
@@ -527,7 +542,7 @@ basename_bin_1(Config) when is_list(Config) ->
{win32, _} ->
?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>),
?line <<"foo">> = filename:basename(<<"A:foo">>);
- {unix, _} ->
+ _ ->
?line <<"strange\\but\\true">> =
filename:basename(<<"strange\\but\\true">>)
end,
@@ -551,7 +566,7 @@ basename_bin_2(Config) when is_list(Config) ->
?line <<"foo.erl">> = filename:basename(<<"c:\\usr.hrl\\foo.erl">>,
<<".hrl">>),
?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>, <<".hrl">>);
- {unix, _} ->
+ _ ->
?line <<"strange\\but\\true">> =
filename:basename(<<"strange\\but\\true.erl">>, <<".erl">>),
?line <<"strange\\but\\true">> =
@@ -639,7 +654,7 @@ join_bin(Config) when is_list(Config) ->
filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]),
?line <<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>),
ok;
- {unix, _} ->
+ _ ->
ok
end.
@@ -653,7 +668,7 @@ pathtype_bin(Config) when is_list(Config) ->
volumerelative = filename:pathtype(<<"/usr/local/bin">>),
volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
ok;
- {unix, _} ->
+ _ ->
absolute = filename:pathtype(<<"/">>),
absolute = filename:pathtype(<<"/usr/local/bin">>),
ok
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 5819ef3890..60a1ba8c60 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -974,6 +974,10 @@ get_state(Config) when is_list(Config) ->
[{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result1),
Result2 = sys:get_state(Pid, 5000),
[{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result2),
+ ok = sys:suspend(Pid),
+ Result3 = sys:get_state(Pid),
+ [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result3),
+ ok = sys:resume(Pid),
ok = gen_event:stop(Pid),
ok.
@@ -998,4 +1002,11 @@ replace_state(Config) when is_list(Config) ->
Replace3 = fun(_) -> exit(fail) end,
[{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace3),
[{dummy1_h,false,NState2}] = sys:get_state(Pid),
+ %% verify state replaced if process sys suspended
+ NState3 = "replaced again and again",
+ Replace4 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState3) end,
+ ok = sys:suspend(Pid),
+ [{dummy1_h,false,NState3}] = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ [{dummy1_h,false,NState3}] = sys:get_state(Pid),
ok.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index fd15838b7d..8aeec07ae8 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -426,6 +426,14 @@ get_state(Config) when is_list(Config) ->
{idle, State} = sys:get_state(gfsm),
{idle, State} = sys:get_state(gfsm, 5000),
stop_it(Pid2),
+
+ %% check that get_state works when pid is sys suspended
+ {ok, Pid3} = gen_fsm:start(gen_fsm_SUITE, {state_data, State}, []),
+ {idle, State} = sys:get_state(Pid3),
+ ok = sys:suspend(Pid3),
+ {idle, State} = sys:get_state(Pid3, 5000),
+ ok = sys:resume(Pid3),
+ stop_it(Pid3),
ok.
replace_state(Config) when is_list(Config) ->
@@ -442,8 +450,18 @@ replace_state(Config) when is_list(Config) ->
{state0, NState2} = sys:get_state(Pid),
%% verify no change in state if replace function crashes
Replace3 = fun(_) -> error(fail) end,
- {state0, NState2} = sys:replace_state(Pid, Replace3),
+ {'EXIT',{{callback_failed,
+ {gen_fsm,system_replace_state},{error,fail}},_}} =
+ (catch sys:replace_state(Pid, Replace3)),
{state0, NState2} = sys:get_state(Pid),
+ %% verify state replaced if process sys suspended
+ ok = sys:suspend(Pid),
+ Suffix2 = " and again",
+ NState3 = NState2 ++ Suffix2,
+ Replace4 = fun({StateName, _}) -> {StateName, NState3} end,
+ {state0, NState3} = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ {state0, NState3} = sys:get_state(Pid, 5000),
stop_it(Pid),
ok.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index a360a0809b..960e7f60e7 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1049,6 +1049,9 @@ get_state(Config) when is_list(Config) ->
{ok, Pid} = gen_server:start_link(?MODULE, {state,State}, []),
State = sys:get_state(Pid),
State = sys:get_state(Pid, 5000),
+ ok = sys:suspend(Pid),
+ State = sys:get_state(Pid),
+ ok = sys:resume(Pid),
ok.
%% Verify that sys:replace_state correctly replaces gen_server state
@@ -1075,8 +1078,18 @@ replace_state(Config) when is_list(Config) ->
NState2 = sys:get_state(Pid, 5000),
%% verify no change in state if replace function crashes
Replace3 = fun(_) -> throw(fail) end,
- NState2 = sys:replace_state(Pid, Replace3),
+ {'EXIT',{{callback_failed,
+ {gen_server,system_replace_state},{throw,fail}},_}} =
+ (catch sys:replace_state(Pid, Replace3)),
NState2 = sys:get_state(Pid, 5000),
+ %% verify state replaced if process sys suspended
+ ok = sys:suspend(Pid),
+ Suffix2 = " and again",
+ NState3 = NState2 ++ Suffix2,
+ Replace4 = fun(S) -> S ++ Suffix2 end,
+ NState3 = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ NState3 = sys:get_state(Pid, 5000),
ok.
%% Test that the time for a huge message queue is not
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 92253ef5b9..f4589a8e24 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -61,7 +61,7 @@
zip_unzip/1, zip_unzip3/1, zipwith/1, zipwith3/1,
filter_partition/1,
otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1,
- suffix/1, subtract/1]).
+ suffix/1, subtract/1, droplast/1]).
%% Sort randomized lists until stopped.
%%
@@ -2641,4 +2641,12 @@ sub_non_matching(A, B) ->
sub(A, B) ->
Res = A -- B,
Res = lists:subtract(A, B).
-
+
+%% Test lists:droplast/1
+droplast(Config) when is_list(Config) ->
+ ?line [] = lists:droplast([x]),
+ ?line [x] = lists:droplast([x, y]),
+ ?line {'EXIT', {function_clause, _}} = (catch lists:droplast([])),
+ ?line {'EXIT', {function_clause, _}} = (catch lists:droplast(x)),
+
+ ok.
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 2846657c09..37fbb5267b 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. 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
@@ -72,7 +72,7 @@
otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1,
otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1,
- otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1,
+ otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, otp_11758/1,
manpage/1,
@@ -142,7 +142,7 @@ groups() ->
{tickets, [],
[otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562,
otp_6590, otp_6673, otp_6964, otp_7114, otp_7232,
- otp_7238, otp_7552, otp_6674, otp_7714]},
+ otp_7238, otp_7552, otp_6674, otp_7714, otp_11758]},
{compat, [], [backward, forward]}].
init_per_suite(Config) ->
@@ -6670,6 +6670,19 @@ otp_7714(Config) when is_list(Config) ->
ets:delete(E2)">>],
?line run(Config, Ts).
+otp_11758(doc) ->
+ "OTP-11758. Bug.";
+otp_11758(suite) -> [];
+otp_11758(Config) when is_list(Config) ->
+ Ts = [<<"T = ets:new(r, [{keypos, 2}]),
+ L = [{rrr, xxx, aaa}, {rrr, yyy, bbb}],
+ true = ets:insert(T, L),
+ QH = qlc:q([{rrr, B, C} || {rrr, B, C} <- ets:table(T),
+ (B =:= xxx) or (B =:= yyy) and (C =:= aaa)]),
+ [{rrr,xxx,aaa}] = qlc:e(QH),
+ ets:delete(T)">>],
+ run(Config, Ts).
+
otp_6674(doc) ->
"OTP-6674. match/comparison.";
otp_6674(suite) -> [];
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 692dfe0faa..e016432f4d 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -54,7 +54,7 @@ config(priv_dir,_) ->
-include_lib("test_server/include/test_server.hrl").
-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(2)).
+-define(default_timeout, ?t:minutes(10)).
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
?line OrigPath = code:get_path(),
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 8fff5e2e05..53a34511d9 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -23,10 +23,6 @@
-include_lib("test_server/include/test_server.hrl").
-% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(1)).
--define(application, stdlib).
-
% Test server specific exports
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
@@ -60,11 +56,8 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(?default_timeout),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
- Dog=?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+ Config.
+end_per_testcase(_Case, _Config) ->
ok.
%
@@ -78,17 +71,18 @@ app_test(Config) when is_list(Config) ->
?t:app_test(stdlib),
ok.
-%% Test that appup allows upgrade from/downgrade to a maximum of two
-%% major releases back.
+%% Test that appup allows upgrade from/downgrade to a maximum of one
+%% major release back.
appup_test(_Config) ->
- do_appup_tests(create_test_vsns()).
+ appup_tests(stdlib,create_test_vsns(stdlib)).
-do_appup_tests({[],[]}) ->
+appup_tests(_App,{[],[]}) ->
{skip,"no previous releases available"};
-do_appup_tests({OkVsns,NokVsns}) ->
- application:load(stdlib),
- {_,_,Vsn} = lists:keyfind(stdlib,1,application:loaded_applications()),
- AppupFile = filename:join([code:lib_dir(stdlib),ebin,"stdlib.appup"]),
+appup_tests(App,{OkVsns,NokVsns}) ->
+ application:load(App),
+ {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),
+ AppupFileName = atom_to_list(App) ++ ".appup",
+ AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),
{ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
ct:log("~p~n",[AppupScript]),
ct:log("Testing ok versions: ~p~n",[OkVsns]),
@@ -99,13 +93,12 @@ do_appup_tests({OkVsns,NokVsns}) ->
check_appup(NokVsns,DownTo,error),
ok.
-create_test_vsns() ->
+create_test_vsns(App) ->
This = erlang:system_info(otp_release),
FirstMajor = previous_major(This),
SecondMajor = previous_major(FirstMajor),
- ThirdMajor = previous_major(SecondMajor),
- Ok = stdlib_vsn([FirstMajor,SecondMajor]),
- Nok0 = stdlib_vsn([ThirdMajor]),
+ Ok = app_vsn(App,[FirstMajor]),
+ Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
[Ok1|_] ->
[Ok1 ++ ",1" | Nok0]; % illegal
@@ -121,18 +114,36 @@ previous_major("r"++Rel) ->
previous_major(Rel) ->
integer_to_list(list_to_integer(Rel)-1).
-stdlib_vsn([R|Rs]) ->
- case test_server:is_release_available(R) of
- true ->
- {ok,N} = test_server:start_node(prevrel,peer,[{erl,[{release,R}]}]),
- As = rpc:call(N,application,which_applications,[]),
- {_,_,KV} = lists:keyfind(stdlib,1,As),
- test_server:stop_node(N),
- [KV|stdlib_vsn(Rs)];
+app_vsn(App,[R|Rs]) ->
+ OldRel =
+ case test_server:is_release_available(R) of
+ true ->
+ {release,R};
+ false ->
+ case ct:get_config({otp_releases,list_to_atom(R)}) of
+ undefined ->
+ false;
+ Prog0 ->
+ case os:find_executable(Prog0) of
+ false ->
+ false;
+ Prog ->
+ {prog,Prog}
+ end
+ end
+ end,
+ case OldRel of
false ->
- stdlib_vsn(Rs)
+ app_vsn(App,Rs);
+ _ ->
+ {ok,N} = test_server:start_node(prevrel,peer,[{erl,[OldRel]}]),
+ _ = rpc:call(N,application,load,[App]),
+ As = rpc:call(N,application,loaded_applications,[]),
+ {_,_,V} = lists:keyfind(App,1,As),
+ test_server:stop_node(N),
+ [V|app_vsn(App,Rs)]
end;
-stdlib_vsn([]) ->
+app_vsn(_App,[]) ->
[].
check_appup([Vsn|Vsns],Instrs,Expected) ->
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index ac5a34c3bc..836ea7c030 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -60,7 +60,7 @@
simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]).
%% Misc tests
--export([child_unlink/1, tree/1, count_children_memory/1,
+-export([child_unlink/1, tree/1, count_children/1,
do_not_save_start_parameters_for_temporary_children/1,
do_not_save_child_specs_for_temporary_children/1,
simple_one_for_one_scale_many_temporary_children/1,
@@ -82,7 +82,7 @@ all() ->
{group, normal_termination},
{group, shutdown_termination},
{group, abnormal_termination}, child_unlink, tree,
- count_children_memory, do_not_save_start_parameters_for_temporary_children,
+ count_children, do_not_save_start_parameters_for_temporary_children,
do_not_save_child_specs_for_temporary_children,
simple_one_for_one_scale_many_temporary_children, temporary_bystander,
simple_global_supervisor, hanging_restart_loop, hanging_restart_loop_simple].
@@ -129,23 +129,10 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-init_per_testcase(count_children_memory, Config) ->
- try erlang:memory() of
- _ ->
- erts_debug:set_internal_state(available_internal_state, true),
- Dog = ?t:timetrap(?TIMEOUT),
- [{watchdog,Dog}|Config]
- catch error:notsup ->
- {skip, "+Meamin used during test; erlang:memory/1 not available"}
- end;
init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?TIMEOUT),
[{watchdog,Dog}|Config].
-end_per_testcase(count_children_memory, Config) ->
- catch erts_debug:set_internal_state(available_internal_state, false),
- ?t:timetrap_cancel(?config(watchdog,Config)),
- ok;
end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(?config(watchdog,Config)),
ok.
@@ -1249,34 +1236,24 @@ tree(Config) when is_list(Config) ->
[0,0,0,0] = get_child_counts(NewSup2).
%%-------------------------------------------------------------------------
-%% Test that count_children does not eat memory.
-count_children_memory(Config) when is_list(Config) ->
+%% Test count_children
+count_children(Config) when is_list(Config) ->
process_flag(trap_exit, true),
Child = {child, {supervisor_1, start_child, []}, temporary, 1000,
worker, []},
{ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
[supervisor:start_child(sup_test, []) || _Ignore <- lists:seq(1,1000)],
- garbage_collect(),
- _Size1 = proc_memory(),
Children = supervisor:which_children(sup_test),
- _Size2 = proc_memory(),
ChildCount = get_child_counts(sup_test),
- _Size3 = proc_memory(),
[supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)],
- garbage_collect(),
- Children2 = supervisor:which_children(sup_test),
- Size4 = proc_memory(),
ChildCount2 = get_child_counts(sup_test),
- Size5 = proc_memory(),
+ Children2 = supervisor:which_children(sup_test),
- garbage_collect(),
- Children3 = supervisor:which_children(sup_test),
- Size6 = proc_memory(),
ChildCount3 = get_child_counts(sup_test),
- Size7 = proc_memory(),
+ Children3 = supervisor:which_children(sup_test),
1000 = length(Children),
[1,1000,0,1000] = ChildCount,
@@ -1285,27 +1262,9 @@ count_children_memory(Config) when is_list(Config) ->
Children3 = Children2,
ChildCount3 = ChildCount2,
- %% count_children consumes memory using an accumulator function,
- %% but the space can be reclaimed incrementally,
- %% which_children may generate garbage that will be reclaimed later.
- case (Size5 =< Size4) of
- true -> ok;
- false ->
- test_server:fail({count_children, used_more_memory,Size4,Size5})
- end,
- case Size7 =< Size6 of
- true -> ok;
- false ->
- test_server:fail({count_children, used_more_memory,Size6,Size7})
- end,
-
[terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3],
[1,0,0,0] = get_child_counts(sup_test).
-proc_memory() ->
- erts_debug:set_internal_state(wait, deallocations),
- erlang:memory(processes_used).
-
%%-------------------------------------------------------------------------
%% Temporary children shall not be restarted so they should not save
%% start parameters, as it potentially can take up a huge amount of
@@ -1483,7 +1442,7 @@ simple_one_for_one_scale_many_temporary_children(_Config) ->
if T1 > 0 ->
Scaling = T2 div T1,
- if Scaling > 20 ->
+ if Scaling > 50 ->
%% The scaling shoul be linear (i.e.10, really), but we
%% give some extra here to avoid failing the test
%% unecessarily.
diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl
index c06ba545e7..f38bc87ae5 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.erl
@@ -19,7 +19,7 @@
-module(sys_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,log/1,log_to_file/1,
- stats/1,trace/1,suspend/1,install/1]).
+ stats/1,trace/1,suspend/1,install/1,special_process/1]).
-export([handle_call/3,terminate/2,init/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -27,14 +27,12 @@
%% Doesn't look into change_code at all
-%% Doesn't address writing your own process that understands
-%% system messages at all.
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [log, log_to_file, stats, trace, suspend, install].
+ [log, log_to_file, stats, trace, suspend, install, special_process].
groups() ->
[].
@@ -157,6 +155,84 @@ install(Config) when is_list(Config) ->
?line stop(),
ok.
+special_process(suite) -> [];
+special_process(Config) when is_list(Config) ->
+ ok = spec_proc(sys_sp1),
+ ok = spec_proc(sys_sp2).
+
+spec_proc(Mod) ->
+ {ok,_} = Mod:start_link(100),
+ ok = sys:statistics(Mod,true),
+ ok = sys:trace(Mod,true),
+ 1 = Ch = Mod:alloc(),
+ Free = lists:seq(2,100),
+ Replace = case sys:get_state(Mod) of
+ {[Ch],Free} ->
+ fun({A,F}) ->
+ Free = F,
+ {A,[2,3,4]}
+ end;
+ {state,[Ch],Free} ->
+ fun({state,A,F}) ->
+ Free = F,
+ {state,A,[2,3,4]}
+ end
+ end,
+ case sys:replace_state(Mod, Replace) of
+ {[Ch],[2,3,4]} -> ok;
+ {state,[Ch],[2,3,4]} -> ok
+ end,
+ ok = Mod:free(Ch),
+ case sys:get_state(Mod) of
+ {[],[1,2,3,4]} -> ok;
+ {state,[],[1,2,3,4]} -> ok
+ end,
+ {ok,[{start_time,_},
+ {current_time,_},
+ {reductions,_},
+ {messages_in,2},
+ {messages_out,1}]} = sys:statistics(Mod,get),
+ ok = sys:statistics(Mod,false),
+ [] = sys:replace_state(Mod, fun(_) -> [] end),
+ process_flag(trap_exit,true),
+ ok = case catch sys:get_state(Mod) of
+ [] ->
+ ok;
+ {'EXIT',{{callback_failed,
+ {Mod,system_get_state},{throw,fail}},_}} ->
+ ok
+ end,
+ Mod:stop(),
+ WaitForUnregister = fun W() ->
+ case whereis(Mod) of
+ undefined -> ok;
+ _ -> timer:sleep(10), W()
+ end
+ end,
+ WaitForUnregister(),
+ {ok,_} = Mod:start_link(4),
+ ok = case catch sys:replace_state(Mod, fun(_) -> {} end) of
+ {} ->
+ ok;
+ {'EXIT',{{callback_failed,
+ {Mod,system_replace_state},{throw,fail}},_}} ->
+ ok
+ end,
+ Mod:stop(),
+ WaitForUnregister(),
+ {ok,_} = Mod:start_link(4),
+ StateFun = fun(_) -> error(fail) end,
+ ok = case catch sys:replace_state(Mod, StateFun) of
+ {} ->
+ ok;
+ {'EXIT',{{callback_failed,
+ {Mod,system_replace_state},{error,fail}},_}} ->
+ ok;
+ {'EXIT',{{callback_failed,StateFun,{error,fail}},_}} ->
+ ok
+ end,
+ Mod:stop().
+
%%%%%%%%%%%%%%%%%%%%
%% Dummy server
diff --git a/lib/stdlib/test/sys_sp1.erl b/lib/stdlib/test/sys_sp1.erl
new file mode 100644
index 0000000000..e84ffcfa12
--- /dev/null
+++ b/lib/stdlib/test/sys_sp1.erl
@@ -0,0 +1,114 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2013. 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(sys_sp1).
+-export([start_link/1, stop/0]).
+-export([alloc/0, free/1]).
+-export([init/1]).
+-export([system_continue/3, system_terminate/4,
+ write_debug/3,
+ system_get_state/1, system_replace_state/2]).
+
+%% Implements the ch4 example from the Design Principles doc. Same as
+%% sys_sp2 except this module exports system_get_state/1 and
+%% system_replace_state/2
+
+start_link(NumCh) ->
+ proc_lib:start_link(?MODULE, init, [[self(),NumCh]]).
+
+stop() ->
+ ?MODULE ! stop,
+ ok.
+
+alloc() ->
+ ?MODULE ! {self(), alloc},
+ receive
+ {?MODULE, Res} ->
+ Res
+ end.
+
+free(Ch) ->
+ ?MODULE ! {free, Ch},
+ ok.
+
+init([Parent,NumCh]) ->
+ register(?MODULE, self()),
+ Chs = channels(NumCh),
+ Deb = sys:debug_options([]),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ loop(Chs, Parent, Deb).
+
+loop(Chs, Parent, Deb) ->
+ receive
+ {From, alloc} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, alloc, From}),
+ {Ch, Chs2} = alloc(Chs),
+ From ! {?MODULE, Ch},
+ Deb3 = sys:handle_debug(Deb2, fun write_debug/3,
+ ?MODULE, {out, {?MODULE, Ch}, From}),
+ loop(Chs2, Parent, Deb3);
+ {free, Ch} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, {free, Ch}}),
+ Chs2 = free(Ch, Chs),
+ loop(Chs2, Parent, Deb2);
+ {system, From, Request} ->
+ sys:handle_system_msg(Request, From, Parent,
+ ?MODULE, Deb, Chs);
+ stop ->
+ sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, stop}),
+ ok
+ end.
+
+system_continue(Parent, Deb, Chs) ->
+ loop(Chs, Parent, Deb).
+
+system_terminate(Reason, _Parent, _Deb, _Chs) ->
+ exit(Reason).
+
+system_get_state([]) ->
+ throw(fail);
+system_get_state(Chs) ->
+ {ok, Chs}.
+
+system_replace_state(_StateFun, {}) ->
+ throw(fail);
+system_replace_state(StateFun, Chs) ->
+ NChs = StateFun(Chs),
+ {ok, NChs, NChs}.
+
+write_debug(Dev, Event, Name) ->
+ io:format(Dev, "~p event = ~p~n", [Name, Event]).
+
+channels(NumCh) ->
+ {_Allocated=[], _Free=lists:seq(1,NumCh)}.
+
+alloc({_, []}) ->
+ {error, "no channels available"};
+alloc({Allocated, [H|T]}) ->
+ {H, {[H|Allocated], T}}.
+
+free(Ch, {Alloc, Free}=Channels) ->
+ case lists:member(Ch, Alloc) of
+ true ->
+ {lists:delete(Ch, Alloc), [Ch|Free]};
+ false ->
+ Channels
+ end.
diff --git a/lib/stdlib/test/sys_sp2.erl b/lib/stdlib/test/sys_sp2.erl
new file mode 100644
index 0000000000..56a5e4d071
--- /dev/null
+++ b/lib/stdlib/test/sys_sp2.erl
@@ -0,0 +1,107 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2013. 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(sys_sp2).
+-export([start_link/1, stop/0]).
+-export([alloc/0, free/1]).
+-export([init/1]).
+-export([system_continue/3, system_terminate/4,
+ write_debug/3]).
+
+%% Implements the ch4 example from the Design Principles doc. Same as
+%% sys_sp1 except this module does not export system_get_state/1 or
+%% system_replace_state/2
+
+start_link(NumCh) ->
+ proc_lib:start_link(?MODULE, init, [[self(),NumCh]]).
+
+stop() ->
+ ?MODULE ! stop,
+ ok.
+
+alloc() ->
+ ?MODULE ! {self(), alloc},
+ receive
+ {?MODULE, Res} ->
+ Res
+ end.
+
+free(Ch) ->
+ ?MODULE ! {free, Ch},
+ ok.
+
+%% can't use 2-tuple for state here as we do in sys_sp1, since the 2-tuple
+%% is not compatible with the backward compatibility handling for
+%% sys:get_state in sys.erl
+-record(state, {alloc,free}).
+
+init([Parent,NumCh]) ->
+ register(?MODULE, self()),
+ Chs = channels(NumCh),
+ Deb = sys:debug_options([]),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ loop(Chs, Parent, Deb).
+
+loop(Chs, Parent, Deb) ->
+ receive
+ {From, alloc} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, alloc, From}),
+ {Ch, Chs2} = alloc(Chs),
+ From ! {?MODULE, Ch},
+ Deb3 = sys:handle_debug(Deb2, fun write_debug/3,
+ ?MODULE, {out, {?MODULE, Ch}, From}),
+ loop(Chs2, Parent, Deb3);
+ {free, Ch} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, {free, Ch}}),
+ Chs2 = free(Ch, Chs),
+ loop(Chs2, Parent, Deb2);
+ {system, From, Request} ->
+ sys:handle_system_msg(Request, From, Parent,
+ ?MODULE, Deb, Chs);
+ stop ->
+ sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, stop}),
+ ok
+ end.
+
+system_continue(Parent, Deb, Chs) ->
+ loop(Chs, Parent, Deb).
+
+system_terminate(Reason, _Parent, _Deb, _Chs) ->
+ exit(Reason).
+
+write_debug(Dev, Event, Name) ->
+ io:format(Dev, "~p event = ~p~n", [Name, Event]).
+
+channels(NumCh) ->
+ #state{alloc=[], free=lists:seq(1,NumCh)}.
+
+alloc(#state{free=[]}=Channels) ->
+ {{error, "no channels available"}, Channels};
+alloc(#state{alloc=Allocated, free=[H|T]}) ->
+ {H, #state{alloc=[H|Allocated], free=T}}.
+
+free(Ch, #state{alloc=Alloc, free=Free}=Channels) ->
+ case lists:member(Ch, Alloc) of
+ true ->
+ #state{alloc=lists:delete(Ch, Alloc), free=[Ch|Free]};
+ false ->
+ Channels
+ end.
diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl
index e2d789bbe6..10b29d0d28 100644
--- a/lib/stdlib/test/unicode_SUITE.erl
+++ b/lib/stdlib/test/unicode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -98,7 +98,7 @@ ex_binaries_errors_utf8(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,20) ],
+ end || N <- lists:seq(1,21,4) ],
ok.
ex_binaries_errors_utf16_little(Config) when is_list(Config) ->
@@ -124,7 +124,7 @@ ex_binaries_errors_utf16_little(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf16,little}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
ex_binaries_errors_utf16_big(Config) when is_list(Config) ->
BrokenPart = << <<X:16/big>> || X <- lists:seq(16#DC00,16#DFFF) >>,
@@ -149,7 +149,7 @@ ex_binaries_errors_utf16_big(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf16,big}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
ex_binaries_errors_utf32_big(Config) when is_list(Config) ->
@@ -175,7 +175,7 @@ ex_binaries_errors_utf32_big(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf32,big}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
ex_binaries_errors_utf32_little(Config) when is_list(Config) ->
@@ -201,7 +201,7 @@ ex_binaries_errors_utf32_little(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf32,little}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 1ffcf31134..877675772f 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -637,6 +637,14 @@ lay_2(Node, Ctxt) ->
sep([follow(text("fun"), D, Ctxt1#ctxt.sub_indent),
text("end")]);
+ named_fun_expr ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:named_fun_expr_name(Node), Ctxt1),
+ D = lay_clauses(erl_syntax:named_fun_expr_clauses(Node),
+ {function,D1}, Ctxt1),
+ sep([follow(text("fun"), D, Ctxt1#ctxt.sub_indent),
+ text("end")]);
+
module_qualifier ->
{PrecL, _Prec, PrecR} = inop_prec(':'),
D1 = lay(erl_syntax:module_qualifier_argument(Node),
@@ -892,6 +900,32 @@ lay_2(Node, Ctxt) ->
beside(floating(text(".")), D2)),
maybe_parentheses(D3, Prec, Ctxt);
+ map_expr ->
+ {PrecL, Prec, _} = inop_prec('#'),
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = par(seq(erl_syntax:map_expr_fields(Node),
+ floating(text(",")), Ctxt1, fun lay/2)),
+ D2 = beside(text("#{"), beside(D1, floating(text("}")))),
+ D3 = case erl_syntax:map_expr_argument(Node) of
+ none ->
+ D2;
+ A ->
+ beside(lay(A, set_prec(Ctxt, PrecL)), D2)
+ end,
+ maybe_parentheses(D3, Prec, Ctxt);
+
+ map_field_assoc ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_field_assoc_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_field_assoc_value(Node), Ctxt1),
+ par([D1, floating(text("=>")), D2], Ctxt1#ctxt.break_indent);
+
+ map_field_exact ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_field_exact_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_field_exact_value(Node), Ctxt1),
+ par([D1, floating(text(":=")), D2], Ctxt1#ctxt.break_indent);
+
rule ->
%% Comments on the name will be repeated; cf.
%% `function'.
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 93187fa018..c9996c954e 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -220,6 +220,16 @@
macro/2,
macro_arguments/1,
macro_name/1,
+ map_expr/1,
+ map_expr/2,
+ map_expr_argument/1,
+ map_expr_fields/1,
+ map_field_assoc/2,
+ map_field_assoc_name/1,
+ map_field_assoc_value/1,
+ map_field_exact/2,
+ map_field_exact_name/1,
+ map_field_exact_value/1,
match_expr/2,
match_expr_body/1,
match_expr_pattern/1,
@@ -443,33 +453,38 @@
%% </tr><tr>
%% <td>list_comp</td>
%% <td>macro</td>
+%% <td>map_expr</td>
+%% <td>map_field_assoc</td>
+%% </tr><tr>
+%% <td>map_field_exact</td>
%% <td>match_expr</td>
%% <td>module_qualifier</td>
-%% </tr><tr>
%% <td>named_fun_expr</td>
+%% </tr><tr>
%% <td>nil</td>
%% <td>operator</td>
%% <td>parentheses</td>
-%% </tr><tr>
%% <td>prefix_expr</td>
+%% </tr><tr>
%% <td>receive_expr</td>
%% <td>record_access</td>
%% <td>record_expr</td>
-%% </tr><tr>
%% <td>record_field</td>
+%% </tr><tr>
%% <td>record_index_expr</td>
%% <td>rule</td>
%% <td>size_qualifier</td>
-%% </tr><tr>
%% <td>string</td>
+%% </tr><tr>
%% <td>text</td>
%% <td>try_expr</td>
%% <td>tuple</td>
-%% </tr><tr>
%% <td>underscore</td>
+%% </tr><tr>
%% <td>variable</td>
%% <td>warning_marker</td>
%% <td></td>
+%% <td></td>
%% </tr>
%% </table></center>
%%
@@ -510,6 +525,9 @@
%% @see list/2
%% @see list_comp/2
%% @see macro/2
+%% @see map_expr/2
+%% @see map_field_assoc/2
+%% @see map_field_exact/2
%% @see match_expr/2
%% @see module_qualifier/2
%% @see named_fun_expr/2
@@ -580,11 +598,12 @@ type(Node) ->
{lc, _, _, _} -> list_comp;
{bc, _, _, _} -> binary_comp;
{match, _, _, _} -> match_expr;
+ {map, _, _, _} -> map_expr;
+ {map, _, _} -> map_expr;
+ {map_field_assoc, _, _, _} -> map_field_assoc;
+ {map_field_exact, _, _, _} -> map_field_exact;
{op, _, _, _, _} -> infix_expr;
{op, _, _, _} -> prefix_expr;
- {map,_,_} -> map;
- {map_field_assoc,_,_,_} -> map_field_assoc;
- {map_field_exact,_,_,_} -> map_field_exact;
{record, _, _, _, _} -> record_expr;
{record, _, _, _} -> record_expr;
{record_field, _, _, _, _} -> record_access;
@@ -1913,26 +1932,206 @@ atom_literal(Node) ->
%% =====================================================================
+%% @equiv map_expr(none, Fields)
+
+-spec map_expr([syntaxTree()]) -> syntaxTree().
+
+map_expr(Fields) ->
+ map_expr(none, Fields).
+
+
+%% =====================================================================
+%% @doc Creates an abstract map expression. If `Fields' is
+%% `[F1, ..., Fn]', then if `Argument' is `none', the result represents
+%% "<code>#{<em>F1</em>, ..., <em>Fn</em>}</code>",
+%% otherwise it represents
+%% "<code><em>Argument</em>#{<em>F1</em>, ..., <em>Fn</em>}</code>".
+%%
+%% @see map_expr/1
+%% @see map_expr_argument/1
+%% @see map_expr_fields/1
+%% @see map_field_assoc/2
+%% @see map_field_exact/2
+
+-record(map_expr, {argument :: 'none' | syntaxTree(),
+ fields :: [syntaxTree()]}).
+
+%% `erl_parse' representation:
+%%
+%% {map, Pos, Fields}
+%% {map, Pos, Argument, Fields}
+
+-spec map_expr('none' | syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+map_expr(Argument, Fields) ->
+ tree(map_expr, #map_expr{argument = Argument, fields = Fields}).
+
+revert_map_expr(Node) ->
+ Pos = get_pos(Node),
+ Argument = map_expr_argument(Node),
+ Fields = map_expr_fields(Node),
+ case Argument of
+ none ->
+ {map, Pos, Fields};
+ _ ->
+ {map, Pos, Argument, Fields}
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the argument subtree of a `map_expr' node, if any. If `Node'
+%% represents "<code>#{...}</code>", `none' is returned.
+%% Otherwise, if `Node' represents "<code><em>Argument</em>#{...}</code>",
+%% `Argument' is returned.
+%%
+%% @see map_expr/2
+
+-spec map_expr_argument(syntaxTree()) -> 'none' | syntaxTree().
-map_elements(Node) ->
+map_expr_argument(Node) ->
case unwrap(Node) of
- {map, _, List} ->
- List;
- Node1 ->
- data(Node1)
+ {map, _, _} ->
+ none;
+ {map, _, Argument, _} ->
+ Argument;
+ Node1 ->
+ (data(Node1))#map_expr.argument
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the list of field subtrees of a `map_expr' node.
+%%
+%% @see map_expr/2
+
+-spec map_expr_fields(syntaxTree()) -> [syntaxTree()].
+
+map_expr_fields(Node) ->
+ case unwrap(Node) of
+ {map, _, Fields} ->
+ Fields;
+ {map, _, _, Fields} ->
+ Fields;
+ Node1 ->
+ (data(Node1))#map_expr.fields
end.
-map_field_elements({_,_,K,V}) ->
- [K,V].
-map(List) ->
- tree(map, List).
+%% =====================================================================
+%% @doc Creates an abstract map assoc field. The result represents
+%% "<code><em>Name</em> => <em>Value</em></code>".
+%%
+%% @see map_field_assoc_name/1
+%% @see map_field_assoc_value/1
+%% @see map_expr/2
+
+-record(map_field_assoc, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {map_field_assoc, Pos, Name, Value}
-map_field_assoc(List) ->
- tree(map_field_assoc, List).
+-spec map_field_assoc(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_field_assoc(Name, Value) ->
+ tree(map_field_assoc, #map_field_assoc{name = Name, value = Value}).
+
+revert_map_field_assoc(Node) ->
+ Pos = get_pos(Node),
+ Name = map_field_assoc_name(Node),
+ Value = map_field_assoc_value(Node),
+ {map_field_assoc, Pos, Name, Value}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_field_assoc' node.
+%%
+%% @see map_field_assoc/2
+
+-spec map_field_assoc_name(syntaxTree()) -> syntaxTree().
+
+map_field_assoc_name(Node) ->
+ case Node of
+ {map_field_assoc, _, Name, _} ->
+ Name;
+ _ ->
+ (data(Node))#map_field_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_field_assoc' node.
+%%
+%% @see map_field_assoc/2
+
+-spec map_field_assoc_value(syntaxTree()) -> syntaxTree().
+
+map_field_assoc_value(Node) ->
+ case Node of
+ {map_field_assoc, _, _, Value} ->
+ Value;
+ _ ->
+ (data(Node))#map_field_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract map exact field. The result represents
+%% "<code><em>Name</em> := <em>Value</em></code>".
+%%
+%% @see map_field_exact_name/1
+%% @see map_field_exact_value/1
+%% @see map_expr/2
+
+-record(map_field_exact, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {map_field_exact, Pos, Name, Value}
+
+-spec map_field_exact(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_field_exact(Name, Value) ->
+ tree(map_field_exact, #map_field_exact{name = Name, value = Value}).
+
+revert_map_field_exact(Node) ->
+ Pos = get_pos(Node),
+ Name = map_field_exact_name(Node),
+ Value = map_field_exact_value(Node),
+ {map_field_exact, Pos, Name, Value}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_field_exact' node.
+%%
+%% @see map_field_exact/2
+
+-spec map_field_exact_name(syntaxTree()) -> syntaxTree().
+
+map_field_exact_name(Node) ->
+ case Node of
+ {map_field_exact, _, Name, _} ->
+ Name;
+ _ ->
+ (data(Node))#map_field_exact.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_field_exact' node.
+%%
+%% @see map_field_exact/2
+
+-spec map_field_exact_value(syntaxTree()) -> syntaxTree().
+
+map_field_exact_value(Node) ->
+ case Node of
+ {map_field_exact, _, _, Value} ->
+ Value;
+ _ ->
+ (data(Node))#map_field_exact.name
+ end.
-map_field_exact(List) ->
- tree(map_field_exact, List).
%% =====================================================================
%% @doc Creates an abstract tuple. If `Elements' is
@@ -6117,6 +6316,12 @@ revert_root(Node) ->
revert_list(Node);
list_comp ->
revert_list_comp(Node);
+ map_expr ->
+ revert_map_expr(Node);
+ map_field_assoc ->
+ revert_map_field_assoc(Node);
+ map_field_exact ->
+ revert_map_field_exact(Node);
match_expr ->
revert_match_expr(Node);
module_qualifier ->
@@ -6358,6 +6563,19 @@ subtrees(T) ->
As ->
[[macro_name(T)], As]
end;
+ map_expr ->
+ case map_expr_argument(T) of
+ none ->
+ [map_expr_fields(T)];
+ V ->
+ [[V], map_expr_fields(T)]
+ end;
+ map_field_assoc ->
+ [[map_field_assoc_name(T)],
+ [map_field_assoc_value(T)]];
+ map_field_exact ->
+ [[map_field_exact_name(T)],
+ [map_field_exact_value(T)]];
match_expr ->
[[match_expr_pattern(T)],
[match_expr_body(T)]];
@@ -6421,12 +6639,6 @@ subtrees(T) ->
try_expr_clauses(T),
try_expr_handlers(T),
try_expr_after(T)];
- map ->
- [map_elements(T)];
- map_field_assoc ->
- [map_field_elements(T)];
- map_field_exact ->
- [map_field_elements(T)];
tuple ->
[tuple_elements(T)]
end
@@ -6502,6 +6714,10 @@ make_tree(list, [P, [S]]) -> list(P, S);
make_tree(list_comp, [[T], B]) -> list_comp(T, B);
make_tree(macro, [[N]]) -> macro(N);
make_tree(macro, [[N], A]) -> macro(N, A);
+make_tree(map_expr, [Fs]) -> map_expr(Fs);
+make_tree(map_expr, [[E], Fs]) -> map_expr(E, Fs);
+make_tree(map_field_assoc, [[K], [V]]) -> map_field_assoc(K, V);
+make_tree(map_field_exact, [[K], [V]]) -> map_field_exact(K, V);
make_tree(match_expr, [[P], [E]]) -> match_expr(P, E);
make_tree(named_fun_expr, [[N], C]) -> named_fun_expr(N, C);
make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N);
@@ -6522,10 +6738,7 @@ make_tree(record_index_expr, [[T], [F]]) ->
make_tree(rule, [[N], C]) -> rule(N, C);
make_tree(size_qualifier, [[N], [A]]) -> size_qualifier(N, A);
make_tree(try_expr, [B, C, H, A]) -> try_expr(B, C, H, A);
-make_tree(tuple, [E]) -> tuple(E);
-make_tree(map, [E]) -> map(E);
-make_tree(map_field_assoc, [E]) -> map_field_assoc(E);
-make_tree(map_field_exact, [E]) -> map_field_exact(E).
+make_tree(tuple, [E]) -> tuple(E).
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index e4665b99fc..2f0488abec 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -288,7 +288,7 @@ mapfoldl(_, S, []) ->
%%
%% @see //stdlib/sets
--spec variables(erl_syntax:syntaxTree()) -> set().
+-spec variables(erl_syntax:syntaxTree()) -> sets:set(atom()).
variables(Tree) ->
variables(Tree, sets:new()).
@@ -343,7 +343,7 @@ default_variable_name(N) ->
%%
%% @see new_variable_name/2
--spec new_variable_name(set()) -> atom().
+-spec new_variable_name(sets:set(atom())) -> atom().
new_variable_name(S) ->
new_variable_name(fun default_variable_name/1, S).
@@ -369,7 +369,7 @@ new_variable_name(S) ->
%% @see //stdlib/sets
%% @see //stdlib/random
--spec new_variable_name(fun((integer()) -> atom()), set()) -> atom().
+-spec new_variable_name(fun((integer()) -> atom()), sets:set(atom())) -> atom().
new_variable_name(F, S) ->
R = start_range(S),
@@ -416,7 +416,7 @@ generate(_Key, Range) ->
%%
%% @see new_variable_name/1
--spec new_variable_names(integer(), set()) -> [atom()].
+-spec new_variable_names(integer(), sets:set(atom())) -> [atom()].
new_variable_names(N, S) ->
new_variable_names(N, fun default_variable_name/1, S).
@@ -432,7 +432,7 @@ new_variable_names(N, S) ->
%%
%% @see new_variable_name/2
--spec new_variable_names(integer(), fun((integer()) -> atom()), set()) ->
+-spec new_variable_names(integer(), fun((integer()) -> atom()), sets:set(atom())) ->
[atom()].
new_variable_names(N, F, S) when is_integer(N) ->
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 7444d8dc67..38e0c2099b 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -14,7 +14,7 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% @copyright 1999-2006 Richard Carlsson
+%% @copyright 1999-2014 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -958,7 +958,7 @@ hidden_uses_2(Tree, Used) ->
-record(env, {file :: file:filename(),
module :: atom(),
current :: fa(),
- imports = dict:new() :: dict(),
+ imports = dict:new() :: dict:dict(atom(), atom()),
context = normal :: context(),
verbosity = 1 :: 0 | 1 | 2,
quiet = false :: boolean(),
@@ -970,12 +970,12 @@ hidden_uses_2(Tree, Used) ->
old_guard_tests = false :: boolean()}).
-record(st, {varc :: non_neg_integer(),
- used = sets:new() :: set(),
- imported :: set(),
- vars :: set(),
- functions :: set(),
+ used = sets:new() :: sets:set({atom(), arity()}),
+ imported :: sets:set({atom(), arity()}),
+ vars :: sets:set(atom()),
+ functions :: sets:set({atom(), arity()}),
new_forms = [] :: [erl_syntax:syntaxTree()],
- rename :: dict()}).
+ rename :: dict:dict(mfa(), {atom(), atom()})}).
visit_used(Names, Defs, Roots, Imports, Module, Opts) ->
File = proplists:get_value(file, Opts, ""),
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 19b1cd592f..0420508f2a 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -14,7 +14,7 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% @copyright 1998-2006 Richard Carlsson
+%% @copyright 1998-2014 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -695,7 +695,7 @@ merge_files1(Files, Opts) ->
preserved :: boolean(),
no_headers :: boolean(),
notes :: notes(),
- redirect :: dict(), % = dict(atom(), atom())
+ redirect :: dict:dict(atom(), atom()),
no_imports :: ordsets:ordset(atom()),
options :: [option()]
}).
@@ -727,7 +727,7 @@ merge_sources(Name, Sources, Opts) ->
%% Data structure for keeping state during transformation.
--record(state, {export :: set()}).
+-record(state, {export :: sets:set({atom(), arity()})}).
state__add_export(Name, Arity, S) ->
S#state{export = sets:add_element({Name, Arity},
@@ -1039,7 +1039,7 @@ make_stub(M, Map, Env) ->
-type atts() :: 'delete' | 'kill'.
-type file_atts() :: 'delete' | 'keep' | 'kill'.
--record(filter, {records :: set(),
+-record(filter, {records :: sets:set(atom()),
file_attributes :: file_atts(),
attributes :: atts()}).
@@ -1588,17 +1588,17 @@ alias_expansions_2(Modules, Table) ->
-record(code, {module :: atom(),
target :: atom(),
- sources :: set(), % set(atom()),
- static :: set(), % set(atom()),
- safe :: set(), % set(atom()),
+ sources :: sets:set(atom()),
+ static :: sets:set(atom()),
+ safe :: sets:set(atom()),
preserved :: boolean(),
no_headers :: boolean(),
notes :: notes(),
map :: map_fun(),
renaming :: fun((atom()) -> map_fun()),
- expand :: dict(), % = dict({atom(), integer()},
- % {atom(), {atom(), integer()}})
- redirect :: dict() % = dict(atom(), atom())
+ expand :: dict:dict({atom(), integer()},
+ {atom(), {atom(), integer()}}),
+ redirect :: dict:dict(atom(), atom())
}).
%% `Trees' must be a list of syntax trees of type `form_list'. The
diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src
index dc0b9edd62..83dcb5fe23 100644
--- a/lib/syntax_tools/src/syntax_tools.app.src
+++ b/lib/syntax_tools/src/syntax_tools.app.src
@@ -14,4 +14,5 @@
prettypr]},
{registered,[]},
{applications, [stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/syntax_tools/src/syntax_tools.appup.src b/lib/syntax_tools/src/syntax_tools.appup.src
index 54a63833e6..89c25d14d7 100644
--- a/lib/syntax_tools/src/syntax_tools.appup.src
+++ b/lib/syntax_tools/src/syntax_tools.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, syntax_tools}]}],
+ [{<<".*">>,[{restart_application, syntax_tools}]}]
+}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index b673b70a95..d4c54a72aa 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -24,12 +24,12 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([smoke_test/1,revert/1]).
+-export([app_test/1,appup_test/1,smoke_test/1,revert/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [smoke_test,revert].
+ [app_test,appup_test,smoke_test,revert].
groups() ->
[].
@@ -46,6 +46,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+app_test(Config) when is_list(Config) ->
+ ok = ?t:app_test(syntax_tools).
+
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(syntax_tools).
%% Read and parse all source in the OTP release.
smoke_test(Config) when is_list(Config) ->
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 26153a55f1..cf396ce636 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.13
+SYNTAX_TOOLS_VSN = 1.6.14
diff --git a/lib/test_server/doc/src/test_server.xml b/lib/test_server/doc/src/test_server.xml
index 4a47a8f45c..ed5569e1fe 100644
--- a/lib/test_server/doc/src/test_server.xml
+++ b/lib/test_server/doc/src/test_server.xml
@@ -625,6 +625,31 @@ Only valid for peer nodes. Note that slave nodes always
</desc>
</func>
<func>
+ <name>appup_test(App) -> ok | test_server:fail()</name>
+ <fsummary>Checks an applications .appup file for obvious errors</fsummary>
+ <type>
+ <v>App = term()</v>
+ <d>The name of the application to test</d>
+ </type>
+ <desc>
+ <p>Checks an applications .appup file for obvious errors.
+ The following is checked:
+ </p>
+ <list type="bulleted">
+ <item>syntax
+ </item>
+ <item>that .app file version and .appup file version match
+ </item>
+ <item>for non-library applications: validity of high-level upgrade
+ instructions, specifying no instructions is explicitly allowed
+ (in this case the application is not upgradeable)</item>
+ <item>for library applications: that there is exactly one wildcard
+ regexp clause restarting the application when upgrading or
+ downgrading from any version</item>
+ </list>
+ </desc>
+ </func>
+ <func>
<name>comment(Comment) -> ok</name>
<fsummary>Print a comment on the HTML result page</fsummary>
<type>
diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in
index 067663feb4..cd723bcd4d 100644
--- a/lib/test_server/src/configure.in
+++ b/lib/test_server/src/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script for Erlang.
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1997-2013. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2014. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -170,7 +170,30 @@ case $system in
fi
SHLIB_EXTRACT_ALL=""
;;
- *-netbsd*|*-freebsd*|*-openbsd*|*-dragonfly*)
+ *-openbsd*)
+ # Not available on all versions: check for include file.
+ AC_CHECK_HEADER(dlfcn.h, [
+ SHLIB_CFLAGS="-fpic"
+ SHLIB_LD="${CC}"
+ SHLIB_LDFLAGS="$LDFLAGS -shared"
+ SHLIB_SUFFIX=".so"
+ if test X${enable_m64_build} = Xyes; then
+ AC_MSG_ERROR(don't know how to link 64-bit dynamic drivers)
+ fi
+ if test X${enable_m32_build} = Xyes; then
+ AC_MSG_ERROR(don't know how to link 32-bit dynamic drivers)
+ fi
+ ], [
+ # No dynamic loading.
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld"
+ SHLIB_LDFLAGS=""
+ SHLIB_SUFFIX=""
+ AC_MSG_ERROR(don't know how to compile and link dynamic drivers)
+ ])
+ SHLIB_EXTRACT_ALL=""
+ ;;
+ *-netbsd*|*-freebsd*|*-dragonfly*)
# Not available on all versions: check for include file.
AC_CHECK_HEADER(dlfcn.h, [
SHLIB_CFLAGS="-fpic"
diff --git a/lib/test_server/src/test_server.app.src b/lib/test_server/src/test_server.app.src
index 42e78ed279..5672baa6ef 100644
--- a/lib/test_server/src/test_server.app.src
+++ b/lib/test_server/src/test_server.app.src
@@ -31,5 +31,8 @@
test_server,
test_server_break_process]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["tools-2.6.14","stdlib-2.0","runtime_tools-1.8.14",
+ "observer-2.0","kernel-3.0","inets-5.10",
+ "erts-6.0"]}]}.
diff --git a/lib/test_server/src/test_server.appup.src b/lib/test_server/src/test_server.appup.src
index 0fbe5f23f7..42c6fe2e46 100644
--- a/lib/test_server/src/test_server.appup.src
+++ b/lib/test_server/src/test_server.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}. \ No newline at end of file
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, test_server}]}],
+ [{<<".*">>,[{restart_application, test_server}]}]
+}.
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 54be6d4c72..9b05bddf63 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -40,7 +40,7 @@
-export([call_crash/3,call_crash/4,call_crash/5]).
-export([temp_name/1]).
-export([start_node/3, stop_node/1, wait_for_node/1, is_release_available/1]).
--export([app_test/1, app_test/2]).
+-export([app_test/1, app_test/2, appup_test/1]).
-export([is_native/1]).
-export([comment/1, make_priv_dir/0]).
-export([os_type/0]).
@@ -176,8 +176,6 @@ module_names(Beams) ->
do_cover_compile(Modules) ->
do_cover_compile1(lists:usort(Modules)). % remove duplicates
-do_cover_compile1([Dont|Rest]) when Dont=:=cover ->
- do_cover_compile1(Rest);
do_cover_compile1([M|Rest]) ->
case {code:is_sticky(M),code:is_loaded(M)} of
{true,_} ->
@@ -405,6 +403,7 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,
ref :: reference(),
pid :: pid(),
mf :: {atom(),atom()},
+ last_known_loc :: term(),
status :: tc_status() | 'undefined',
ret_val :: term(),
comment :: list(char()),
@@ -436,8 +435,9 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
LogOpts, TCCallback)
end),
put(test_server_detected_fail, []),
- St = #st{ref=Ref,pid=Pid,mf={Mod,Func},status=starting,ret_val=[],
- comment="",timeout=infinity,config=hd(Args)},
+ St = #st{ref=Ref,pid=Pid,mf={Mod,Func},last_known_loc=unknown,
+ status=starting,ret_val=[],comment="",timeout=infinity,
+ config=hd(Args)},
run_test_case_msgloop(St).
%% Ugly bug (pre R5A):
@@ -537,7 +537,22 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
St = setup_termination(RetVal, St0#st{config=undefined}),
run_test_case_msgloop(St);
{'EXIT',Pid,Reason} ->
- St = handle_tc_exit(Reason, St0),
+ %% This exit typically happens when an unknown external process
+ %% has caused a test case process to terminate (e.g. if a linked
+ %% process has crashed).
+ St =
+ case Reason of
+ {What,[Loc0={_M,_F,A,[{file,_}|_]}|_]} when
+ is_integer(A) ->
+ Loc = rewrite_loc_item(Loc0),
+ handle_tc_exit(What, St0#st{last_known_loc=[Loc]});
+ {What,[Details,Loc0={_M,_F,A,[{file,_}|_]}|_]} when
+ is_integer(A) ->
+ Loc = rewrite_loc_item(Loc0),
+ handle_tc_exit({What,Details}, St0#st{last_known_loc=[Loc]});
+ _ ->
+ handle_tc_exit(Reason, St0)
+ end,
run_test_case_msgloop(St);
{EndConfPid0,{call_end_conf,Data,_Result}} ->
#st{mf={Mod,Func},config=CurrConf} = St0,
@@ -695,7 +710,7 @@ handle_tc_exit(Reason, #st{config=Config,mf={Mod,Func0},pid=Pid,
{testcase_aborted=E,AbortReason,Loc0} ->
{{E,AbortReason},Loc0};
Other ->
- {Other,unknown}
+ {Other,St#st.last_known_loc}
end,
Func = case Status of
init_per_testcase=F -> {F,Func0};
@@ -837,9 +852,13 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
spawn_link(FwCall);
spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
+ Func1 = case Func of
+ {_InitOrEndPerTC,F} -> F;
+ F -> F
+ end,
FwCall =
fun() ->
- case catch fw_error_notify(Mod,Func,[],
+ case catch fw_error_notify(Mod,Func1,[],
Error,Loc) of
{'EXIT',FwErrorNotifyErr} ->
exit({fw_notify_done,error_notification,
@@ -847,8 +866,8 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
_ ->
ok
end,
- Conf = [{tc_status,{failed,timetrap_timeout}}|CurrConf],
- case catch do_end_tc_call(Mod,Func,
+ Conf = [{tc_status,{failed,Error}}|CurrConf],
+ case catch do_end_tc_call(Mod,Func1,
{Pid,Error,[Conf]},Error) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
@@ -1189,6 +1208,10 @@ do_init_per_testcase(Mod, Args) ->
"a Config list.\n",[]},
{skip,{failed,{Mod,init_per_testcase,bad_return}}}
catch
+ throw:{Skip,Reason} when Skip =:= skip; Skip =:= skipped ->
+ {skip,Reason};
+ exit:{Skip,Reason} when Skip =:= skip; Skip =:= skipped ->
+ {skip,Reason};
throw:Other ->
set_loc(erlang:get_stacktrace()),
Line = get_loc(),
@@ -1359,6 +1382,10 @@ ts_tc(M, F, A) ->
Result = try
apply(M, F, A)
catch
+ throw:{skip, Reason} -> {skip, Reason};
+ throw:{skipped, Reason} -> {skip, Reason};
+ exit:{skip, Reason} -> {skip, Reason};
+ exit:{skipped, Reason} -> {skip, Reason};
Type:Reason ->
Stk = erlang:get_stacktrace(),
set_loc(Stk),
@@ -2429,8 +2456,11 @@ app_test(App) ->
app_test(App, Mode) ->
test_server_sup:app_test(App, Mode).
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% appup_test/1
+%%
+appup_test(App) ->
+ test_server_sup:appup_test(App).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% is_native(Mod) -> true | false
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index e24d6ceacb..5fbc47a813 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -487,6 +487,7 @@ init([]) ->
ok
end,
test_server_sup:cleanup_crash_dumps(),
+ test_server_sup:util_start(),
State = #state{jobs=[],finish=false},
TI0 = test_server:init_target_info(),
TargetHost = test_server_sup:hoststr(),
@@ -1055,6 +1056,7 @@ handle_info(_, State) ->
%% test suites (if any) and any possible remainting slave node
terminate(_Reason, State) ->
+ test_server_sup:util_stop(),
case State#state.trc of
false -> ok;
Sock -> test_server_node:stop_tracer_node(Sock)
@@ -1405,7 +1407,7 @@ remove_conf([{conf, _Ref, Props, _MF}|Cases], NoConf, Repeats) ->
end;
remove_conf([{make,_Ref,_MF}|Cases], NoConf, Repeats) ->
remove_conf(Cases, NoConf, Repeats);
-remove_conf([{skip_case,{{_M,all},_Cmt}}|Cases], NoConf, Repeats) ->
+remove_conf([{skip_case,{{_M,all},_Cmt},_Mode}|Cases], NoConf, Repeats) ->
remove_conf(Cases, NoConf, Repeats);
remove_conf([{skip_case,{Type,_Ref,_MF,_Cmt}}|Cases],
NoConf, Repeats) when Type==conf;
@@ -1429,7 +1431,7 @@ remove_conf([], NoConf, true) ->
remove_conf([], NoConf, false) ->
lists:reverse(NoConf).
-get_suites([{skip_case,{{Mod,_Func},_Cmt}}|Tests], Mods) when is_atom(Mod) ->
+get_suites([{skip_case,{{Mod,_F},_Cmt},_Mode}|Tests], Mods) when is_atom(Mod) ->
case add_mod(Mod, Mods) of
true -> get_suites(Tests, [Mod|Mods]);
false -> get_suites(Tests, Mods)
@@ -1725,30 +1727,33 @@ make_html_link(LinkName, Target, Explanation) ->
ok = write_html_file(LinkName, H).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% start_minor_log_file(Mod, Func) -> AbsName
+%% start_minor_log_file(Mod, Func, ParallelTC) -> AbsName
%% Mod = atom()
%% Func = atom()
+%% ParallelTC = bool()
%% AbsName = string()
%%
%% Create a minor log file for the test case Mod,Func,Args. The log file
-%% will be stored in the log directory under the name <Mod>.<Func>.log.
-%% Some header info will also be inserted into the log file.
+%% will be stored in the log directory under the name <Mod>.<Func>.html.
+%% Some header info will also be inserted into the log file. If the test
+%% case runs in a parallel group, then to avoid clashing file names if the
+%% case is executed more than once, the name <Mod>.<Func>.<Timestamp>.html
+%% is used.
-start_minor_log_file(Mod, Func) ->
+start_minor_log_file(Mod, Func, ParallelTC) ->
MFA = {Mod,Func,1},
LogDir = get(test_server_log_dir_base),
Name0 = lists:flatten(io_lib:format("~w.~w~ts", [Mod,Func,?html_ext])),
Name = downcase(Name0),
AbsName = filename:join(LogDir, Name),
- case file:read_file_info(AbsName) of
- {error,_} -> %% normal case, unique name
+ case (ParallelTC orelse (element(1,file:read_file_info(AbsName))==ok)) of
+ false -> %% normal case, unique name
start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA);
- {ok,_} -> %% special case, duplicate names
- {_,S,Us} = now(),
+ true -> %% special case, duplicate names
+ Tag = test_server_sup:unique_name(),
Name1_0 =
- lists:flatten(io_lib:format("~w.~w.~w.~w~ts", [Mod,Func,S,
- trunc(Us/1000),
- ?html_ext])),
+ lists:flatten(io_lib:format("~w.~w.~ts~ts", [Mod,Func,Tag,
+ ?html_ext])),
Name1 = downcase(Name1_0),
AbsName1 = filename:join(LogDir, Name1),
start_minor_log_file1(Mod, Func, LogDir, AbsName1, MFA)
@@ -1828,7 +1833,7 @@ html_isolate_modules(List, FwMod) ->
html_isolate_modules(List, sets:new(), FwMod).
html_isolate_modules([], Set, _) -> sets:to_list(Set);
-html_isolate_modules([{skip_case,_}|Cases], Set, FwMod) ->
+html_isolate_modules([{skip_case,{_Case,_Cmt},_Mode}|Cases], Set, FwMod) ->
html_isolate_modules(Cases, Set, FwMod);
html_isolate_modules([{conf,_Ref,Props,{FwMod,_Func}}|Cases], Set, FwMod) ->
Set1 = case proplists:get_value(suite, Props) of
@@ -1932,26 +1937,30 @@ copy_html_file(Src, DestDir) ->
add_init_and_end_per_suite([{make,_,_}=Case|Cases], LastMod, LastRef, FwMod) ->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
-add_init_and_end_per_suite([{skip_case,{{Mod,all},_}}=Case|Cases], LastMod,
+add_init_and_end_per_suite([{skip_case,{{Mod,all},_},_}=Case|Cases], LastMod,
LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
-add_init_and_end_per_suite([{skip_case,{{Mod,_},_}}=Case|Cases], LastMod,
- LastRef, FwMod) when Mod =/= LastMod ->
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
+add_init_and_end_per_suite([{skip_case,{{Mod,_},_Cmt},_Mode}=Case|Cases],
+ LastMod, LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
-add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_},_}=Case|Cases], LastMod,
- LastRef, FwMod) when Mod =/= LastMod ->
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
+add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_},_}=Case|Cases],
+ LastMod, LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_}}=Case|Cases], LastMod,
LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([{conf,Ref,Props,{FwMod,Func}}=Case|Cases], LastMod,
LastRef, FwMod) ->
%% if Mod == FwMod, this conf test is (probably) a test case group where
@@ -1972,7 +1981,8 @@ add_init_and_end_per_suite([{conf,_,_,{Mod,_}}=Case|Cases], LastMod,
LastRef, FwMod) when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([SkipCase|Cases], LastMod, LastRef, FwMod)
when element(1,SkipCase) == skip_case ->
[SkipCase|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
@@ -1982,12 +1992,14 @@ add_init_and_end_per_suite([{Mod,_}=Case|Cases], LastMod, LastRef, FwMod)
when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([{Mod,_,_}=Case|Cases], LastMod, LastRef, FwMod)
when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([Case|Cases], LastMod, LastRef, FwMod)->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
add_init_and_end_per_suite([], _LastMod, undefined, _FwMod) ->
@@ -2164,7 +2176,7 @@ run_test_cases(TestSpec, Config, TimetrapData) ->
%% comment (which gets printed in the log files) describes why the case
%% was skipped.
%%
-%% {skip_case,{Case,Comment}} A normal test case skipped by the user.
+%% {skip_case,{Case,Comment},Mode} A normal test case skipped by the user.
%% The comment (which gets printed in the log files) describes why the
%% case was skipped.
%%
@@ -2337,7 +2349,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases],
ParentRef ->
Reason = {group_result,GrName,failed},
skip_cases_upto(ParentRef, Cases,
- Reason, tc, Mode,
+ Reason, tc, ParentMode,
SkipTag)
end;
false ->
@@ -2396,22 +2408,27 @@ run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(auto, undefined, get(test_server_case_num)+1,
Case, Comment, is_io_buffered(), SkipMode),
- test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
+ test_server_sup:framework_call(report, [tc_auto_skip,
+ {Mod,{Func,get_name(SkipMode)},
+ Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
-run_test_cases_loop([{skip_case,{{Mod,all}=Case,Comment}}|Cases],
+run_test_cases_loop([{skip_case,{{Mod,all}=Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
- skip_case(user, undefined, 0, Case, Comment, false, Mode),
+ skip_case(user, undefined, 0, Case, Comment, false, SkipMode),
test_server_sup:framework_call(report, [tc_user_skip,
- {Mod,all,Comment}]),
+ {Mod,{all,get_name(SkipMode)},
+ Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status);
-run_test_cases_loop([{skip_case,{Case,Comment}}|Cases],
+run_test_cases_loop([{skip_case,{Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(user, undefined, get(test_server_case_num)+1,
- Case, Comment, is_io_buffered()),
- test_server_sup:framework_call(report, [tc_user_skip,{Mod,Func,Comment}]),
+ Case, Comment, is_io_buffered(), SkipMode),
+ test_server_sup:framework_call(report, [tc_user_skip,
+ {Mod,{Func,get_name(SkipMode)},
+ Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
@@ -2425,8 +2442,9 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{Ref,Ref} ->
case check_props(parallel, tl(Mode0)) of
false ->
- %% this is an end conf for a top level parallel group, collect
- %% results from the test case processes and calc total time
+ %% this is an end conf for a top level parallel group,
+ %% collect results from the test case processes
+ %% and calc total time
OkSkipFail = handle_test_case_io_and_status(),
file:set_cwd(filename:dirname(get(test_server_dir))),
After = ?now,
@@ -2950,7 +2968,6 @@ get_tc_results([{_,{OkSkipFail,_}} | _Status]) ->
get_tc_results([]) -> % in case init_per_suite crashed
{[],[],[]}.
-
conf(Ref, Props) ->
{Ref,Props,?now}.
@@ -3155,10 +3172,6 @@ random_order(N, {Pos,NewSeed}, IxCases, Shuffled) ->
%% SendSync determines if start and finished messages must be sent so
%% that the printouts can be buffered and handled in order with io from
%% parallel processes.
-
-skip_case(Type, Ref, CaseNum, Case, Comment, SendSync) ->
- skip_case(Type, Ref, CaseNum, Case, Comment, SendSync, []).
-
skip_case(Type, Ref, CaseNum, Case, Comment, SendSync, Mode) ->
MF = {Mod,Func} = case Case of
{M,F,_A} -> {M,F};
@@ -3236,7 +3249,7 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
%% SkipType = skip_case | auto_skip_case
%% Mark all cases tagged with Ref as skipped.
-skip_cases_upto(Ref, Cases, Reason, Origin, Mode, SkipType) ->
+skip_cases_upto(Ref, Cases, Reason, Origin, Mode, SkipType) ->
{_,Modified,Rest} =
modify_cases_upto(Ref, {skip,Reason,Origin,Mode,SkipType}, Cases),
Modified++Rest.
@@ -3321,21 +3334,34 @@ modify_cases_upto1(Ref, {copy,NewRef},
{[C|Orig],[{skip_case,{Type,NewRef,MF,Cmt}}|Alt],T};
%% next is a skip_case, could be one test case or 'all' in suite, we must proceed
-modify_cases_upto1(Ref, ModOp, [{skip_case,{_F,_Cmt}}=MF|T], Orig, Alt) ->
+modify_cases_upto1(Ref, ModOp, [{skip_case,{_F,_Cmt},_Mode}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, ModOp, T, [MF|Orig], [MF|Alt]);
%% next is a normal case (possibly in a sequence), mark as skipped, or copy, and proceed
-modify_cases_upto1(Ref, {skip,Reason,_,_,skip_case}=Op,
+modify_cases_upto1(Ref, {skip,Reason,_,Mode,skip_case}=Op,
[{_M,_F}=MF|T], Orig, Alt) ->
- modify_cases_upto1(Ref, Op, T, Orig, [{skip_case,{MF,Reason}}|Alt]);
+ modify_cases_upto1(Ref, Op, T, Orig, [{skip_case,{MF,Reason},Mode}|Alt]);
modify_cases_upto1(Ref, {skip,Reason,_,Mode,auto_skip_case}=Op,
[{_M,_F}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, Op, T, Orig, [{auto_skip_case,{MF,Reason},Mode}|Alt]);
modify_cases_upto1(Ref, CopyOp, [{_M,_F}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, CopyOp, T, [MF|Orig], [MF|Alt]);
+%% next is a conf case, modify the Mode arg to keep track of sub groups
+modify_cases_upto1(Ref, {skip,Reason,FType,Mode,SkipType},
+ [{conf,OtherRef,Props,_MF}|T], Orig, Alt) ->
+ case hd(Mode) of
+ {OtherRef,_,_} -> % end conf
+ modify_cases_upto1(Ref, {skip,Reason,FType,tl(Mode),SkipType},
+ T, Orig, Alt);
+ _ -> % start conf
+ Mode1 = [conf(OtherRef,Props)|Mode],
+ modify_cases_upto1(Ref, {skip,Reason,FType,Mode1,SkipType},
+ T, Orig, Alt)
+ end;
+
%% next is some other case, ignore or copy
-modify_cases_upto1(Ref, {skip,_,_,_,_}=Op, [_|T], Orig, Alt) ->
+modify_cases_upto1(Ref, {skip,_,_,_,_}=Op, [_Other|T], Orig, Alt) ->
modify_cases_upto1(Ref, Op, T, Orig, Alt);
modify_cases_upto1(Ref, CopyOp, [C|T], Orig, Alt) ->
modify_cases_upto1(Ref, CopyOp, T, [C|Orig], [C|Alt]).
@@ -3631,7 +3657,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
TSDir = get(test_server_dir),
print(major, "=case ~w:~w", [Mod, Func]),
- MinorName = start_minor_log_file(Mod, Func),
+ MinorName = start_minor_log_file(Mod, Func, self() /= Main),
print(minor, "<a name=\"top\"></a>", [], internal_raw),
MinorBase = filename:basename(MinorName),
print(major, "=logfile ~ts", [filename:basename(MinorName)]),
@@ -3660,12 +3686,13 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
update_config(hd(Args), [{priv_dir,PrivDir++"/"},
{tc_logfile,MinorName}])
end,
-
+ GrName = get_name(Mode),
test_server_sup:framework_call(report,
- [tc_start,{{Mod,Func},MinorName}]),
+ [tc_start,{{Mod,{Func,GrName}},
+ MinorName}]),
print_props((RunInit==skip_init), get_props(Mode)),
- GroupName = case get_name(Mode) of
+ GrNameStr = case GrName of
undefined -> "";
Name -> cast_to_list(Name)
end,
@@ -3678,14 +3705,14 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
"<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
"<td><a href=\"~ts\">~w</a></td>"
"<td><a href=\"~ts#top\"><</a> <a href=\"~ts#end\">></a></td>",
- [num2str(Num),fw_name(Mod),GroupName,EncMinorBase,Func,
+ [num2str(Num),fw_name(Mod),GrNameStr,EncMinorBase,Func,
EncMinorBase,EncMinorBase]),
do_unless_parallel(Main, fun erlang:yield/0),
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
- run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode),
+ run_test_case_apply(Num, Mod, Func, [UpdatedArgs], GrName,
RunInit, TimetrapData),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
@@ -3704,41 +3731,41 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
Status =
case {Time,RetVal} of
{died,{timetrap_timeout,TimetrapTimeout}} ->
- progress(failed, Num, Mod, Func, Loc,
+ progress(failed, Num, Mod, Func, GrName, Loc,
timetrap_timeout, TimetrapTimeout, Comment, Style);
{died,Reason} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',{Skip,Reason}}} when Skip==skip; Skip==skipped;
Skip==auto_skip ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',_Pid,{Skip,Reason}}} when Skip==skip; Skip==skipped ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',_Pid,Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{Fail,Reason}} when Fail =:= fail; Fail =:= failed ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,Reason={auto_skip,_Why}} ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{Skip,Reason}} when Skip==skip; Skip==skipped ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{Time,RetVal} ->
case DetectedFail of
[] ->
- progress(ok, Num, Mod, Func, Loc, RetVal,
+ progress(ok, Num, Mod, Func, GrName, Loc, RetVal,
Time, Comment, Style);
Reason ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style)
end
end,
@@ -3843,7 +3870,7 @@ num2str(N) -> integer_to_list(N).
%% Note: Strings that are to be written to the minor log must
%% be prefixed with "=== " here, or the indentation will be wrong.
-progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
+progress(skip, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
Comment, {St0,St1}) ->
{Reason1,{Color,Ret,ReportTag}} =
if_auto_skip(Reason,
@@ -3852,7 +3879,7 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
print(major, "=result ~w: ~p", [ReportTag,Reason1]),
print(1, "*** SKIPPED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{ReportTag,Reason1}}]),
ReasonStr = reason_to_string(Reason1),
ReasonStr1 = lists:flatten([string:strip(S,left) ||
@@ -3877,13 +3904,13 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
print(minor, "=== reason = ~ts", [ReasonStr1]),
Ret;
-progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
+progress(failed, CaseNum, Mod, Func, GrName, Loc, timetrap_timeout, T,
Comment0, {St0,St1}) ->
print(major, "=result failed: timeout, ~p", [Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
- [tc_done,{Mod,Func,
+ [tc_done,{Mod,{Func,GrName},
{failed,timetrap_timeout}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{timetrap_timeout,~ts}", [FormatLastLoc]),
@@ -3903,13 +3930,13 @@ progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
print(minor, "=== reason = timetrap timeout", []),
failed;
-progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
+progress(failed, CaseNum, Mod, Func, GrName, Loc, {testcase_aborted,Reason}, _T,
Comment0, {St0,St1}) ->
print(major, "=result failed: testcase_aborted, ~p", [Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
- [tc_done,{Mod,Func,
+ [tc_done,{Mod,{Func,GrName},
{failed,testcase_aborted}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{testcase_aborted,~ts}", [FormatLastLoc]),
@@ -3929,12 +3956,12 @@ progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
print(minor, "=== reason = {testcase_aborted,~p}", [Reason]),
failed;
-progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
+progress(failed, CaseNum, Mod, Func, GrName, unknown, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~w", [Reason,unknown]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
@@ -3965,12 +3992,12 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
print(minor, "=== reason = " ++ FStr, [FormattedReason]),
failed;
-progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
+progress(failed, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~p", [Reason,Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
@@ -3992,10 +4019,10 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
print(minor, "=== reason = " ++ FStr, [FormattedReason]),
failed;
-progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time,
+progress(ok, _CaseNum, Mod, Func, GrName, _Loc, RetVal, Time,
Comment0, {St0,St1}) ->
print(minor, "successfully completed test case", []),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,ok}]),
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},ok}]),
Comment =
case RetVal of
{comment,RetComment} ->
@@ -4484,18 +4511,18 @@ update_config(Config, []) ->
collect_all_cases(Top, Skip) when is_list(Skip) ->
Result =
- case collect_cases(Top, #cc{mod=[],skip=Skip}) of
+ case collect_cases(Top, #cc{mod=[],skip=Skip}, []) of
{ok,Cases,_St} -> Cases;
Other -> Other
end,
Result.
-collect_cases([], St) -> {ok,[],St};
-collect_cases([Case|Cs0], St0) ->
- case collect_cases(Case, St0) of
+collect_cases([], St, _) -> {ok,[],St};
+collect_cases([Case|Cs0], St0, Mode) ->
+ case collect_cases(Case, St0, Mode) of
{ok,FlatCases1,St1} ->
- case collect_cases(Cs0, St1) of
+ case collect_cases(Cs0, St1, Mode) of
{ok,FlatCases2,St} ->
{ok,FlatCases1 ++ FlatCases2,St};
{error,_Reason} = Error -> Error
@@ -4504,39 +4531,41 @@ collect_cases([Case|Cs0], St0) ->
end;
-collect_cases({module,Case}, St) when is_atom(Case), is_atom(St#cc.mod) ->
- collect_case({St#cc.mod,Case}, St);
-collect_cases({module,Mod,Case}, St) ->
- collect_case({Mod,Case}, St);
-collect_cases({module,Mod,Case,Args}, St) ->
- collect_case({Mod,Case,Args}, St);
-
-collect_cases({dir,SubDir}, St) ->
- collect_files(SubDir, "*_SUITE", St);
-collect_cases({dir,SubDir,Pattern}, St) ->
- collect_files(SubDir, Pattern++"*", St);
-
-collect_cases({conf,InitF,CaseList,FinMF}, St) when is_atom(InitF) ->
- collect_cases({conf,[],{St#cc.mod,InitF},CaseList,FinMF}, St);
-collect_cases({conf,InitMF,CaseList,FinF}, St) when is_atom(FinF) ->
- collect_cases({conf,[],InitMF,CaseList,{St#cc.mod,FinF}}, St);
-collect_cases({conf,InitMF,CaseList,FinMF}, St0) ->
- collect_cases({conf,[],InitMF,CaseList,FinMF}, St0);
-collect_cases({conf,Props,InitF,CaseList,FinMF}, St) when is_atom(InitF) ->
+collect_cases({module,Case}, St, Mode) when is_atom(Case), is_atom(St#cc.mod) ->
+ collect_case({St#cc.mod,Case}, St, Mode);
+collect_cases({module,Mod,Case}, St, Mode) ->
+ collect_case({Mod,Case}, St, Mode);
+collect_cases({module,Mod,Case,Args}, St, Mode) ->
+ collect_case({Mod,Case,Args}, St, Mode);
+
+collect_cases({dir,SubDir}, St, Mode) ->
+ collect_files(SubDir, "*_SUITE", St, Mode);
+collect_cases({dir,SubDir,Pattern}, St, Mode) ->
+ collect_files(SubDir, Pattern++"*", St, Mode);
+
+collect_cases({conf,InitF,CaseList,FinMF}, St, Mode) when is_atom(InitF) ->
+ collect_cases({conf,[],{St#cc.mod,InitF},CaseList,FinMF}, St, Mode);
+collect_cases({conf,InitMF,CaseList,FinF}, St, Mode) when is_atom(FinF) ->
+ collect_cases({conf,[],InitMF,CaseList,{St#cc.mod,FinF}}, St, Mode);
+collect_cases({conf,InitMF,CaseList,FinMF}, St0, Mode) ->
+ collect_cases({conf,[],InitMF,CaseList,FinMF}, St0, Mode);
+collect_cases({conf,Props,InitF,CaseList,FinMF}, St, Mode) when is_atom(InitF) ->
case init_props(Props) of
{error,_} ->
{ok,[],St};
Props1 ->
- collect_cases({conf,Props1,{St#cc.mod,InitF},CaseList,FinMF}, St)
+ collect_cases({conf,Props1,{St#cc.mod,InitF},CaseList,FinMF},
+ St, Mode)
end;
-collect_cases({conf,Props,InitMF,CaseList,FinF}, St) when is_atom(FinF) ->
+collect_cases({conf,Props,InitMF,CaseList,FinF}, St, Mode) when is_atom(FinF) ->
case init_props(Props) of
{error,_} ->
{ok,[],St};
Props1 ->
- collect_cases({conf,Props1,InitMF,CaseList,{St#cc.mod,FinF}}, St)
+ collect_cases({conf,Props1,InitMF,CaseList,{St#cc.mod,FinF}},
+ St, Mode)
end;
-collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
+collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St, Mode) ->
case init_props(Props) of
{error,_} ->
{ok,[],St};
@@ -4544,13 +4573,13 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
Ref = make_ref(),
Skips = St#cc.skip,
Props2 = [{suite,St#cc.mod} | lists:delete(suite,Props1)],
- Mode = [{Ref,Props2,undefined}],
+ Mode1 = [{Ref,Props2,undefined} | Mode],
case in_skip_list({St#cc.mod,Conf}, Skips) of
{true,Comment} -> % conf init skipped
- {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode} |
+ {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode1} |
[] ++ [{conf,Ref,[],FinMF}]],St};
{true,Name,Comment} when is_atom(Name) -> % all cases skipped
- case collect_cases(CaseList, St) of
+ case collect_cases(CaseList, St, Mode1) of
{ok,[],_St} = Empty ->
Empty;
{ok,FlatCases,St1} ->
@@ -4558,15 +4587,15 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
keep_name(Props1),
FinMF}],
Skipped = skip_cases_upto(Ref, Cases2Skip, Comment,
- conf, Mode, skip_case),
- {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode} |
+ conf, Mode1, skip_case),
+ {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode1} |
Skipped],St1};
{error,_Reason} = Error ->
Error
end;
{true,ToSkip,_} when is_list(ToSkip) -> % some cases skipped
case collect_cases(CaseList,
- St#cc{skip=ToSkip++Skips}) of
+ St#cc{skip=ToSkip++Skips}, Mode1) of
{ok,[],_St} = Empty ->
Empty;
{ok,FlatCases,St1} ->
@@ -4578,7 +4607,7 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
Error
end;
false ->
- case collect_cases(CaseList, St) of
+ case collect_cases(CaseList, St, Mode1) of
{ok,[],_St} = Empty ->
Empty;
{ok,FlatCases,St1} ->
@@ -4592,8 +4621,8 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
end
end;
-collect_cases({make,InitMFA,CaseList,FinMFA}, St0) ->
- case collect_cases(CaseList, St0) of
+collect_cases({make,InitMFA,CaseList,FinMFA}, St0, Mode) ->
+ case collect_cases(CaseList, St0, Mode) of
{ok,[],_St} = Empty -> Empty;
{ok,FlatCases,St} ->
Ref = make_ref(),
@@ -4602,62 +4631,62 @@ collect_cases({make,InitMFA,CaseList,FinMFA}, St0) ->
{error,_Reason} = Error -> Error
end;
-collect_cases({Module, Cases}, St) when is_list(Cases) ->
- case (catch collect_case(Cases, St#cc{mod=Module}, [])) of
+collect_cases({Module, Cases}, St, Mode) when is_list(Cases) ->
+ case (catch collect_case(Cases, St#cc{mod=Module}, [], Mode)) of
{ok, NewCases, NewSt} ->
{ok, NewCases, NewSt};
Other ->
{error, Other}
end;
-collect_cases({_Mod,_Case}=Spec, St) ->
- collect_case(Spec, St);
+collect_cases({_Mod,_Case}=Spec, St, Mode) ->
+ collect_case(Spec, St, Mode);
-collect_cases({_Mod,_Case,_Args}=Spec, St) ->
- collect_case(Spec, St);
-collect_cases(Case, St) when is_atom(Case), is_atom(St#cc.mod) ->
- collect_case({St#cc.mod,Case}, St);
-collect_cases(Other, St) ->
+collect_cases({_Mod,_Case,_Args}=Spec, St, Mode) ->
+ collect_case(Spec, St, Mode);
+collect_cases(Case, St, Mode) when is_atom(Case), is_atom(St#cc.mod) ->
+ collect_case({St#cc.mod,Case}, St, Mode);
+collect_cases(Other, St, _Mode) ->
{error,{bad_subtest_spec,St#cc.mod,Other}}.
-collect_case({Mod,{conf,_,_,_,_}=Conf}, St) ->
- collect_case_invoke(Mod, Conf, [], St);
+collect_case({Mod,{conf,_,_,_,_}=Conf}, St, Mode) ->
+ collect_case_invoke(Mod, Conf, [], St, Mode);
-collect_case(MFA, St) ->
+collect_case(MFA, St, Mode) ->
case in_skip_list(MFA, St#cc.skip) of
{true,Comment} ->
- {ok,[{skip_case,{MFA,Comment}}],St};
+ {ok,[{skip_case,{MFA,Comment},Mode}],St};
false ->
case MFA of
- {Mod,Case} -> collect_case_invoke(Mod, Case, MFA, St);
+ {Mod,Case} -> collect_case_invoke(Mod, Case, MFA, St, Mode);
{_Mod,_Case,_Args} -> {ok,[MFA],St}
end
end.
-collect_case([], St, Acc) ->
+collect_case([], St, Acc, _Mode) ->
{ok, Acc, St};
-collect_case([Case | Cases], St, Acc) ->
- {ok, FlatCases, NewSt} = collect_case({St#cc.mod, Case}, St),
- collect_case(Cases, NewSt, Acc ++ FlatCases).
+collect_case([Case | Cases], St, Acc, Mode) ->
+ {ok, FlatCases, NewSt} = collect_case({St#cc.mod, Case}, St, Mode),
+ collect_case(Cases, NewSt, Acc ++ FlatCases, Mode).
-collect_case_invoke(Mod, Case, MFA, St) ->
+collect_case_invoke(Mod, Case, MFA, St, Mode) ->
case get_fw_mod(undefined) of
undefined ->
case catch apply(Mod, Case, [suite]) of
{'EXIT',_} ->
{ok,[MFA],St};
Suite ->
- collect_subcases(Mod, Case, MFA, St, Suite)
+ collect_subcases(Mod, Case, MFA, St, Suite, Mode)
end;
_ ->
Suite = test_server_sup:framework_call(get_suite,
[Mod,Case],
[]),
- collect_subcases(Mod, Case, MFA, St, Suite)
+ collect_subcases(Mod, Case, MFA, St, Suite, Mode)
end.
-collect_subcases(Mod, Case, MFA, St, Suite) ->
+collect_subcases(Mod, Case, MFA, St, Suite, Mode) ->
case Suite of
[] when Case == all -> {ok,[],St};
[] when element(1, Case) == conf -> {ok,[],St};
@@ -4665,28 +4694,28 @@ collect_subcases(Mod, Case, MFA, St, Suite) ->
%%%! --- START Kept for backwards compatibility ---
%%%! Requirements are not used
{req,ReqList} ->
- collect_case_deny(Mod, Case, MFA, ReqList, [], St);
+ collect_case_deny(Mod, Case, MFA, ReqList, [], St, Mode);
{req,ReqList,SubCases} ->
- collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St);
+ collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St, Mode);
%%%! --- END Kept for backwards compatibility ---
{Skip,Reason} when Skip==skip; Skip==skipped ->
- {ok,[{skip_case,{MFA,Reason}}],St};
+ {ok,[{skip_case,{MFA,Reason},Mode}],St};
{error,Reason} ->
throw(Reason);
SubCases ->
- collect_case_subcases(Mod, Case, SubCases, St)
+ collect_case_subcases(Mod, Case, SubCases, St, Mode)
end.
-collect_case_subcases(Mod, Case, SubCases, St0) ->
+collect_case_subcases(Mod, Case, SubCases, St0, Mode) ->
OldMod = St0#cc.mod,
- case collect_cases(SubCases, St0#cc{mod=Mod}) of
+ case collect_cases(SubCases, St0#cc{mod=Mod}, Mode) of
{ok,FlatCases,St} ->
{ok,FlatCases,St#cc{mod=OldMod}};
{error,Reason} ->
{error,{{Mod,Case},Reason}}
end.
-collect_files(Dir, Pattern, St) ->
+collect_files(Dir, Pattern, St, Mode) ->
{ok,Cwd} = file:get_cwd(),
Dir1 = filename:join(Cwd, Dir),
Wc = filename:join([Dir1,Pattern++code:objfile_extension()]),
@@ -4696,7 +4725,7 @@ collect_files(Dir, Pattern, St) ->
{error,{collect_fail,Dir,Pattern}};
Mods0 ->
Mods = [{path_to_module(Mod),all} || Mod <- lists:sort(Mods0)],
- collect_cases(Mods, St)
+ collect_cases(Mods, St, Mode)
end.
path_to_module(Path) when is_list(Path) ->
@@ -4706,14 +4735,14 @@ path_to_module(Path) when is_list(Path) ->
%% anyway. It should be removed or renamed!
list_to_atom(filename:rootname(filename:basename(Path))).
-collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St) ->
+collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St, Mode) ->
case {check_deny(ReqList, St#cc.skip),SubCases} of
{{denied,Comment},_SubCases} ->
- {ok,[{skip_case,{MFA,Comment}}],St};
+ {ok,[{skip_case,{MFA,Comment},Mode}],St};
{granted,[]} ->
{ok,[MFA],St};
{granted,SubCases} ->
- collect_case_subcases(Mod, Case, SubCases, St)
+ collect_case_subcases(Mod, Case, SubCases, St, Mode)
end.
check_deny([Req|Reqs], DenyList) ->
@@ -5471,10 +5500,16 @@ write_html_file(File,Content) ->
%% The 'major' log file, which is a pure text file is also written
%% with utf8 encoding
open_utf8_file(File) ->
- file:open(File,[write,{encoding,utf8}]).
+ case file:open(File,AllOpts=[write,{encoding,utf8}]) of
+ {error,Reason} -> {error,{Reason,{File,AllOpts}}};
+ Result -> Result
+ end.
open_utf8_file(File,Opts) ->
- file:open(File,[{encoding,utf8}|Opts]).
+ case file:open(File,AllOpts=[{encoding,utf8}|Opts]) of
+ {error,Reason} -> {error,{Reason,{File,AllOpts}}};
+ Result -> Result
+ end.
%% Write a file with specified encoding
write_file(File,Content,latin1) ->
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 377aa21018..96e369a138 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. 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
@@ -29,10 +29,13 @@
hostatom/0, hostatom/1, hoststr/0, hoststr/1,
framework_call/2,framework_call/3,framework_call/4,
format_loc/1,
- call_trace/1]).
+ util_start/0, util_stop/0, unique_name/0,
+ call_trace/1,
+ appup_test/1]).
-include("test_server_internal.hrl").
-define(crash_dump_tar,"crash_dumps.tar.gz").
-define(src_listing_ext, ".src.html").
+-record(util_state, {starter, latest_name}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% timetrap(Timeout,Scale,Pid) -> Handle
@@ -263,6 +266,249 @@ app_check_export_all([Mod|Mods]) ->
end
end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% appup_test/1
+%%
+%% Checks one applications .appup file for obvious errors.
+%% Checks..
+%% * .. syntax
+%% * .. that version in app file matches appup file version
+%% * .. validity of appup instructions
+%%
+%% For library application this function checks that the proper
+%% 'restart_application' upgrade and downgrade clauses exist.
+appup_test(Application) ->
+ case is_app(Application) of
+ {ok, AppFile} ->
+ case is_appup(Application, proplists:get_value(vsn, AppFile)) of
+ {ok, Up, Down} ->
+ StartMod = proplists:get_value(mod, AppFile),
+ Modules = proplists:get_value(modules, AppFile),
+ do_appup_tests(StartMod, Application, Up, Down, Modules);
+ Error ->
+ test_server:fail(Error)
+ end;
+ Error ->
+ test_server:fail(Error)
+ end.
+
+is_appup(Application, Version) ->
+ AppupFile = atom_to_list(Application) ++ ".appup",
+ AppupPath = filename:join([code:lib_dir(Application), "ebin", AppupFile]),
+ case file:consult(AppupPath) of
+ {ok, [{Version, Up, Down}]} when is_list(Up), is_list(Down) ->
+ {ok, Up, Down};
+ _ ->
+ test_server:format(
+ minor,
+ "Application upgrade (.appup) file not found, "
+ "or it has very bad syntax.~n"),
+ {error, appup_not_readable}
+ end.
+
+do_appup_tests(undefined, Application, Up, Down, _Modules) ->
+ %% library application
+ case Up of
+ [{<<".*">>, [{restart_application, Application}]}] ->
+ case Down of
+ [{<<".*">>, [{restart_application, Application}]}] ->
+ ok;
+ _ ->
+ test_server:format(
+ minor,
+ "Library application needs restart_application "
+ "downgrade instruction.~n"),
+ {error, library_downgrade_instruction_malformed}
+ end;
+ _ ->
+ test_server:format(
+ minor,
+ "Library application needs restart_application "
+ "upgrade instruction.~n"),
+ {error, library_upgrade_instruction_malformed}
+ end;
+do_appup_tests(_, _Application, Up, Down, Modules) ->
+ %% normal application
+ case check_appup_clauses_plausible(Up, up, Modules) of
+ ok ->
+ case check_appup_clauses_plausible(Down, down, Modules) of
+ ok ->
+ test_server:format(minor, "OK~n");
+ Error ->
+ test_server:format(minor, "ERROR ~p~n", [Error]),
+ test_server:fail(Error)
+ end;
+ Error ->
+ test_server:format(minor, "ERROR ~p~n", [Error]),
+ test_server:fail(Error)
+ end.
+
+check_appup_clauses_plausible([], _Direction, _Modules) ->
+ ok;
+check_appup_clauses_plausible([{Re, Instrs} | Rest], Direction, Modules)
+ when is_binary(Re) ->
+ case re:compile(Re) of
+ {ok, _} ->
+ case check_appup_instructions(Instrs, Direction, Modules) of
+ ok ->
+ check_appup_clauses_plausible(Rest, Direction, Modules);
+ Error ->
+ Error
+ end;
+ {error, Error} ->
+ {error, {version_regex_malformed, Re, Error}}
+ end;
+check_appup_clauses_plausible([{V, Instrs} | Rest], Direction, Modules)
+ when is_list(V) ->
+ case check_appup_instructions(Instrs, Direction, Modules) of
+ ok ->
+ check_appup_clauses_plausible(Rest, Direction, Modules);
+ Error ->
+ Error
+ end;
+check_appup_clauses_plausible(Clause, _Direction, _Modules) ->
+ {error, {clause_malformed, Clause}}.
+
+check_appup_instructions(Instrs, Direction, Modules) ->
+ case check_instructions(Direction, Instrs, Instrs, [], [], Modules) of
+ {_Good, []} ->
+ ok;
+ {_, Bad} ->
+ {error, {bad_instructions, Bad}}
+ end.
+
+check_instructions(_, [], _, Good, Bad, _) ->
+ {lists:reverse(Good), lists:reverse(Bad)};
+check_instructions(UpDown, [Instr | Rest], All, Good, Bad, Modules) ->
+ case catch check_instruction(UpDown, Instr, All, Modules) of
+ ok ->
+ check_instructions(UpDown, Rest, All, [Instr | Good], Bad, Modules);
+ {error, Reason} ->
+ NewBad = [{Instr, Reason} | Bad],
+ check_instructions(UpDown, Rest, All, Good, NewBad, Modules)
+ end.
+
+check_instruction(up, {add_module, Module}, _, Modules) ->
+ %% A new module is added
+ check_module(Module, Modules);
+check_instruction(down, {add_module, Module}, _, Modules) ->
+ %% An old module is re-added
+ case (catch check_module(Module, Modules)) of
+ {error, {unknown_module, Module, Modules}} -> ok;
+ ok -> throw({error, {existing_readded_module, Module}})
+ end;
+check_instruction(_, {load_module, Module}, _, Modules) ->
+ check_module(Module, Modules);
+check_instruction(_, {load_module, Module, DepMods}, _, Modules) ->
+ check_module(Module, Modules),
+ check_depend(DepMods);
+check_instruction(_, {load_module, Module, Pre, Post, DepMods}, _, Modules) ->
+ check_module(Module, Modules),
+ check_depend(DepMods),
+ check_purge(Pre),
+ check_purge(Post);
+check_instruction(up, {delete_module, Module}, _, Modules) ->
+ case (catch check_module(Module, Modules)) of
+ {error, {unknown_module, Module, Modules}} ->
+ ok;
+ ok ->
+ throw({error,{existing_module_deleted, Module}})
+ end;
+check_instruction(down, {delete_module, Module}, _, Modules) ->
+ check_module(Module, Modules);
+check_instruction(_, {update, Module}, _, Modules) ->
+ check_module(Module, Modules);
+check_instruction(_, {update, Module, supervisor}, _, Modules) ->
+ check_module(Module, Modules);
+check_instruction(_, {update, Module, DepMods}, _, Modules)
+ when is_list(DepMods) ->
+ check_module(Module, Modules);
+check_instruction(_, {update, Module, Change}, _, Modules) ->
+ check_module(Module, Modules),
+ check_change(Change);
+check_instruction(_, {update, Module, Change, DepMods}, _, Modules) ->
+ check_module(Module, Modules),
+ check_change(Change),
+ check_depend(DepMods);
+check_instruction(_, {update, Module, Change, Pre, Post, DepMods}, _, Modules) ->
+ check_module(Module, Modules),
+ check_change(Change),
+ check_purge(Pre),
+ check_purge(Post),
+ check_depend(DepMods);
+check_instruction(_,
+ {update, Module, Timeout, Change, Pre, Post, DepMods},
+ _,
+ Modules) ->
+ check_module(Module, Modules),
+ check_timeout(Timeout),
+ check_change(Change),
+ check_purge(Pre),
+ check_purge(Post),
+ check_depend(DepMods);
+check_instruction(_,
+ {update, Module, ModType, Timeout, Change, Pre, Post, DepMods},
+ _,
+ Modules) ->
+ check_module(Module, Modules),
+ check_mod_type(ModType),
+ check_timeout(Timeout),
+ check_change(Change),
+ check_purge(Pre),
+ check_purge(Post),
+ check_depend(DepMods);
+check_instruction(_, {restart_application, Application}, _, _) ->
+ check_application(Application);
+check_instruction(_, {remove_application, Application}, _, _) ->
+ check_application(Application);
+check_instruction(_, {add_application, Application}, _, _) ->
+ check_application(Application);
+check_instruction(_, {add_application, Application, Type}, _, _) ->
+ check_application(Application),
+ check_restart_type(Type);
+check_instruction(_, Instr, _, _) ->
+ throw({error, {low_level_or_invalid_instruction, Instr}}).
+
+check_module(Module, Modules) ->
+ case {is_atom(Module), lists:member(Module, Modules)} of
+ {true, true} -> ok;
+ {true, false} -> throw({error, {unknown_module, Module}});
+ {false, _} -> throw({error, {bad_module, Module}})
+ end.
+
+check_application(App) ->
+ case is_atom(App) of
+ true -> ok;
+ false -> throw({error, {bad_application, App}})
+ end.
+
+check_depend(Dep) when is_list(Dep) -> ok;
+check_depend(Dep) -> throw({error, {bad_depend, Dep}}).
+
+check_restart_type(permanent) -> ok;
+check_restart_type(transient) -> ok;
+check_restart_type(temporary) -> ok;
+check_restart_type(load) -> ok;
+check_restart_type(none) -> ok;
+check_restart_type(Type) -> throw({error, {bad_restart_type, Type}}).
+
+check_timeout(T) when is_integer(T), T > 0 -> ok;
+check_timeout(default) -> ok;
+check_timeout(infinity) -> ok;
+check_timeout(T) -> throw({error, {bad_timeout, T}}).
+
+check_mod_type(static) -> ok;
+check_mod_type(dynamic) -> ok;
+check_mod_type(Type) -> throw({error, {bad_mod_type, Type}}).
+
+check_purge(soft_purge) -> ok;
+check_purge(brutal_purge) -> ok;
+check_purge(Purge) -> throw({error, {bad_purge, Purge}}).
+
+check_change(soft) -> ok;
+check_change({advanced, _}) -> ok;
+check_change(Change) -> throw({error, {bad_change, Change}}).
+
%% Given two sorted lists, L1 and L2, returns {NotInL2, NotInL1},
%% NotInL2 is the elements of L1 which don't occurr in L2,
%% NotInL1 is the elements of L2 which don't ocurr in L1.
@@ -583,6 +829,69 @@ downcase([], Result) ->
lists:reverse(Result).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% util_start() -> ok
+%%
+%% Start local utility process
+util_start() ->
+ Starter = self(),
+ case whereis(?MODULE) of
+ undefined ->
+ spawn_link(fun() ->
+ register(?MODULE, self()),
+ util_loop(#util_state{starter=Starter})
+ end);
+ _Pid ->
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% util_stop() -> ok
+%%
+%% Stop local utility process
+util_stop() ->
+ try (?MODULE ! {self(),stop}) of
+ _ ->
+ receive {?MODULE,stopped} -> ok
+ after 5000 -> exit(whereis(?MODULE), kill)
+ end
+ catch
+ _:_ ->
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% unique_name() -> string()
+%%
+unique_name() ->
+ ?MODULE ! {self(),unique_name},
+ receive {?MODULE,Name} -> Name
+ after 5000 -> exit({?MODULE,no_util_process})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% util_loop(State) -> ok
+%%
+util_loop(State) ->
+ receive
+ {From,unique_name} ->
+ {_,S,Us} = now(),
+ Ms = trunc(Us/1000),
+ Name = lists:flatten(io_lib:format("~w.~w", [S,Ms])),
+ if Name == State#util_state.latest_name ->
+ timer:sleep(1),
+ self() ! {From,unique_name},
+ util_loop(State);
+ true ->
+ From ! {?MODULE,Name},
+ util_loop(State#util_state{latest_name = Name})
+ end;
+ {From,stop} ->
+ catch unlink(State#util_state.starter),
+ From ! {?MODULE,stopped},
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% call_trace(TraceSpecFile) -> ok
%%
%% Read terms on format {m,Mod} | {f,Mod,Func}
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 189a71a8ce..bc7d244c7c 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -24,7 +24,7 @@
-module(ts).
--export([run/0, run/1, run/2, run/3, run/4,
+-export([run/0, run/1, run/2, run/3, run/4, run/5,
tests/0, tests/1,
install/0, install/1,
bench/0, bench/1, bench/2, benchmarks/0,
@@ -212,6 +212,12 @@ run_all(_Vars) ->
run_some([], _Opts) ->
ok;
+run_some([{Spec,Mod}|Specs], Opts) ->
+ case run(Spec, Mod, Opts) of
+ ok -> ok;
+ Error -> io:format("~p: ~p~n",[{Spec,Mod},Error])
+ end,
+ run_some(Specs, Opts);
run_some([Spec|Specs], Opts) ->
case run(Spec, Opts) of
ok -> ok;
@@ -263,8 +269,17 @@ run(List, Opts) when is_list(List), is_list(Opts) ->
run_some(List, Opts);
%% run/2
-%% Runs one test spec with Options
-run(Testspec, Config) when is_atom(Testspec), is_list(Config) ->
+%% Runs one test spec with list of suites or with options
+run(Testspec, ModsOrConfig) when is_atom(Testspec),
+ is_list(ModsOrConfig) ->
+ case is_list_of_suites(ModsOrConfig) of
+ false ->
+ run(Testspec, {config_list,ModsOrConfig});
+ true ->
+ run_some([{Testspec,M} || M <- ModsOrConfig],
+ [batch])
+ end;
+run(Testspec, {config_list,Config}) ->
Options=check_test_get_opts(Testspec, Config),
IsSmoke=proplists:get_value(smoke,Config),
File=atom_to_list(Testspec),
@@ -310,34 +325,95 @@ run(Testspec, Config) when is_atom(Testspec), is_list(Config) ->
run_test(File, [{spec,[Spec]}], Options);
%% Runs one module in a spec (interactive)
run(Testspec, Mod) when is_atom(Testspec), is_atom(Mod) ->
- run_test({atom_to_list(Testspec), Mod},
+ run_test({atom_to_list(Testspec),Mod},
[{suite,Mod}],
[interactive]).
%% run/3
%% Run one module in a spec with Config
-run(Testspec,Mod,Config) when is_atom(Testspec), is_atom(Mod), is_list(Config) ->
+run(Testspec, Mod, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
- run_test({atom_to_list(Testspec), Mod},
- [{suite,Mod}],
- Options);
-
-%% Runs one testcase in a module.
-run(Testspec, Mod, Case) when is_atom(Testspec), is_atom(Mod), is_atom(Case) ->
+ run_test({atom_to_list(Testspec),Mod},
+ [{suite,Mod}], Options);
+%% Run multiple modules with Config
+run(Testspec, Mods, Config) when is_atom(Testspec),
+ is_list(Mods),
+ is_list(Config) ->
+ run_some([{Testspec,M} || M <- Mods], Config);
+%% Runs one test case in a module.
+run(Testspec, Mod, Case) when is_atom(Testspec),
+ is_atom(Mod),
+ is_atom(Case) ->
+ Options=check_test_get_opts(Testspec, []),
+ Args = [{suite,Mod},{testcase,Case}],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Runs one or more groups in a module.
+run(Testspec, Mod, Grs={group,_Groups}) when is_atom(Testspec),
+ is_atom(Mod) ->
Options=check_test_get_opts(Testspec, []),
- Args = [{suite,atom_to_list(Mod)},{testcase,atom_to_list(Case)}],
+ Args = [{suite,Mod},Grs],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Runs one or more test cases in a module.
+run(Testspec, Mod, TCs={testcase,_Cases}) when is_atom(Testspec),
+ is_atom(Mod) ->
+ Options=check_test_get_opts(Testspec, []),
+ Args = [{suite,Mod},TCs],
run_test(atom_to_list(Testspec), Args, Options).
%% run/4
-%% Run one testcase in a module with Options.
+%% Run one test case in a module with Options.
run(Testspec, Mod, Case, Config) when is_atom(Testspec),
is_atom(Mod),
is_atom(Case),
is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
- Args = [{suite,atom_to_list(Mod)}, {testcase,atom_to_list(Case)}],
+ Args = [{suite,Mod},{testcase,Case}],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Run one or more test cases in a module with Options.
+run(Testspec, Mod, {testcase,Cases}, Config) when is_atom(Testspec),
+ is_atom(Mod) ->
+ run(Testspec, Mod, Cases, Config);
+run(Testspec, Mod, Cases, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_list(Cases),
+ is_list(Config) ->
+ Options=check_test_get_opts(Testspec, Config),
+ Args = [{suite,Mod},Cases],
+ run_test(atom_to_list(Testspec), Args, Options);
+%% Run one or more groups in a module with Options.
+run(Testspec, Mod, Grs={group,_Groups}, Config) when is_atom(Testspec),
+ is_atom(Mod) ->
+ Options=check_test_get_opts(Testspec, Config),
+ Args = [{suite,Mod},Grs],
run_test(atom_to_list(Testspec), Args, Options).
+%% run/5
+%% Run one or more test cases in a group with Options.
+run(Testspec, Mod, Group, Cases, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_list(Config) ->
+ Group1 = if is_tuple(Group) -> Group; true -> {group,Group} end,
+ Cases1 = if is_tuple(Cases) -> Cases; true -> {testcase,Cases} end,
+ Options=check_test_get_opts(Testspec, Config),
+ Args = [{suite,Mod},Group1,Cases1],
+ run_test(atom_to_list(Testspec), Args, Options).
+
+is_list_of_suites(List) ->
+ lists:all(fun(Suite) ->
+ S = if is_atom(Suite) -> atom_to_list(Suite);
+ true -> Suite
+ end,
+ try lists:last(string:tokens(S,"_")) of
+ "SUITE" -> true;
+ "suite" -> true;
+ _ -> false
+ catch
+ _:_ -> false
+ end
+ end, List).
+
%% Create a spec to skip all SUITES, this is used when the application
%% to be tested is not part of the OTP release to be tested.
create_skip_spec(Testspec, SuitesToSkip) ->
diff --git a/lib/test_server/src/ts.unix.config b/lib/test_server/src/ts.unix.config
index a34857b9e5..1ba5d9033e 100644
--- a/lib/test_server/src/ts.unix.config
+++ b/lib/test_server/src/ts.unix.config
@@ -3,4 +3,4 @@
%% Always run a (VNC) X server on host
%% {xserver, "xserver.example.com:66"}.
-{unix,[{telnet,"belegost"},{username,"bofh"},{password,"root"},{keep_alive,true}]}.
+{unix,[{telnet,"belegost"},{username,"telnet-test"},{password,"tset-tenlet"},{keep_alive,true}]}.
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index d96abfc55a..18d021f780 100644
--- a/lib/test_server/src/ts_run.erl
+++ b/lib/test_server/src/ts_run.erl
@@ -398,8 +398,9 @@ make_common_test_args(Args0, Options0, _Vars) ->
end,
ConfigFiles = [{config,[filename:join(ConfigPath,File)
|| File <- get_config_files()]}],
- io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++
- ConfigFiles++Options++TimeTrap]).
+ io_lib:format("~100000p",[[{abort_if_missing_suites,true} |
+ Args0++Trace++Cover++Logdir++
+ ConfigFiles++Options++TimeTrap]]).
to_list(X) when is_atom(X) ->
atom_to_list(X);
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
index f38f768f3b..9658191289 100644
--- a/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
@@ -191,7 +191,7 @@ conf1_end(Config) ->
%% check 2s & 3s < 4s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4000000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -204,7 +204,7 @@ conf2_end(Config) ->
%% check 3s & 2s < 4s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4000000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -217,7 +217,7 @@ conf3_end(Config) ->
%% check 6s & 6s & (2s & 3s) & 1s = ~6s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 6500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 7000000 -> exit({bad_parallel_exec,Ms});
Ms < 6000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -230,7 +230,7 @@ conf4_end(Config) ->
%% check 2s & 3s >= 5s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 5500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 6000000 -> exit({bad_parallel_exec,Ms});
Ms < 5000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -243,7 +243,7 @@ conf5_end(Config) ->
%% check 1s & 1s & (3s & 2s) & 1s = ~6s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 7000000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 7500000 -> exit({bad_parallel_exec,Ms});
Ms < 6000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -257,7 +257,7 @@ conf6_end(Config) ->
%% check 3s & 2s < 5s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4500000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -277,7 +277,7 @@ conf7_end(Config) ->
%% check 1s & 1s & (2s & 2s) & 1s = ~3s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4000000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -291,7 +291,7 @@ conf8_end(Config) ->
%% check 2s & 2s < 4s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 2500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 3000000 -> exit({bad_parallel_exec,Ms});
Ms < 2000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk
index 6871b5bd14..4eb70aa2cd 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1 +1 @@
-TEST_SERVER_VSN = 3.6.4
+TEST_SERVER_VSN = 3.7
diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in
index aea5686ae9..b1eb69f9dc 100644
--- a/lib/tools/c_src/Makefile.in
+++ b/lib/tools/c_src/Makefile.in
@@ -96,8 +96,11 @@ DRIVERS=
ifneq ($(strip $(ETHR_LIB_NAME)),)
# Need ethread package for emem
+ifneq ($(findstring ose,$(TARGET)),ose)
+# Do not build on OSE
PROGS += $(BIN_DIR)/emem$(TYPEMARKER)@EXEEXT@
endif
+endif
EMEM_OBJ_DIR=$(OBJ_DIR)/emem
CREATE_DIRS += $(EMEM_OBJ_DIR)
@@ -148,7 +151,12 @@ ERTS_LIB = $(ERL_TOP/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
_create_dirs := $(shell mkdir -p $(CREATE_DIRS))
+ifneq ($(findstring ose,$(TARGET)),ose)
all: $(PROGS) $(DRIVERS)
+else
+# Do not build dynamic files on OSE
+all:
+endif
$(ERTS_LIB):
$(make_verbose)cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 3a868f1300..ec5a1f4bc5 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -664,6 +664,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"is_function"
"is_integer"
"is_list"
+ "is_map"
"is_number"
"is_pid"
"is_port"
@@ -715,7 +716,8 @@ resulting regexp is surrounded by \\_< and \\_>."
"pos_integer"
"string"
"term"
- "timeout")
+ "timeout"
+ "map")
"Erlang type specs types"))
(eval-and-compile
@@ -772,6 +774,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"is_function"
"is_integer"
"is_list"
+ "is_map"
"is_number"
"is_pid"
"is_port"
@@ -791,6 +794,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"list_to_tuple"
"load_module"
"make_ref"
+ "map_size"
"max"
"min"
"module_loaded"
@@ -1416,6 +1420,10 @@ Other commands:
(if (boundp 'after-change-major-mode-hook)
(run-hooks 'after-change-major-mode-hook)))
+;;;###autoload
+(dolist (r '("\\.erl$" "\\.app\\.src$" "\\.escript"
+ "\\.hrl$" "\\.xrl$" "\\.yrl" "/ebin/.+\\.app"))
+ (add-to-list 'auto-mode-alist (cons r 'erlang-mode)))
(defun erlang-syntax-table-init ()
(if (null erlang-mode-syntax-table)
@@ -2566,9 +2574,9 @@ Value is list (stack token-start token-type in-what)."
(erlang-pop stack))
(if (and stack (memq (car (car stack)) '(icr begin fun try)))
(erlang-pop stack))))
- ((looking-at "catch.*of")
+ ((looking-at "catch\\b.*of")
t)
- ((looking-at "catch\\s *\\($\\|%\\|.*->\\)")
+ ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
;; Must pop top icr layer, `catch' in try/catch
;;will push a new layer next.
(progn
@@ -2616,9 +2624,9 @@ Value is list (stack token-start token-type in-what)."
;;((looking-at "when\\s *\\($\\|%\\)")
((looking-at "when[^_a-zA-Z0-9]")
(erlang-push (list 'when token (current-column)) stack))
- ((looking-at "catch.*of")
+ ((looking-at "catch\\b.*of")
t)
- ((looking-at "catch\\s *\\($\\|%\\|.*->\\)")
+ ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
(erlang-push (list 'icr token (current-column)) stack))
;;(erlang-push (list '-> token (current-column)) stack))
;;((looking-at "^of$")
@@ -2909,7 +2917,7 @@ Return nil if inside string, t if in a comment."
(if stack
(erlang-caddr (car stack))
0))
- ((looking-at "catch\\($\\|[^_a-zA-Z0-9]\\)")
+ ((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
;; Are we in a try
(let ((start (if (eq (car stack-top) '->)
(car (cdr stack))
@@ -3120,12 +3128,12 @@ This assumes that the preceding expression is either simple
(defun erlang-at-keyword ()
"Are we looking at an Erlang keyword which will increase indentation?"
(looking-at (concat "\\(when\\|if\\|fun\\|case\\|begin\\|"
- "of\\|receive\\|after\\|catch\\|try\\)[^_a-zA-Z0-9]")))
+ "of\\|receive\\|after\\|catch\\|try\\)\\b")))
(defun erlang-at-operator ()
"Are we looking at an Erlang operator?"
(looking-at
- "\\(bnot\\|div\\|mod\\|band\\|bor\\|bxor\\|bsl\\|bsr\\)[^_a-zA-Z0-9]"))
+ "\\(bnot\\|div\\|mod\\|band\\|bor\\|bxor\\|bsl\\|bsr\\)\\b"))
(defun erlang-comment-indent ()
"Compute Erlang comment indentation.
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index 0dc1b47f0d..1c1086ca58 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -749,3 +749,14 @@ commas_first() ->
%% this used to result in a scan-sexp error
[{
}].
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+ try generate_exception(N) of
+ Val -> {N, normal, Val}
+ catch
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
+ end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index c7d2dc4ce5..a9d09000d2 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -749,3 +749,14 @@ commas_first() ->
%% this used to result in a scan-sexp error
[{
}].
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+try generate_exception(N) of
+Val -> {N, normal, Val}
+catch
+throw:X -> {N, caught, thrown, X};
+exit:X -> {N, caught, exited, X};
+error:X -> {N, caught, error, X}
+end.
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 6aeb251ac2..113fa24bd5 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -89,8 +89,9 @@
flush/1,
stop/0, stop/1]).
-export([remote_start/1,get_main_node/0]).
-%-export([bump/5]).
--export([transform/4]). % for test purposes
+
+%% Used internally to ensure we upgrade the code to the latest version.
+-export([main_process_loop/1,remote_process_loop/1]).
-record(main_state, {compiled=[], % [{Module,File}]
imported=[], % [{Module,File,ImportFile}]
@@ -110,7 +111,6 @@
-define(BUMP_REC_NAME,bump).
-record(vars, {module, % atom() Module name
- vsn, % atom()
init_info=[], % [{M,F,A,C,L}]
@@ -230,25 +230,9 @@ compile_directory(Dir) when is_list(Dir) ->
compile_directory(Dir, Options) when is_list(Dir), is_list(Options) ->
case file:list_dir(Dir) of
{ok, Files} ->
-
- %% Filter out all erl files (except cover.erl)
- ErlFileNames =
- lists:filter(fun("cover.erl") ->
- false;
- (File) ->
- case filename:extension(File) of
- ".erl" -> true;
- _ -> false
- end
- end,
- Files),
-
- %% Create a list of .erl file names (incl path) and call
- %% compile_modules/2 with the list of file names.
- ErlFiles = lists:map(fun(ErlFileName) ->
- filename:join(Dir, ErlFileName)
- end,
- ErlFileNames),
+ ErlFiles = [filename:join(Dir, File) ||
+ File <- Files,
+ filename:extension(File) =:= ".erl"],
compile_modules(ErlFiles, Options);
Error ->
Error
@@ -262,7 +246,7 @@ compile_modules([File|Files], Options, Result) ->
R = call({compile, File, Options}),
compile_modules(Files,Options,[R|Result]);
compile_modules([],_Opts,Result) ->
- reverse(Result).
+ lists:reverse(Result).
filter_options(Options) ->
lists:filter(fun(Option) ->
@@ -320,25 +304,9 @@ compile_beam_directory() ->
compile_beam_directory(Dir) when is_list(Dir) ->
case file:list_dir(Dir) of
{ok, Files} ->
-
- %% Filter out all beam files (except cover.beam)
- BeamFileNames =
- lists:filter(fun("cover.beam") ->
- false;
- (File) ->
- case filename:extension(File) of
- ".beam" -> true;
- _ -> false
- end
- end,
- Files),
-
- %% Create a list of .beam file names (incl path) and call
- %% compile_beam/1 for each such file name
- BeamFiles = lists:map(fun(BeamFileName) ->
- filename:join(Dir, BeamFileName)
- end,
- BeamFileNames),
+ BeamFiles = [filename:join(Dir, File) ||
+ File <- Files,
+ filename:extension(File) =:= ".beam"],
compile_beams(BeamFiles);
Error ->
Error
@@ -350,7 +318,7 @@ compile_beams([File|Files],Result) ->
R = compile_beam(File),
compile_beams(Files,[R|Result]);
compile_beams([],Result) ->
- reverse(Result).
+ lists:reverse(Result).
%% analyse(Module) ->
@@ -613,8 +581,11 @@ main_process_loop(State) ->
Compiled = add_compiled(Module, File,
State#main_state.compiled),
Imported = remove_imported(Module,State#main_state.imported),
- main_process_loop(State#main_state{compiled = Compiled,
- imported = Imported});
+ NewState = State#main_state{compiled = Compiled,
+ imported = Imported},
+ %% This module (cover) could have been reloaded. Make
+ %% sure we run the new code.
+ ?MODULE:main_process_loop(NewState);
error ->
reply(From, {error, File}),
main_process_loop(State)
@@ -639,8 +610,11 @@ main_process_loop(State) ->
end,
reply(From,Reply),
Imported = remove_imported(Module,State#main_state.imported),
- main_process_loop(State#main_state{compiled = Compiled,
- imported = Imported});
+ NewState = State#main_state{compiled = Compiled,
+ imported = Imported},
+ %% This module (cover) could have been reloaded. Make
+ %% sure we run the new code.
+ ?MODULE:main_process_loop(NewState);
{error,no_beam} ->
%% The module has first been compiled from .erl, and now
%% someone tries to compile it from .beam
@@ -857,7 +831,7 @@ remote_process_loop(State) ->
{remote,load_compiled,Compiled} ->
Compiled1 = load_compiled(Compiled,State#remote_state.compiled),
remote_reply(State#remote_state.main_node, ok),
- remote_process_loop(State#remote_state{compiled=Compiled1});
+ ?MODULE:remote_process_loop(State#remote_state{compiled=Compiled1});
{remote,unload,UnloadedModules} ->
unload(UnloadedModules),
@@ -1257,12 +1231,12 @@ add_imported(M, F1, ImportFile, [{M,_F2,ImportFiles}|Imported], Acc) ->
dont_import;
false ->
NewEntry = {M, F1, [ImportFile | ImportFiles]},
- {ok, reverse([NewEntry | Acc]) ++ Imported}
+ {ok, lists:reverse([NewEntry | Acc]) ++ Imported}
end;
add_imported(M, F, ImportFile, [H|Imported], Acc) ->
add_imported(M, F, ImportFile, Imported, [H|Acc]);
add_imported(M, F, ImportFile, [], Acc) ->
- {ok, reverse([{M, F, [ImportFile]} | Acc])}.
+ {ok, lists:reverse([{M, F, [ImportFile]} | Acc])}.
%% Removes a module from the list of imported modules and writes a warning
%% This is done when a module is compiled.
@@ -1383,9 +1357,9 @@ do_compile_beam(Module,Beam,UserOptions) ->
{error,E};
encrypted_abstract_code=E ->
{error,E};
- {Vsn,Code} ->
+ {raw_abstract_v1,Code} ->
Forms0 = epp:interpret_file_attribute(Code),
- {Forms,Vars} = transform(Vsn, Forms0, Module, Beam),
+ {Forms,Vars} = transform(Forms0, Module),
%% We need to recover the source from the compilation
%% info otherwise the newly compiled module will have
@@ -1400,7 +1374,7 @@ do_compile_beam(Module,Beam,UserOptions) ->
{module, Module} ->
%% Store info about all function clauses in database
- InitInfo = reverse(Vars#vars.init_info),
+ InitInfo = lists:reverse(Vars#vars.init_info),
ets:insert(?COVER_CLAUSE_TABLE, {Module, InitInfo}),
%% Store binary code so it can be loaded on remote nodes
@@ -1411,7 +1385,11 @@ do_compile_beam(Module,Beam,UserOptions) ->
_Error ->
do_clear(Module),
error
- end
+ end;
+ {_VSN,_Code} ->
+ %% Wrong version of abstract code. Just report that there
+ %% is no abstract code.
+ {error,no_abstract_code}
end.
get_abstract_code(Module, Beam) ->
@@ -1445,28 +1423,9 @@ get_compile_info(Module, Beam) ->
[]
end.
-transform(Vsn, Code, Module, Beam) when Vsn=:=abstract_v1; Vsn=:=abstract_v2 ->
- Vars0 = #vars{module=Module, vsn=Vsn},
- MainFile=find_main_filename(Code),
- {ok, MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),
-
- %% Add module and export information to the munged forms
- %% Information about module_info must be removed as this function
- %% is added at compilation
- {ok, {Module, [{exports,Exports1}]}} = beam_lib:chunks(Beam, [exports]),
- Exports2 = lists:filter(fun(Export) ->
- case Export of
- {module_info,_} -> false;
- _ -> true
- end
- end,
- Exports1),
- Forms = [{attribute,1,module,Module},
- {attribute,2,export,Exports2}]++ MungedForms,
- {Forms,Vars};
-transform(Vsn=raw_abstract_v1, Code, Module, _Beam) ->
+transform(Code, Module) ->
MainFile=find_main_filename(Code),
- Vars0 = #vars{module=Module, vsn=Vsn},
+ Vars0 = #vars{module=Module},
{ok,MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),
{MungedForms,Vars}.
@@ -1486,7 +1445,7 @@ transform_2([Form0|Forms],MungedForms,Vars,MainFile,Switch) ->
transform_2(Forms,[MungedForm|MungedForms],Vars2,MainFile,NewSwitch)
end;
transform_2([],MungedForms,Vars,_,_) ->
- {ok, reverse(MungedForms), Vars}.
+ {ok, lists:reverse(MungedForms), Vars}.
%% Expand short-circuit Boolean expressions.
expand(Expr) ->
@@ -1553,14 +1512,9 @@ aux_var(Vars, N) ->
end.
%% This code traverses the abstract code, stored as the abstract_code
-%% chunk in the BEAM file, as described in absform(3) for Erlang/OTP R8B
-%% (Vsn=abstract_v2).
-%% The abstract format after preprocessing differs slightly from the abstract
-%% format given eg using epp:parse_form, this has been noted in comments.
-%% The switch is turned off when we encounter other files then the main file.
+%% chunk in the BEAM file, as described in absform(3).
+%% The switch is turned off when we encounter other files than the main file.
%% This way we will be able to exclude functions defined in include files.
-munge({function,0,module_info,_Arity,_Clauses},_Vars,_MainFile,_Switch) ->
- ignore; % module_info will be added again when the forms are recompiled
munge({function,Line,Function,Arity,Clauses},Vars,_MainFile,on) ->
Vars2 = Vars#vars{function=Function,
arity=Arity,
@@ -1618,7 +1572,7 @@ munge_clauses([Clause|Clauses], Vars, Lines, MClauses) ->
MClauses])
end;
munge_clauses([], Vars, Lines, MungedClauses) ->
- {reverse(MungedClauses), Vars#vars{lines = Lines}}.
+ {lists:reverse(MungedClauses), Vars#vars{lines = Lines}}.
munge_body(Expr, Vars) ->
munge_body(Expr, Vars, [], []).
@@ -1662,7 +1616,7 @@ munge_body([Expr|Body], Vars, MungedBody, LastExprBumpLines) ->
munge_body(Body, Vars3, MungedExprs1, NewBumps)
end;
munge_body([], Vars, MungedBody, _LastExprBumpLines) ->
- {reverse(MungedBody), Vars}.
+ {lists:reverse(MungedBody), Vars}.
%%% Fix last expression (OTP-8188). A typical example:
%%%
@@ -1800,16 +1754,35 @@ munge_expr({match,Line,ExprL,ExprR}, Vars) ->
munge_expr({tuple,Line,Exprs}, Vars) ->
{MungedExprs, Vars2} = munge_exprs(Exprs, Vars, []),
{{tuple,Line,MungedExprs}, Vars2};
-munge_expr({record,Line,Expr,Exprs}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
- {MungedExprName, Vars2} = munge_expr(Expr, Vars),
+munge_expr({record,Line,Name,Exprs}, Vars) ->
+ {MungedExprFields, Vars2} = munge_exprs(Exprs, Vars, []),
+ {{record,Line,Name,MungedExprFields}, Vars2};
+munge_expr({record,Line,Arg,Name,Exprs}, Vars) ->
+ {MungedArg, Vars2} = munge_expr(Arg, Vars),
{MungedExprFields, Vars3} = munge_exprs(Exprs, Vars2, []),
- {{record,Line,MungedExprName,MungedExprFields}, Vars3};
+ {{record,Line,MungedArg,Name,MungedExprFields}, Vars3};
munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
- {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
- {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
- {{record_field,Line,MungedExprL,MungedExprR}, Vars3};
+ {MungedExprR, Vars2} = munge_expr(ExprR, Vars),
+ {{record_field,Line,ExprL,MungedExprR}, Vars2};
+munge_expr({map,Line,Fields}, Vars) ->
+ %% EEP 43
+ {MungedFields, Vars2} = munge_exprs(Fields, Vars, []),
+ {{map,Line,MungedFields}, Vars2};
+munge_expr({map,Line,Arg,Fields}, Vars) ->
+ %% EEP 43
+ {MungedArg, Vars2} = munge_expr(Arg, Vars),
+ {MungedFields, Vars3} = munge_exprs(Fields, Vars2, []),
+ {{map,Line,MungedArg,MungedFields}, Vars3};
+munge_expr({map_field_assoc,Line,Name,Value}, Vars) ->
+ %% EEP 43
+ {MungedName, Vars2} = munge_expr(Name, Vars),
+ {MungedValue, Vars3} = munge_expr(Value, Vars2),
+ {{map_field_assoc,Line,MungedName,MungedValue}, Vars3};
+munge_expr({map_field_exact,Line,Name,Value}, Vars) ->
+ %% EEP 43
+ {MungedName, Vars2} = munge_expr(Name, Vars),
+ {MungedValue, Vars3} = munge_expr(Value, Vars2),
+ {{map_field_exact,Line,MungedName,MungedValue}, Vars3};
munge_expr({cons,Line,ExprH,ExprT}, Vars) ->
{MungedExprH, Vars2} = munge_expr(ExprH, Vars),
{MungedExprT, Vars3} = munge_expr(ExprT, Vars2),
@@ -1871,23 +1844,10 @@ munge_expr({'try',Line,Body,Clauses,CatchClauses,After}, Vars) ->
{MungedAfter, Vars4} = munge_body(After, Vars3),
{{'try',Line,MungedBody,MungedClauses,MungedCatchClauses,MungedAfter},
Vars4};
-%% Difference in abstract format after preprocessing: Funs get an extra
-%% element Extra.
-%% NOT NECESSARY FOR Vsn=raw_abstract_v1
-munge_expr({'fun',Line,{function,Name,Arity},_Extra}, Vars) ->
- {{'fun',Line,{function,Name,Arity}}, Vars};
-munge_expr({'fun',Line,{clauses,Clauses},_Extra}, Vars) ->
- {MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
- {{'fun',Line,{clauses,MungedClauses}}, Vars2};
munge_expr({'fun',Line,{clauses,Clauses}}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
{MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
{{'fun',Line,{clauses,MungedClauses}}, Vars2};
-munge_expr({named_fun,Line,Name,Clauses,_Extra}, Vars) ->
- {MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
- {{named_fun,Line,Name,MungedClauses}, Vars2};
munge_expr({named_fun,Line,Name,Clauses}, Vars) ->
- %% Only for Vsn=raw_abstract_v1
{MungedClauses,Vars2}=munge_clauses(Clauses, Vars),
{{named_fun,Line,Name,MungedClauses}, Vars2};
munge_expr({bin,Line,BinElements}, Vars) ->
@@ -1908,7 +1868,7 @@ munge_exprs([Expr|Exprs], Vars, MungedExprs) ->
{MungedExpr, Vars2} = munge_expr(Expr, Vars),
munge_exprs(Exprs, Vars2, [MungedExpr|MungedExprs]);
munge_exprs([], Vars, MungedExprs) ->
- {reverse(MungedExprs), Vars}.
+ {lists:reverse(MungedExprs), Vars}.
%% Every qualifier is decorated with a counter.
munge_qualifiers(Qualifiers, Vars) ->
@@ -1927,7 +1887,7 @@ munge_qs([Expr|Qs], Vars, MQs) ->
{MungedExpr, Vars2} = munge_expr(Expr, Vars),
munge_qs1(Qs, L, MungedExpr, Vars, Vars2, MQs);
munge_qs([], Vars, MQs) ->
- {reverse(MQs), Vars}.
+ {lists:reverse(MQs), Vars}.
munge_qs1(Qs, Line, NQ, Vars, Vars2, MQs) ->
case new_bumps(Vars2, Vars) of
@@ -2120,7 +2080,7 @@ merge_clauses([{{M,F,A,_C1},R1},{{M,F,A,C2},R2}|Clauses], MFun, Result) ->
merge_clauses([{{M,F,A,_C},R}|Clauses], MFun, Result) ->
merge_clauses(Clauses, MFun, [{{M,F,A},R}|Result]);
merge_clauses([], _Fun, Result) ->
- reverse(Result).
+ lists:reverse(Result).
merge_functions([{_MFA,R}|Functions], MFun) ->
merge_functions(Functions, MFun, R);
@@ -2441,14 +2401,6 @@ not_loaded(_Module,_Else, State) ->
%%%--Div-----------------------------------------------------------------
-reverse(List) ->
- reverse(List,[]).
-reverse([H|T],Acc) ->
- reverse(T,[H|Acc]);
-reverse([],Acc) ->
- Acc.
-
-
escape_lt_and_gt(Rawline,HTML) when HTML =/= true ->
Rawline;
escape_lt_and_gt(Rawline,_HTML) ->
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index 553c5eb96b..ec5b6f3a82 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -39,23 +39,9 @@
{applications, [kernel, stdlib]},
{env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]}
]
- }
+ },
+ {runtime_dependencies, ["webtool-0.8.10","stdlib-2.0","runtime_tools-1.8.14",
+ "kernel-3.0","inets-5.10","erts-6.0",
+ "compiler-5.0"]}
]
}.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/tools/src/tools.appup.src b/lib/tools/src/tools.appup.src
index 8de1ec76c9..9a27456a81 100644
--- a/lib/tools/src/tools.appup.src
+++ b/lib/tools/src/tools.appup.src
@@ -1,19 +1,21 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2001-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%
-#
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, tools}]}],
+ [{<<".*">>,[{restart_application, tools}]}]
+}.
diff --git a/lib/tools/src/xref_compiler.erl b/lib/tools/src/xref_compiler.erl
index f0fed502a5..c4b5c04c12 100644
--- a/lib/tools/src/xref_compiler.erl
+++ b/lib/tools/src/xref_compiler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. 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
@@ -885,7 +885,7 @@ evaluate([pop | P], T, [_ | S]) ->
evaluate([], T, [R]) ->
{T, R}.
-%% (PossibleGraph, 1 | -1, dict()) -> dict()
+%% (PossibleGraph, 1 | -1, dict:dict()) -> dict:dict()
%% Use the same table for everything... Here: Reference counters for digraphs.
update_graph_counter(Value, Inc, T) ->
case catch digraph:info(Value) of
diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index 7b72165e6f..49c397a140 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. 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
@@ -437,7 +437,7 @@ regexpr({ModExpr, FunExpr, ArityExpr}, Var) ->
V2
end.
-%% -> digraph()
+%% -> digraph:graph()
relation_to_graph(S) ->
G = digraph:new(),
Fun = fun({From, To}) ->
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index bd71218474..80807b1d38 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -22,7 +22,8 @@
suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([start/1, compile/1, analyse/1, misc/1, stop/1,
+-export([coverage/1, coverage_analysis/1,
+ start/1, compile/1, analyse/1, misc/1, stop/1,
distribution/1, reconnect/1, die_and_reconnect/1,
dont_reconnect_after_stop/1, stop_node_after_disconnect/1,
export_import/1,
@@ -30,6 +31,8 @@
otp_8188/1, otp_8270/1, otp_8273/1, otp_8340/1,
otp_10979_hanging_node/1, compile_beam_opts/1, eep37/1]).
+-export([do_coverage/1]).
+
-include_lib("test_server/include/test_server.hrl").
%%----------------------------------------------------------------------
@@ -46,25 +49,25 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ NoStartStop = [eif,otp_5305,otp_5418,otp_7095,otp_8273,
+ otp_8340,otp_8188,compile_beam_opts,eep37],
+ StartStop = [start, compile, analyse, misc, stop,
+ distribution, reconnect, die_and_reconnect,
+ dont_reconnect_after_stop, stop_node_after_disconnect,
+ export_import, otp_5031, otp_6115,
+ otp_8270, otp_10979_hanging_node],
case whereis(cover_server) of
undefined ->
- [start, compile, analyse, misc, stop,
- distribution, reconnect, die_and_reconnect,
- dont_reconnect_after_stop, stop_node_after_disconnect,
- export_import, otp_5031, eif, otp_5305, otp_5418,
- otp_6115, otp_7095, otp_8188, otp_8270, otp_8273,
- otp_8340, otp_10979_hanging_node, compile_beam_opts, eep37];
+ [coverage,StartStop ++ NoStartStop];
_pid ->
- {skip,
- "It looks like the test server is running "
- "cover. Can't run cover test."}
+ [coverage|NoStartStop++[coverage_analysis]]
end.
groups() ->
[].
init_per_suite(Config) ->
- Config.
+ [{ct_is_running_cover,whereis(cover_server) =/= undefined}|Config].
end_per_suite(_Config) ->
ok.
@@ -90,13 +93,64 @@ init_per_testcase(TC, Config) when TC =:= misc;
init_per_testcase(_TestCase, Config) ->
Config.
-end_per_testcase(TestCase, _Config) ->
- case lists:member(TestCase,[start,compile,analyse,misc]) of
+end_per_testcase(TestCase, Config) ->
+ NoStop = [start,compile,analyse,misc],
+ DontStop = proplists:get_bool(ct_is_running_cover, Config) orelse
+ lists:member(TestCase, NoStop),
+ case DontStop of
true -> ok;
false -> cover:stop()
end,
ok.
+coverage(Config) when is_list(Config) ->
+ {ok,?MODULE} = cover:compile_beam(?MODULE),
+ ?MODULE:do_coverage(Config).
+
+do_coverage(Config) ->
+ Outdir = ?config(priv_dir, Config),
+ ExportFile = filename:join(Outdir, "export"),
+ ok = cover:export(ExportFile, ?MODULE),
+ {error,{already_started,_}} = cover:start(),
+ {error,_} = cover:compile_beam(non_existing_module),
+ _ = cover:which_nodes(),
+ _ = cover:modules(),
+ _ = cover:imported(),
+ {error,{not_cover_compiled,lists}} = cover:analyze(lists),
+
+ %% Cover escaping of '&' in HTML files.
+
+ case proplists:get_bool(ct_is_running_cover, Config) of
+ false ->
+ %% Cover server was implicitly started when this module
+ %% was cover-compiled. We must stop the cover server, but
+ %% we must ensure that this module is not on the call
+ %% stack when it is unloaded. Therefore, the call that
+ %% follows MUST be tail-recursive.
+ cover:stop();
+ true ->
+ %% Cover server was started by common_test; don't stop it.
+ ok
+ end.
+
+%% This test case will only be run when common_test is running cover.
+coverage_analysis(Config) when is_list(Config) ->
+ {ok,Analysis1} = cover:analyze(?MODULE),
+ io:format("~p\n", [Analysis1]),
+ {ok,Analysis2} = cover:analyze(?MODULE, calls),
+ io:format("~p\n", [Analysis2]),
+ {ok,_Analysis3} = cover:analyze(?MODULE, calls, line),
+
+ Outdir = ?config(priv_dir, Config),
+ Outfile = filename:join(Outdir, ?MODULE),
+
+ {ok,Outfile} = cover:analyze_to_file(?MODULE, Outfile),
+ {ok,Contents} = file:read_file(Outfile),
+ ok = file:delete(Outfile),
+ ok = io:put_chars(Contents),
+ {ok,Outfile} = cover:analyze_to_file(?MODULE, Outfile, [html]),
+ ok.
+
start(suite) -> [];
start(Config) when is_list(Config) ->
?line ok = file:set_cwd(?config(data_dir, Config)),
@@ -462,13 +516,11 @@ reconnect(Config) ->
cover:flush(N1),
rpc:call(N1,f,f1,[]),
- %% This will cause a call to f:f2() when nodes()==[] on N1
+ %% This will cause first casue the N1 node to initiate a
+ %% disconnect and then call f:f2() when nodes() =:= [] on N1.
rpc:cast(N1,f,call_f2_when_isolated,[]),
-
- %% Disconnect and check that node is removed from main cover node
- net_kernel:disconnect(N1),
timer:sleep(500), % allow some to detect disconnect and for f:f2() call
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
%% Do some add one module (b) and remove one module (a)
code:purge(a),
@@ -476,7 +528,7 @@ reconnect(Config) ->
{ok,b} = cover:compile(b),
cover_compiled = code:which(b),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
check_f_calls(1,0), % only the first call - before the flush
%% Reconnect the node and check that b and f are cover compiled but not a
@@ -519,7 +571,7 @@ die_and_reconnect(Config) ->
%% Kill the node
rpc:call(N1,erlang,halt,[]),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
check_f_calls(1,0), % only the first call - before the flush
@@ -560,7 +612,7 @@ dont_reconnect_after_stop(Config) ->
%% Stop cover on the node, then terminate the node
cover:stop(N1),
rpc:call(N1,erlang,halt,[]),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
check_f_calls(1,0),
@@ -568,7 +620,7 @@ dont_reconnect_after_stop(Config) ->
{ok,N1} = ?t:start_node(NodeName,peer,
[{args," -pa " ++ DataDir},{start_cover,false}]),
timer:sleep(300),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
Beam = rpc:call(N1,code,which,[f]),
false = (Beam==cover_compiled),
@@ -613,7 +665,7 @@ stop_node_after_disconnect(Config) ->
{ok,N1} = ?t:start_node(NodeName,peer,
[{args," -pa " ++ DataDir},{start_cover,false}]),
timer:sleep(300),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
Beam = rpc:call(N1,code,which,[f]),
false = (Beam==cover_compiled),
@@ -759,7 +811,6 @@ eif(Config) when is_list(Config) ->
%% in cover_inc.beam - not the ones from the included file.
?line cover_inc:func(),
?line {ok, [_, _]} = cover:analyse(cover_inc, line),
- ?line cover:stop(),
ok.
otp_5305(suite) -> [];
@@ -775,7 +826,6 @@ otp_5305(Config) when is_list(Config) ->
">>,
?line ok = file:write_file(File, Test),
?line {ok, t} = cover:compile(File),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -790,7 +840,6 @@ otp_5418(Config) when is_list(Config) ->
?line ok = file:write_file(File, Test),
?line {ok, t} = cover:compile(File),
?line {ok,{t,{0,0}}} = cover:analyse(t, module),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -952,7 +1001,6 @@ otp_7095(Config) when is_list(Config) ->
{{t,67},1},{{t,69},1},{{t,71},1},{{t,74},1},
{{t,76},0},{{t,78},1},
{{t,82},2}]} = cover:analyse(t, calls, line),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -1028,7 +1076,6 @@ otp_8273(Config) when is_list(Config) ->
">>,
?line File = cc_mod(t, Test, Config),
?line ok = t:t(),
- ?line cover:stop(),
?line ok = file:delete(File),
ok.
@@ -1066,7 +1113,6 @@ otp_8188(Config) when is_list(Config) ->
?line File = cc_mod(t, Test, Config),
?line false = t:test(nok),
?line {ok,[{{t,11},1},{{t,12},1}]} = cover:analyse(t, calls, line),
- ?line cover:stop(),
?line ok = file:delete(File),
%% Bit string comprehensions are now traversed;
@@ -1433,6 +1479,8 @@ compile_beam_opts(Config) when is_list(Config) ->
export_all,
debug_info,
return_errors]),
+ code:purge(t),
+ code:delete(t),
Exports =
[{func1,0},
{macro, 0},
@@ -1443,7 +1491,6 @@ compile_beam_opts(Config) when is_list(Config) ->
Exports = t:module_info(exports),
{ok, t} = cover:compile_beam("t"),
Exports = t:module_info(exports),
- cover:stop(),
ok = file:delete("t.beam"),
ok = file:set_cwd(Cwd),
ok.
@@ -1526,3 +1573,21 @@ is_unloaded(What) ->
check_f_calls(F1,F2) ->
{ok,[{{f,f1,0},F1},{{f,f2,0},F2}|_]} = cover:analyse(f,calls,function).
+
+cover_which_nodes(Expected) ->
+ case cover:which_nodes() of
+ Expected ->
+ ok;
+ Other ->
+ {Time,ok} = timer:tc(fun Retry() ->
+ case cover:which_nodes() of
+ Expected -> ok;
+ _ ->
+ ?t:sleep(100),
+ Retry()
+ end
+ end),
+ io:format("~p ms before cover:which_nodes() returned ~p",
+ [Time,Expected]),
+ Expected = Other
+ end.
diff --git a/lib/tools/test/cover_SUITE_data/f.erl b/lib/tools/test/cover_SUITE_data/f.erl
index ce2963014a..a29a67b388 100644
--- a/lib/tools/test/cover_SUITE_data/f.erl
+++ b/lib/tools/test/cover_SUITE_data/f.erl
@@ -10,10 +10,15 @@ f2() ->
f2_line2.
call_f2_when_isolated() ->
+ [Other] = nodes(),
+ net_kernel:disconnect(Other),
+ do_call_f2_when_isolated().
+
+do_call_f2_when_isolated() ->
case nodes() of
[] ->
f2();
_ ->
timer:sleep(100),
- call_f2_when_isolated()
+ do_call_f2_when_isolated()
end.
diff --git a/lib/tools/test/eprof_SUITE.erl b/lib/tools/test/eprof_SUITE.erl
index 26685a6a84..1227d5b841 100644
--- a/lib/tools/test/eprof_SUITE.erl
+++ b/lib/tools/test/eprof_SUITE.erl
@@ -127,6 +127,14 @@ basic(Config) when is_list(Config) ->
ok.
basic_option(Config) when is_list(Config) ->
+ %% Eprof is not supported on native-compile code.
+ case lists:module_info(native_addresses) of
+ [] -> basic_option_1(Config);
+ [_|_] -> {skip,"lists is native-compiled"}
+ end.
+
+basic_option_1(Config) ->
+
%% load eprof_test and change directory
{ok, OldCurDir} = file:get_cwd(),
diff --git a/lib/tools/test/tools_SUITE.erl b/lib/tools/test/tools_SUITE.erl
index ea3f59dbe1..e3582b995b 100644
--- a/lib/tools/test/tools_SUITE.erl
+++ b/lib/tools/test/tools_SUITE.erl
@@ -30,12 +30,12 @@
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
--export([app_test/1]).
+-export([app_test/1, appup_test/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test].
+ [app_test, appup_test].
groups() ->
[].
@@ -71,3 +71,7 @@ app_test(suite) ->
[];
app_test(Config) when is_list(Config) ->
?line ?t:app_test(tools, tolerant).
+
+%% Test that the .appup file does not contain any `basic' errors
+appup_test(Config) when is_list(Config) ->
+ ok = ?t:appup_test(tools).
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 0cead00554..2d2970de3a 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.6.13
+TOOLS_VSN = 2.6.14
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
index 850829e1dc..974091b44c 100644
--- a/lib/typer/src/typer.app.src
+++ b/lib/typer/src/typer.app.src
@@ -6,4 +6,6 @@
{modules, [typer]},
{registered, []},
{applications, [compiler, dialyzer, hipe, kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0",
+ "dialyzer-2.7","compiler-5.0"]}]}.
diff --git a/lib/typer/src/typer.appup.src b/lib/typer/src/typer.appup.src
index 54a63833e6..bebe7a159c 100644
--- a/lib/typer/src/typer.appup.src
+++ b/lib/typer/src/typer.appup.src
@@ -1 +1,21 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
+{"%VSN%",
+ [{<<".*">>,[{restart_application, typer}]}],
+ [{<<".*">>,[{restart_application, typer}]}]
+}.
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index 20ae4d6066..572bf24ca4 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -1094,7 +1094,7 @@ rcv_ext_types(Self, ExtTypes) ->
%% specialized for the uses in this module
%%--------------------------------------------------------------------
--type map_dict() :: dict().
+-type map_dict() :: dict:dict().
-spec map__new() -> map_dict().
map__new() ->
diff --git a/lib/typer/test/Makefile b/lib/typer/test/Makefile
new file mode 100644
index 0000000000..d6dd22b6cf
--- /dev/null
+++ b/lib/typer/test/Makefile
@@ -0,0 +1,65 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ typer_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/typer_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) typer.spec "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+
+release_docs_spec:
diff --git a/lib/typer/test/typer.spec b/lib/typer/test/typer.spec
new file mode 100644
index 0000000000..79f51b6781
--- /dev/null
+++ b/lib/typer/test/typer.spec
@@ -0,0 +1 @@
+{suites,"../typer_test",all}.
diff --git a/lib/typer/test/typer_SUITE.erl b/lib/typer/test/typer_SUITE.erl
new file mode 100644
index 0000000000..99c4facbad
--- /dev/null
+++ b/lib/typer/test/typer_SUITE.erl
@@ -0,0 +1,56 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(typer_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ case application:ensure_all_started(typer) of
+ {ok, Apps} ->
+ [application:stop(App) || App <- lists:reverse(Apps)],
+ [app, appup];
+ _ ->
+ [appup]
+ end.
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the typer app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(typer).
+
+appup() ->
+ [{doc, "Test that the typer appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(typer).
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index 5ac145d9ff..49fdda756e 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.5
+TYPER_VSN = 0.9.6
diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile
index f28c777240..af565c8895 100644
--- a/lib/webtool/src/Makefile
+++ b/lib/webtool/src/Makefile
@@ -66,7 +66,7 @@ ERL_COMPILE_FLAGS += +warn_obsolete_guard
debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
rm -f core
docs:
diff --git a/lib/webtool/src/webtool.app.src b/lib/webtool/src/webtool.app.src
index 8c6774c533..3d8d11ea60 100644
--- a/lib/webtool/src/webtool.app.src
+++ b/lib/webtool/src/webtool.app.src
@@ -21,5 +21,7 @@
{vsn,"%VSN%"},
{modules,[webtool,webtool_sup]},
{registered,[web_tool,websup]},
- {applications,[kernel,stdlib]}]}.
+ {applications,[kernel,stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","observer-2.0","kernel-3.0",
+ "inets-5.10","erts-6.0"]}]}.
diff --git a/lib/webtool/src/webtool.appup.src b/lib/webtool/src/webtool.appup.src
index 7a435e9b22..9e6f4b9b5b 100644
--- a/lib/webtool/src/webtool.appup.src
+++ b/lib/webtool/src/webtool.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2014. 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
@@ -15,5 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-{"%VSN%",[],[]}.
+{"%VSN%",
+ [{<<".*">>,[{restart_application, webtool}]}],
+ [{<<".*">>,[{restart_application, webtool}]}]
+}.
diff --git a/lib/webtool/test/Makefile b/lib/webtool/test/Makefile
new file mode 100644
index 0000000000..93aa1c09eb
--- /dev/null
+++ b/lib/webtool/test/Makefile
@@ -0,0 +1,65 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ webtool_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/webtool_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) webtool.spec "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+
+release_docs_spec:
diff --git a/lib/webtool/test/webtool.spec b/lib/webtool/test/webtool.spec
new file mode 100644
index 0000000000..134e6ed40c
--- /dev/null
+++ b/lib/webtool/test/webtool.spec
@@ -0,0 +1 @@
+{suites,"../webtool_test",all}.
diff --git a/lib/webtool/test/webtool_SUITE.erl b/lib/webtool/test/webtool_SUITE.erl
new file mode 100644
index 0000000000..64ff221a1b
--- /dev/null
+++ b/lib/webtool/test/webtool_SUITE.erl
@@ -0,0 +1,50 @@
+%% ``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 via the world wide web 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 Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+-module(webtool_SUITE).
+
+-compile([export_all]).
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [app, appup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+app() ->
+ [{doc, "Test that the webtool app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(webtool).
+
+appup() ->
+ [{doc, "Test that the webtool appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(webtool).
diff --git a/lib/webtool/vsn.mk b/lib/webtool/vsn.mk
index d356a8954d..a79c273d9f 100644
--- a/lib/webtool/vsn.mk
+++ b/lib/webtool/vsn.mk
@@ -1 +1 @@
-WEBTOOL_VSN=0.8.9.2
+WEBTOOL_VSN=0.8.10
diff --git a/lib/wx/aclocal.m4 b/lib/wx/aclocal.m4
index 46b30a16b3..2b47f7c4bc 100644
--- a/lib/wx/aclocal.m4
+++ b/lib/wx/aclocal.m4
@@ -74,6 +74,21 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re
AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)])
+dnl Cross compilation variables for OSE
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
+
])
AC_DEFUN(ERL_XCOMP_SYSROOT_INIT,
@@ -488,6 +503,8 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support,
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -500,6 +517,8 @@ else
#ifdef __WIN32__
#include <winsock2.h>
#include <ws2tcpip.h>
+#elif __OSE__
+#error "no ipv6"
#else
#include <netinet/in.h>
#endif],
@@ -728,6 +747,12 @@ if test "X$host_os" = "Xwin32"; then
THR_LIBS=
THR_LIB_NAME=win32_threads
THR_LIB_TYPE=win32_threads
+elif test "X$host_os" = "Xose"; then
+ AC_MSG_RESULT(yes)
+ THR_DEFS="-DOSE_THREADS"
+ THR_LIBS=
+ THR_LIB_NAME=ose_threads
+ THR_LIB_TYPE=ose_threads
else
AC_MSG_RESULT(no)
THR_DEFS=
@@ -1078,9 +1103,22 @@ case "$THR_LIB_NAME" in
test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes
;;
- pthread)
- ETHR_THR_LIB_BASE_DIR=pthread
- AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ pthread|ose_threads)
+ case "$THR_LIB_NAME" in
+ pthread)
+ ETHR_THR_LIB_BASE_DIR=pthread
+ AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ ;;
+ ose_threads)
+ AC_DEFINE(ETHR_OSE_THREADS, 1,
+ [Define if you have OSE style threads])
+ ETHR_THR_LIB_BASE_DIR=ose
+ AC_CHECK_HEADER(ose_spi/ose_spi.h,
+ AC_DEFINE(HAVE_OSE_SPI_H, 1,
+ [Define if you have the "ose_spi/ose_spi.h" header file.]))
+ ;;
+ esac
+ if test "x$THR_LIB_NAME" == "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1139,6 +1177,7 @@ case "$THR_LIB_NAME" in
*) ;;
esac
+ fi
dnl We sometimes need ETHR_DEFS in order to find certain headers
dnl (at least for pthread.h on osf1).
saved_cppflags="$CPPFLAGS"
@@ -1151,7 +1190,6 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for headers
dnl
-
AC_CHECK_HEADER(pthread.h, \
AC_DEFINE(ETHR_HAVE_PTHREAD_H, 1, \
[Define if you have the <pthread.h> header file.]))
@@ -1184,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
-
+ if test "x$THR_LIB_NAME" == "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
@@ -1311,6 +1349,8 @@ case "$THR_LIB_NAME" in
AC_MSG_RESULT([$linux_futex])
test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.])
+ fi
+
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(long long)
diff --git a/lib/wx/api_gen/Makefile b/lib/wx/api_gen/Makefile
index 8adb485ba9..3e41ac7bc5 100644
--- a/lib/wx/api_gen/Makefile
+++ b/lib/wx/api_gen/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2012. All Rights Reserved.
+# Copyright Ericsson AB 2008-2014. 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
@@ -60,7 +60,7 @@ $(GL): glxml_generated $(GL_COMP_T) glapi.conf
erl -noshell -run gl_gen code && touch gl_code_generated
%.beam: %.erl wx_gen.hrl gl_gen.hrl
- $(ERLC) -W $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) $< -o$(EBIN)
+ $(ERLC) -W $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
# TODO split cleans into separate targets?
complete_clean:
diff --git a/lib/wx/api_gen/wx_doxygen.conf b/lib/wx/api_gen/wx_doxygen.conf
index 829702cbbf..a8516aa08e 100644
--- a/lib/wx/api_gen/wx_doxygen.conf
+++ b/lib/wx/api_gen/wx_doxygen.conf
@@ -57,7 +57,6 @@ GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
-SHOW_DIRECTORIES = YES
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
@@ -110,7 +109,6 @@ HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
-HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
@@ -252,6 +250,7 @@ PREDEFINED = \
wxUSE_SLIDER=1 \
wxUSE_CLIPBOARD=1 \
wxUSE_SYSTEM_OPTIONS=1 \
+ wxUSE_INTL=1 \
wxABI_VERSION=20809 \
__WXGTK24__=1 \
__WXGTK20__=1 \
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
index 9c5f46b253..5d20019d8f 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src
@@ -1,17 +1,5 @@
-case 98: { // wxeEvtListener::wxeEvtListener
- wxeEvtListener *Result = new wxeEvtListener(Ecmd.port);
- rt.addRef(getRef((void *)Result,memenv), "wxeEvtListener");
- break;
-}
-case 99: { // wxeEvtListener::destroy
- wxObject *This = (wxObject *) getPtr(bp,memenv);
- rt.addAtom("ok");
- delete This;
- break;
-}
-case 100: { // wxEvtHandler::Connect
- wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
+case 100: { // wxEvtHandler::Connect
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
int * lastId = (int *) bp; bp += 4;
@@ -22,20 +10,22 @@ case 100: { // wxEvtHandler::Connect
int * eventTypeLen = (int *) bp; bp += 4;
int * class_nameLen = (int *) bp; bp += 4;
- if(*haveUserData) {
+ if(*haveUserData) {
userData = new wxeErlTerm(Ecmd.bin[0]);
}
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
char *class_name = bp; bp+= *class_nameLen;
if(eventType > 0 ) {
- wxeCallbackData * Evt_cb = new wxeCallbackData(Ecmd.caller,getRef(This, memenv),
- class_name,*fun_cb,
- *skip, userData, Listener);
+ wxeEvtListener * Evt_cb = new wxeEvtListener(Ecmd.caller,getRef(This, memenv),
+ class_name,*fun_cb,
+ *skip, userData, Ecmd.port);
This->Connect((int) *winid,(int) *lastId,eventType,
(wxObjectEventFunction)(wxEventFunction) &wxeEvtListener::forward,
- Evt_cb, Listener);
+ Evt_cb, Evt_cb);
rt.addAtom("ok");
+ rt.addRef(getRef((void *)Evt_cb,memenv), "wxeEvtListener");
+ rt.addTupleCount(2);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
@@ -43,7 +33,7 @@ case 100: { // wxEvtHandler::Connect
}
break;
}
-case 101: { // wxEvtHandler::Disconnect
+case 101: { // wxEvtHandler::Disconnect
wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
@@ -53,14 +43,14 @@ case 101: { // wxEvtHandler::Disconnect
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
if(eventType > 0) {
bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
- (wxObjectEventFunction)(wxEventFunction)
- &wxeEvtListener::forward,
- NULL, Listener);
+ (wxObjectEventFunction)(wxEventFunction)
+ &wxeEvtListener::forward,
+ NULL, Listener);
rt.addBool(Result);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
- rt.addTupleCount(2);
+ rt.addTupleCount(2);
}
break;
}
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
index c5802af679..c9726fd475 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
@@ -27,15 +27,11 @@
-export([connect/2, connect/3, disconnect/1, disconnect/2, disconnect/3]).
%% internal exports
--export([connect_impl/3, disconnect_impl/2, disconnect_impl/3,
- new_evt_listener/0, destroy_evt_listener/1,
- get_callback/1, replace_fun_with_id/2]).
+-export([connect_impl/2, disconnect_impl/2]).
-export_type([wxEvtHandler/0, wx/0, event/0]).
-type wxEvtHandler() :: wx:wx_object().
--record(evh, {et=null,id=?wxID_ANY,lastId=?wxID_ANY,skip=undefined,userdata=[],cb=0}).
-
%% @doc Equivalent to {@link connect/3. connect(This, EventType, [])}
-spec connect(This::wxEvtHandler(), EventType::wxEventType()) -> ok.
connect(This, EventType) ->
@@ -130,54 +126,34 @@ disconnect(This=#wx_ref{type=ThisT,ref=_ThisRef}, EventType, Opts) ->
%% @hidden
-connect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType,
- skip=Skip, userdata=Userdata, cb=FunID})
+connect_impl(#wx_ref{type=ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ skip=Skip, userdata=Userdata, cb=FunID})
when is_integer(FunID)->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
ThisTypeBin = list_to_binary([atom_to_list(ThisT)|[0]]),
UD = if Userdata =:= [] -> 0;
- true ->
+ true ->
wxe_util:send_bin(term_to_binary(Userdata)),
1
end,
- wxe_util:call(100, <<EvtList:32/?UI,ThisRef:32/?UI,
+ wxe_util:call(100, <<ThisRef:32/?UI,
Winid:32/?UI,LastId:32/?UI,
(wxe_util:from_bool(Skip)):32/?UI,
UD:32/?UI,
FunID:32/?UI,
(size(EventTypeBin)):32/?UI,
- (size(ThisTypeBin)):32/?UI,
+ (size(ThisTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary,ThisTypeBin/binary>>).
%% @hidden
-disconnect_impl(Listener, Object) ->
- disconnect_impl(Listener, Object, #evh{}).
-%% @hidden
-disconnect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=_ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType}) ->
+disconnect_impl(#wx_ref{type=_ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ handler=#wx_ref{type=wxeEvtListener,ref=EvtList}}) ->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
- wxe_util:call(101, <<EvtList:32/?UI,
+ wxe_util:call(101, <<EvtList:32/?UI,
ThisRef:32/?UI,Winid:32/?UI,LastId:32/?UI,
(size(EventTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary>>).
-
-%% @hidden
-new_evt_listener() ->
- wxe_util:call(98, <<>>).
-
-%% @hidden
-destroy_evt_listener(#wx_ref{type=wxeEvtListener,ref=EvtList}) ->
- wxe_util:call(99, <<EvtList:32/?UI>>).
-
-%% @hidden
-get_callback(#evh{cb=Callback}) ->
- Callback.
-
-%% @hidden
-replace_fun_with_id(Evh, Id) ->
- Evh#evh{cb=Id}.
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.c_src b/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
index 8fa31e512e..d6196d11a2 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -165,7 +165,7 @@ case ~s: { // wxListCtrl::SortItems taylormade
callbackInfo* cb = new callbackInfo();
cb->port = Ecmd.port;
cb->callbackID = sortCallback;
- bool Result = This->SortItems(wxEListCtrlCompare, (long)cb);
+ bool Result = This->SortItems(wxEListCtrlCompare, (wxeIntPtr)cb);
delete cb;
/* Destroy the callback, see wxEPrintout::clear_cb */
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index 3ca8cd7d14..a60a9a93d5 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -25,7 +25,7 @@
-include_lib("xmerl/include/xmerl.hrl").
--import(lists, [foldl/3,foldr/3,reverse/1, keysearch/3, map/2, filter/2]).
+-import(lists, [foldl/3,foldr/3,reverse/1,keysearch/3,map/2,filter/2,droplast/1]).
-import(proplists, [get_value/2,get_value/3]).
-compile(export_all).
@@ -69,7 +69,7 @@ gen_code() ->
gen_xml() ->
%% {ok, Defs} = file:consult("wxapi.conf"),
-%% Rel = reverse(tl(reverse(os:cmd("wx-config --release")))),
+%% Rel = droplast(os:cmd("wx-config --release")),
%% Dir = " /usr/include/wx-" ++ Rel ++ "/wx/",
%% Files0 = [Dir ++ File || {class, File, _, _, _} <- Defs],
%% Files1 = [Dir ++ File || {doxygen, File} <- Defs],
@@ -172,7 +172,7 @@ parse_defs([], Acc) -> reverse(Acc).
meta_info(C=#class{name=CName,methods=Ms0}) ->
Ms = lists:append(Ms0),
HaveConstructor = lists:keymember(constructor, #method.method_type, Ms),
- case lists:keysearch(destructor, #method.method_type, Ms) of
+ case keysearch(destructor, #method.method_type, Ms) of
false when HaveConstructor ->
Dest = #method{name = "destroy", id = next_id(func_id),
method_type = destructor, params = [this(CName)]},
@@ -288,7 +288,7 @@ parse_attr1([{{attr,_}, #xmlElement{content=C, attributes=Attrs}}|R], AttrList0,
parse_attr1([{_Id,_}|R],AttrList,Info, Res) ->
parse_attr1(R,AttrList,Info, Res);
parse_attr1([],Left,_, Res) ->
- {lists:reverse(Res), Left}.
+ {reverse(Res), Left}.
attr_acc(#param{name=N}, List) ->
Name = list_to_atom(N),
@@ -743,7 +743,14 @@ parse_type2([N="wxTreeItemData"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name="wxETreeItemData",base={term,N}});
parse_type2([N="wxClientData"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name="wxeErlTerm",base={term,N}});
-parse_type2([N="wxChar"|R],Info,Opts,T) ->
+parse_type2([N="wxChar",{by_ref,_}|R],Info,Opts,T = #type{mod=[const]}) ->
+ case get(current_class) of
+ "wxLocale" -> %% Special since changed between 2.8 and 3.0
+ parse_type2(R,Info,Opts,T#type{name="wxeLocaleC",base=string});
+ _ ->
+ parse_type2(R,Info,Opts,T#type{name=N,base=int,single=false})
+ end;
+parse_type2([N="wxChar"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name=N,base=int});
parse_type2(["wxUint32"|R],Info,Opts,T=#type{mod=Mod}) ->
parse_type2(R,Info,Opts,T#type{name=int,base=int,mod=[unsigned|Mod]});
@@ -994,7 +1001,7 @@ erl_skip_opt2([F={_,{N,In,_},M=#method{where=Where}}|Ms],Acc1,Acc2,Check) ->
[] ->
erl_skip_opt2(Ms,[F|Acc1],[M#method{where=erl_no_opt}|Acc2],[]);
_ ->
- Skipped = reverse(tl(reverse(In))),
+ Skipped = droplast(In),
T = fun({_,{_,Args,_},_}) -> true =:= types_differ(Skipped,Args) end,
case lists:all(T, Check) of
true ->
@@ -1274,6 +1281,7 @@ parse_enums([File|Files], Parsed) ->
%%io:format("Parse Enums in ~s ~n", [FileName]),
case xmerl_scan:file(FileName, [{space, normalize}]) of
{error, enoent} ->
+ %% io:format("Ignore ~p~n", [FileName]),
parse_enums(Files, gb_sets:add(File,Parsed));
{Doc, _} ->
ES = "./compounddef/sectiondef/memberdef[@kind=\"enum\"]",
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 5ac57e4929..31ed1374c2 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -235,24 +235,27 @@ gen_funcs(Defs) ->
"wxFileDataObject", "wxTextDataObject", "wxBitmapDataObject"
],
- w("void WxeApp::delete_object(void *ptr, wxeRefData *refd) {~n", []),
+ w("bool WxeApp::delete_object(void *ptr, wxeRefData *refd) {~n", []),
w(" switch(refd->type) {~n", []),
- Case = fun(#class{name=Class, id=Id, abstract=IsAbs, parent=P}) when P /= "static" ->
+ Case = fun(C=#class{name=Class, id=Id, abstract=IsAbs, parent=P}) when P /= "static" ->
UglyWorkaround = lists:member(Class, UglySkipList),
+ HaveVirtual = virtual_dest(C),
case hd(reverse(wx_gen_erl:parents(Class))) of
- root when IsAbs == false, UglyWorkaround == false ->
- w(" case ~p: delete (~s *) ptr; break;~n", [Id, Class]);
root when IsAbs == false, UglyWorkaround == true ->
w(" case ~p: /* delete (~s *) ptr;"
"These objects must be deleted by owner object */ "
"break;~n", [Id, Class]);
+ root when IsAbs == false, HaveVirtual == true ->
+ w(" case ~p: delete (E~s *) ptr; return false;~n", [Id, Class]);
+ root when IsAbs == false, UglyWorkaround == false ->
+ w(" case ~p: delete (~s *) ptr; break;~n", [Id, Class]);
_ -> ok
end;
(_) -> ok
end,
[Case(Class) || Class <- Defs],
- w(" default: delete (wxObject *) ptr;~n", []),
- w("}}~n~n", []),
+ w(" default: delete (wxObject *) ptr; return false;~n", []),
+ w(" }~n return true;~n}~n~n", []),
Res.
gen_class(C=#class{name=Name,methods=Ms,options=Opts}) ->
@@ -401,6 +404,8 @@ declare_type(N,true,Def,#type{base=Base,single=true,name=Type,by_val=false,ref={
w(" ~s *~s=~s;~n", [Type,N,Def]);
declare_type(N,true,Def,#type{single=true,name="wxArtClient"}) ->
w(" wxArtClient ~s= ~s;~n", [N,Def]);
+declare_type(N,true,_Def,#type{name="wxeLocaleC", single=true,base=string}) ->
+ w(" wxString ~s= wxEmptyString;~n", [N]);
declare_type(N,true,Def,#type{single=true,base=string}) ->
w(" wxString ~s= ~s;~n", [N,Def]);
%% declare_type(N,true,_Def,#type{name="wxString"}) ->
@@ -997,6 +1002,8 @@ build_ret(Name,_,#type{base=float,single=true}) ->
w(" rt.addFloat(~s);~n",[Name]);
build_ret(Name,_,#type{base=double,single=true}) ->
w(" rt.addFloat(~s);~n",[Name]);
+build_ret(Name,_,#type{name="wxeLocaleC"}) ->
+ w(" rt.add(wxeLocaleC2String(~s));~n",[Name]);
build_ret(Name,_,#type{base=string,single=true}) ->
w(" rt.add(~s);~n",[Name]);
build_ret(Name,_,#type{name="wxArrayString", single=array}) ->
@@ -1190,15 +1197,6 @@ find_id(OtherClass) ->
encode_events(Evs) ->
?WTC("encode_events"),
- w("void wxeEvtListener::forward(wxEvent& event)~n"
- "{~n"
- "#ifdef DEBUG~n"
- " if(!sendevent(&event, port))~n"
- " fprintf(stderr, \"Couldn't send event!\\r\\n\");~n"
- "#else~n"
- "sendevent(&event, port);~n"
- "#endif~n"
- "}~n~n"),
w("int getRef(void* ptr, wxeMemEnv* memenv)~n"
"{~n"
" WxeApp * app = (WxeApp *) wxTheApp;~n"
@@ -1209,7 +1207,7 @@ encode_events(Evs) ->
" char * evClass = NULL;~n"
" wxMBConvUTF32 UTFconverter;~n"
" wxeEtype *Etype = etmap[event->GetEventType()];~n"
- " wxeCallbackData *cb = (wxeCallbackData *)event->m_callbackUserData;~n"
+ " wxeEvtListener *cb = (wxeEvtListener *)event->m_callbackUserData;~n"
" WxeApp * app = (WxeApp *) wxTheApp;~n"
" wxeMemEnv *memenv = app->getMemEnv(port);~n"
" if(!memenv) return 0;~n~n"
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index c87187edc5..5ac781b40c 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -44,7 +44,11 @@ gen(Defs) ->
gen_unique_names(Defs),
gen_event_recs(),
gen_enums_ints(),
- [gen_class(Class) || Class <- Defs],
+ Static = gen_static([C || C=#class{parent="static"} <- Defs]),
+ Replace = fun(C=#class{name=Name}, Dfs) ->
+ lists:keyreplace(Name, #class.name, Dfs, C)
+ end,
+ [gen_class(Class) || Class <- lists:foldl(Replace, Defs, Static)],
gen_funcnames().
gen_class(Class) ->
@@ -54,9 +58,8 @@ gen_class(Class) ->
Class
end.
-gen_class1(C=#class{name=Name,parent="static",methods=Ms,options=_Opts}) ->
+gen_static(Files) ->
open_write("../src/gen/wx_misc.erl"),
- put(current_class, Name),
erl_copyright(),
w("", []),
w("%% This file is generated DO NOT EDIT~n~n", []),
@@ -67,17 +70,27 @@ gen_class1(C=#class{name=Name,parent="static",methods=Ms,options=_Opts}) ->
w("-module(wx_misc).~n", []),
w("-include(\"wxe.hrl\").~n",[]),
%% w("-compile(export_all).~n~n", []), %% XXXX remove ???
+ [gen_static_exports(C) || C <- Files],
+ Classes = [gen_static_methods(C) || C <- Files],
+ close(),
+ Classes.
+
+gen_static_exports(C=#class{parent="static",methods=Ms}) ->
Exp = fun(M) -> gen_export(C,M) end,
ExportList = lists:usort(lists:append(lists:map(Exp,reverse(Ms)))),
w("-export([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", ExportList, 60)]),
+ ok.
+gen_static_methods(C=#class{name=Name, parent="static",methods=Ms}) ->
+ put(current_class, Name),
Gen = fun(M) -> gen_method(Name,M) end,
NewMs = lists:map(Gen,reverse(Ms)),
- close(),
erase(current_class),
- C#class{methods=NewMs};
+ C#class{methods=NewMs}.
+gen_class1(C=#class{parent="static"}) ->
+ C;
gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
case Opts of
["ignore"] -> throw(skipped);
@@ -137,15 +150,27 @@ gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
w("%% inherited exports~n",[]),
Done0 = ["Destroy", "New", "Create", "destroy", "new", "create"],
Done = gb_sets:from_list(Done0 ++ [M|| #method{name=M} <- lists:append(Ms)]),
- {_, InExported} = gen_inherited(Parents, Done, []),
- w("-export([~s]).~n~n", [args(fun(EF) -> EF end, ",",
- lists:usort(["parent_class/1"|InExported]),
+ {_, InExported0} = gen_inherited(Parents, Done, []),
+ InExported = lists:ukeysort(2, [{?MODULE,{"parent_class","1"},false}|InExported0]),
+ w("-export([~s]).~n~n", [args(fun({_M,{F,A},_Dep}) -> F ++ "/" ++ A end, ",",
+ InExported,
60)]),
w("-export_type([~s/0]).~n", [Name]),
case lists:filter(fun({_F,Depr}) -> Depr end, ExportList) of
[] -> ok;
Depr -> w("-deprecated([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", Depr, 60)])
end,
+ case lists:filter(fun({_,_,Depr}) -> Depr end, InExported) of
+ [] -> ok;
+ NoWDepr -> w("-compile([~s]).~n~n",
+ [args(fun({M,{F,A},_}) ->
+ DStr=io_lib:format("{nowarn_deprecated_function, {~s,~s,~s}}",
+ [M,F,A]),
+ lists:flatten(DStr)
+ end, ",", NoWDepr, 60)])
+ end,
+
+
w("%% @hidden~n", []),
parents_check(Parents),
w("-type ~s() :: wx:wx_object().~n", [Name]),
@@ -375,7 +400,7 @@ gen_inherited([Parent|Ps], Done0, Exported0) ->
{Done,Exported} = gen_inherited_ms(Ms, Class, Done0, gb_sets:empty(), Exported0),
gen_inherited(Ps, gb_sets:union(Done,Done0), Exported).
-gen_inherited_ms([[#method{name=Name,alias=A,params=Ps0,where=W,method_type=MT}|_]|R],
+gen_inherited_ms([[M=#method{name=Name,alias=A,params=Ps0,where=W,method_type=MT}|_]|R],
Class,Skip,Done, Exported)
when W =/= merged_c ->
case gb_sets:is_member(Name,Skip) of
@@ -399,8 +424,10 @@ gen_inherited_ms([[#method{name=Name,alias=A,params=Ps0,where=W,method_type=MT}|
_ when W =:= erl_no_opt -> 0;
_ -> 1
end,
- Export = erl_func_name(Name,A) ++ "/" ++ integer_to_list(length(Args) + OptLen),
- gen_inherited_ms(R,Class,Skip, gb_sets:add(Name,Done), [Export|Exported]);
+ {_, Depr} = deprecated(M,ignore),
+ Export = {Class,{erl_func_name(Name,A),integer_to_list(length(Args) + OptLen)}, Depr},
+ gen_inherited_ms(R,Class,Skip, gb_sets:add(Name,Done),
+ [Export|Exported]);
_ ->
gen_inherited_ms(R,Class, Skip, Done, Exported)
end;
@@ -741,7 +768,7 @@ write_spec(Args, Optional, {complex, Res}, Eol) ->
optional_type(Opts, Eol) ->
"Option :: " ++ args(fun optional_type2/1, Eol++"\t\t | ", Opts).
-optional_type2(#param{name=Name, def=Def, type=T}) ->
+optional_type2(#param{name=Name, def=_Def, type=T}) ->
"{" ++ erl_option_name(Name) ++ ", " ++ doc_arg_type2(T) ++ "}". %% %% Default: " ++ Def.
doc_link("utils", Func) ->
@@ -1343,4 +1370,3 @@ split_list(F, Keep, [M|Ms], Acc) ->
split_list(_, _, [], []) -> [];
split_list(_, _, [], Acc) -> [lists:reverse(Acc)].
-
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index 73c5af43d8..3a1dcc7ba5 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -1577,9 +1577,14 @@
'wxShutdown', {'wxShell', 1}, 'wxLaunchDefaultBrowser',
{'wxGetEmailAddress',0}, {'wxGetUserId',0}, {'wxGetHomeDir',0},
'wxNewId', 'wxRegisterId', 'wxGetCurrentId',
- 'wxGetOsDescription', 'wxIsPlatformLittleEndian', 'wxIsPlatform64Bit'
+ 'wxGetOsDescription', 'wxIsPlatformLittleEndian', 'wxIsPlatform64Bit'
]}.
+{class, gdicmn, static, [],
+ [ {'wxDisplaySize', [{"width", [out]}, {"height", [out]}]}, 'wxSetCursor'
+ ]}.
+
+
{class, wxPrintout, object, [{alias, [{wxePrintout, wxePrintout}]}],
[{'wxPrintout', [{where, taylormade}]},'~wxPrintout',
'GetDC', %% 'GetPageInfo',Callback
@@ -1885,3 +1890,15 @@
wxEVT_TASKBAR_LEFT_DCLICK,wxEVT_TASKBAR_RIGHT_DCLICK]}],[]}.
{class, wxInitDialogEvent, wxEvent, [{event,[wxEVT_INIT_DIALOG]}], []}.
+
+{class, wxLocale, root, [{skip, [{'wxLocale', 5}, {'Init', 6}]}],
+ ['wxLocale', '~wxLocale', 'Init',
+ 'AddCatalog','AddCatalogLookupPathPrefix',
+ %%'AddLanguage','FindLanguageInfo', 'GetLanguageInfo',
+ 'GetCanonicalName','GetLanguage',
+ 'GetLanguageName','GetLocale','GetName','GetString',
+ 'GetHeaderValue',
+ 'GetSysName',
+ 'GetSystemEncoding','GetSystemEncodingName',
+ 'GetSystemLanguage',
+ 'IsLoaded','IsOk']}.
diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in
index 93a191378f..4a7342f714 100644
--- a/lib/wx/c_src/Makefile.in
+++ b/lib/wx/c_src/Makefile.in
@@ -80,8 +80,8 @@ TARGET_DIR = ../priv/$(SYS_TYPE)
COMMON_CFLAGS = @DEFS@ $(ERL_INCS)
CC = @CC@
-CPP = @CXX@
-LD = $(CPP)
+CXX = @CXX@
+LD = $(CXX)
LDFLAGS = @LDFLAGS@
RESCOMP = @WX_RESCOMP@
@@ -111,7 +111,7 @@ GL_LIBS = @GL_LIBS@
CC_O = $(V_CC) -c $(CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS)
OBJC_CC_O = $(OBJC_CC) -c $(CFLAGS) $(OBJC_CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS)
-CPP_O = $(V_CPP) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS)
+CXX_O = $(V_CXX) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS)
# Targets
@@ -141,7 +141,7 @@ $(WX_OBJECTS): $(GENERATED_H) $(GENERAL_H)
$(SYS_TYPE)/%.o: %.cpp
$(V_at)mkdir -p $(SYS_TYPE)
- $(CPP_O) $< -o $@
+ $(CXX_O) $< -o $@
$(SYS_TYPE)/%.o: %.c
$(V_at)mkdir -p $(SYS_TYPE)
@@ -153,7 +153,7 @@ $(SYS_TYPE)/wxe_ps_init.o: wxe_ps_init.c
$(SYS_TYPE)/%.o: gen/%.cpp
$(V_at)mkdir -p $(SYS_TYPE)
- $(CPP_O) $< -o $@
+ $(CXX_O) $< -o $@
$(SYS_TYPE)/%.o: gen/%.c
$(V_at)mkdir -p $(SYS_TYPE)
diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h
index 7e2d4524cb..42925bff3a 100644
--- a/lib/wx/c_src/gen/wxe_derived_dest.h
+++ b/lib/wx/c_src/gen/wxe_derived_dest.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2014. 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
@@ -764,3 +764,9 @@ class EwxTaskBarIcon : public wxTaskBarIcon {
EwxTaskBarIcon() : wxTaskBarIcon() {};
};
+class EwxLocale : public wxLocale {
+ public: ~EwxLocale() {((WxeApp *)wxTheApp)->clearPtr(this);};
+ EwxLocale(int language,int flags) : wxLocale(language,flags) {};
+ EwxLocale() : wxLocale() {};
+};
+
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index a1b4d090b3..0ca059ead4 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -220,84 +220,84 @@ void initEventTable()
{wxEVT_STC_HOTSPOT_DCLICK, 203, "stc_hotspot_dclick"},
{wxEVT_STC_CALLTIP_CLICK, 203, "stc_calltip_click"},
{wxEVT_STC_AUTOCOMP_SELECTION, 203, "stc_autocomp_selection"},
- {wxEVT_COMMAND_TREE_BEGIN_DRAG, 208, "command_tree_begin_drag"},
- {wxEVT_COMMAND_TREE_BEGIN_RDRAG, 208, "command_tree_begin_rdrag"},
- {wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 208, "command_tree_begin_label_edit"},
- {wxEVT_COMMAND_TREE_END_LABEL_EDIT, 208, "command_tree_end_label_edit"},
- {wxEVT_COMMAND_TREE_DELETE_ITEM, 208, "command_tree_delete_item"},
- {wxEVT_COMMAND_TREE_GET_INFO, 208, "command_tree_get_info"},
- {wxEVT_COMMAND_TREE_SET_INFO, 208, "command_tree_set_info"},
- {wxEVT_COMMAND_TREE_ITEM_EXPANDED, 208, "command_tree_item_expanded"},
- {wxEVT_COMMAND_TREE_ITEM_EXPANDING, 208, "command_tree_item_expanding"},
- {wxEVT_COMMAND_TREE_ITEM_COLLAPSED, 208, "command_tree_item_collapsed"},
- {wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 208, "command_tree_item_collapsing"},
- {wxEVT_COMMAND_TREE_SEL_CHANGED, 208, "command_tree_sel_changed"},
- {wxEVT_COMMAND_TREE_SEL_CHANGING, 208, "command_tree_sel_changing"},
- {wxEVT_COMMAND_TREE_KEY_DOWN, 208, "command_tree_key_down"},
- {wxEVT_COMMAND_TREE_ITEM_ACTIVATED, 208, "command_tree_item_activated"},
- {wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, 208, "command_tree_item_right_click"},
- {wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 208, "command_tree_item_middle_click"},
- {wxEVT_COMMAND_TREE_END_DRAG, 208, "command_tree_end_drag"},
- {wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 208, "command_tree_state_image_click"},
- {wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 208, "command_tree_item_gettooltip"},
- {wxEVT_COMMAND_TREE_ITEM_MENU, 208, "command_tree_item_menu"},
- {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, 209, "command_notebook_page_changed"},
- {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, 209, "command_notebook_page_changing"},
- {wxEVT_COMMAND_TEXT_COPY, 215, "command_text_copy"},
- {wxEVT_COMMAND_TEXT_CUT, 215, "command_text_cut"},
- {wxEVT_COMMAND_TEXT_PASTE, 215, "command_text_paste"},
- {wxEVT_COMMAND_SPINCTRL_UPDATED, 216, "command_spinctrl_updated"},
+ {wxEVT_COMMAND_TREE_BEGIN_DRAG, 209, "command_tree_begin_drag"},
+ {wxEVT_COMMAND_TREE_BEGIN_RDRAG, 209, "command_tree_begin_rdrag"},
+ {wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 209, "command_tree_begin_label_edit"},
+ {wxEVT_COMMAND_TREE_END_LABEL_EDIT, 209, "command_tree_end_label_edit"},
+ {wxEVT_COMMAND_TREE_DELETE_ITEM, 209, "command_tree_delete_item"},
+ {wxEVT_COMMAND_TREE_GET_INFO, 209, "command_tree_get_info"},
+ {wxEVT_COMMAND_TREE_SET_INFO, 209, "command_tree_set_info"},
+ {wxEVT_COMMAND_TREE_ITEM_EXPANDED, 209, "command_tree_item_expanded"},
+ {wxEVT_COMMAND_TREE_ITEM_EXPANDING, 209, "command_tree_item_expanding"},
+ {wxEVT_COMMAND_TREE_ITEM_COLLAPSED, 209, "command_tree_item_collapsed"},
+ {wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 209, "command_tree_item_collapsing"},
+ {wxEVT_COMMAND_TREE_SEL_CHANGED, 209, "command_tree_sel_changed"},
+ {wxEVT_COMMAND_TREE_SEL_CHANGING, 209, "command_tree_sel_changing"},
+ {wxEVT_COMMAND_TREE_KEY_DOWN, 209, "command_tree_key_down"},
+ {wxEVT_COMMAND_TREE_ITEM_ACTIVATED, 209, "command_tree_item_activated"},
+ {wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, 209, "command_tree_item_right_click"},
+ {wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 209, "command_tree_item_middle_click"},
+ {wxEVT_COMMAND_TREE_END_DRAG, 209, "command_tree_end_drag"},
+ {wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 209, "command_tree_state_image_click"},
+ {wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 209, "command_tree_item_gettooltip"},
+ {wxEVT_COMMAND_TREE_ITEM_MENU, 209, "command_tree_item_menu"},
+ {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, 210, "command_notebook_page_changed"},
+ {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, 210, "command_notebook_page_changing"},
+ {wxEVT_COMMAND_TEXT_COPY, 216, "command_text_copy"},
+ {wxEVT_COMMAND_TEXT_CUT, 216, "command_text_cut"},
+ {wxEVT_COMMAND_TEXT_PASTE, 216, "command_text_paste"},
+ {wxEVT_COMMAND_SPINCTRL_UPDATED, 217, "command_spinctrl_updated"},
{wxEVT_SCROLL_LINEUP + wxEVT_USER_FIRST, 165, "spin_up"},
{wxEVT_SCROLL_LINEDOWN + wxEVT_USER_FIRST, 165, "spin_down"},
{wxEVT_SCROLL_THUMBTRACK + wxEVT_USER_FIRST, 165, "spin"},
- {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, 218, "command_splitter_sash_pos_changed"},
- {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, 218, "command_splitter_sash_pos_changing"},
- {wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, 218, "command_splitter_doubleclicked"},
- {wxEVT_COMMAND_SPLITTER_UNSPLIT, 218, "command_splitter_unsplit"},
- {wxEVT_COMMAND_HTML_LINK_CLICKED, 220, "command_html_link_clicked"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 223, "command_auinotebook_page_close"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 223, "command_auinotebook_page_changed"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 223, "command_auinotebook_page_changing"},
- {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 223, "command_auinotebook_button"},
- {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 223, "command_auinotebook_begin_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 223, "command_auinotebook_end_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 223, "command_auinotebook_drag_motion"},
- {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 223, "command_auinotebook_allow_dnd"},
+ {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, 219, "command_splitter_sash_pos_changed"},
+ {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, 219, "command_splitter_sash_pos_changing"},
+ {wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, 219, "command_splitter_doubleclicked"},
+ {wxEVT_COMMAND_SPLITTER_UNSPLIT, 219, "command_splitter_unsplit"},
+ {wxEVT_COMMAND_HTML_LINK_CLICKED, 221, "command_html_link_clicked"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 224, "command_auinotebook_page_close"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 224, "command_auinotebook_page_changed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 224, "command_auinotebook_page_changing"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 224, "command_auinotebook_button"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 224, "command_auinotebook_begin_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 224, "command_auinotebook_end_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 224, "command_auinotebook_drag_motion"},
+ {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 224, "command_auinotebook_allow_dnd"},
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 223, "command_auinotebook_tab_middle_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 224, "command_auinotebook_tab_middle_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 223, "command_auinotebook_tab_middle_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 224, "command_auinotebook_tab_middle_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 223, "command_auinotebook_tab_right_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 224, "command_auinotebook_tab_right_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 223, "command_auinotebook_tab_right_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 224, "command_auinotebook_tab_right_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 223, "command_auinotebook_page_closed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 224, "command_auinotebook_page_closed"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 223, "command_auinotebook_drag_done"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 224, "command_auinotebook_drag_done"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 223, "command_auinotebook_bg_dclick"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 224, "command_auinotebook_bg_dclick"},
#endif
- {wxEVT_AUI_PANE_BUTTON, 224, "aui_pane_button"},
- {wxEVT_AUI_PANE_CLOSE, 224, "aui_pane_close"},
- {wxEVT_AUI_PANE_MAXIMIZE, 224, "aui_pane_maximize"},
- {wxEVT_AUI_PANE_RESTORE, 224, "aui_pane_restore"},
- {wxEVT_AUI_RENDER, 224, "aui_render"},
- {wxEVT_AUI_FIND_MANAGER, 224, "aui_find_manager"},
- {wxEVT_TASKBAR_MOVE, 227, "taskbar_move"},
- {wxEVT_TASKBAR_LEFT_DOWN, 227, "taskbar_left_down"},
- {wxEVT_TASKBAR_LEFT_UP, 227, "taskbar_left_up"},
- {wxEVT_TASKBAR_RIGHT_DOWN, 227, "taskbar_right_down"},
- {wxEVT_TASKBAR_RIGHT_UP, 227, "taskbar_right_up"},
- {wxEVT_TASKBAR_LEFT_DCLICK, 227, "taskbar_left_dclick"},
- {wxEVT_TASKBAR_RIGHT_DCLICK, 227, "taskbar_right_dclick"},
- {wxEVT_INIT_DIALOG, 228, "init_dialog"},
+ {wxEVT_AUI_PANE_BUTTON, 225, "aui_pane_button"},
+ {wxEVT_AUI_PANE_CLOSE, 225, "aui_pane_close"},
+ {wxEVT_AUI_PANE_MAXIMIZE, 225, "aui_pane_maximize"},
+ {wxEVT_AUI_PANE_RESTORE, 225, "aui_pane_restore"},
+ {wxEVT_AUI_RENDER, 225, "aui_render"},
+ {wxEVT_AUI_FIND_MANAGER, 225, "aui_find_manager"},
+ {wxEVT_TASKBAR_MOVE, 228, "taskbar_move"},
+ {wxEVT_TASKBAR_LEFT_DOWN, 228, "taskbar_left_down"},
+ {wxEVT_TASKBAR_LEFT_UP, 228, "taskbar_left_up"},
+ {wxEVT_TASKBAR_RIGHT_DOWN, 228, "taskbar_right_down"},
+ {wxEVT_TASKBAR_RIGHT_UP, 228, "taskbar_right_up"},
+ {wxEVT_TASKBAR_LEFT_DCLICK, 228, "taskbar_left_dclick"},
+ {wxEVT_TASKBAR_RIGHT_DCLICK, 228, "taskbar_right_dclick"},
+ {wxEVT_INIT_DIALOG, 229, "init_dialog"},
{-1, 0, }
};
for(int i=0; event_types[i].ev_type != -1; i++) {
@@ -316,16 +316,6 @@ void initEventTable()
}
}
-void wxeEvtListener::forward(wxEvent& event)
-{
-#ifdef DEBUG
- if(!sendevent(&event, port))
- fprintf(stderr, "Couldn't send event!\r\n");
-#else
-sendevent(&event, port);
-#endif
-}
-
int getRef(void* ptr, wxeMemEnv* memenv)
{
WxeApp * app = (WxeApp *) wxTheApp;
@@ -338,7 +328,7 @@ bool sendevent(wxEvent *event, ErlDrvTermData port)
char * evClass = NULL;
wxMBConvUTF32 UTFconverter;
wxeEtype *Etype = etmap[event->GetEventType()];
- wxeCallbackData *cb = (wxeCallbackData *)event->m_callbackUserData;
+ wxeEvtListener *cb = (wxeEvtListener *)event->m_callbackUserData;
WxeApp * app = (WxeApp *) wxTheApp;
wxeMemEnv *memenv = app->getMemEnv(port);
if(!memenv) return 0;
@@ -729,7 +719,7 @@ case 203: {// wxStyledTextEvent
rt.addTupleCount(22);
break;
}
-case 208: {// wxTreeEvent
+case 209: {// wxTreeEvent
wxTreeEvent * ev = (wxTreeEvent *) event;
evClass = (char*)"wxTreeEvent";
rt.addAtom((char*)"wxTree");
@@ -740,21 +730,21 @@ case 208: {// wxTreeEvent
rt.addTupleCount(5);
break;
}
-case 209: {// wxNotebookEvent
+case 210: {// wxNotebookEvent
evClass = (char*)"wxNotebookEvent";
rt.addAtom((char*)"wxNotebook");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 215: {// wxClipboardTextEvent
+case 216: {// wxClipboardTextEvent
evClass = (char*)"wxClipboardTextEvent";
rt.addAtom((char*)"wxClipboardText");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 216: {// wxSpinEvent
+case 217: {// wxSpinEvent
wxSpinEvent * ev = (wxSpinEvent *) event;
evClass = (char*)"wxSpinEvent";
rt.addAtom((char*)"wxSpin");
@@ -763,14 +753,14 @@ case 216: {// wxSpinEvent
rt.addTupleCount(3);
break;
}
-case 218: {// wxSplitterEvent
+case 219: {// wxSplitterEvent
evClass = (char*)"wxSplitterEvent";
rt.addAtom((char*)"wxSplitter");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 220: {// wxHtmlLinkEvent
+case 221: {// wxHtmlLinkEvent
wxHtmlLinkEvent * ev = (wxHtmlLinkEvent *) event;
evClass = (char*)"wxHtmlLinkEvent";
rt.addAtom((char*)"wxHtmlLink");
@@ -779,7 +769,7 @@ case 220: {// wxHtmlLinkEvent
rt.addTupleCount(3);
break;
}
-case 223: {// wxAuiNotebookEvent
+case 224: {// wxAuiNotebookEvent
wxAuiNotebookEvent * ev = (wxAuiNotebookEvent *) event;
wxAuiNotebook * GetDragSource = ev->GetDragSource();
evClass = (char*)"wxAuiNotebookEvent";
@@ -791,7 +781,7 @@ case 223: {// wxAuiNotebookEvent
rt.addTupleCount(5);
break;
}
-case 224: {// wxAuiManagerEvent
+case 225: {// wxAuiManagerEvent
wxAuiManagerEvent * ev = (wxAuiManagerEvent *) event;
wxAuiManager * GetManager = ev->GetManager();
wxAuiPaneInfo * GetPane = ev->GetPane();
@@ -808,14 +798,14 @@ case 224: {// wxAuiManagerEvent
rt.addTupleCount(8);
break;
}
-case 227: {// wxTaskBarIconEvent
+case 228: {// wxTaskBarIconEvent
evClass = (char*)"wxTaskBarIconEvent";
rt.addAtom((char*)"wxTaskBarIcon");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 228: {// wxInitDialogEvent
+case 229: {// wxInitDialogEvent
evClass = (char*)"wxInitDialogEvent";
rt.addAtom((char*)"wxInitDialog");
rt.addAtom(Etype->eName);
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 82dd414911..c1e9f3829a 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -67,20 +67,8 @@ void WxeApp::wxe_dispatch(wxeCommand& Ecmd)
case WXE_INIT_OPENGL:
wxe_initOpenGL(rt, bp);
break;
-case 98: { // wxeEvtListener::wxeEvtListener
- wxeEvtListener *Result = new wxeEvtListener(Ecmd.port);
- rt.addRef(getRef((void *)Result,memenv), "wxeEvtListener");
- break;
-}
-case 99: { // wxeEvtListener::destroy
- wxObject *This = (wxObject *) getPtr(bp,memenv);
- rt.addAtom("ok");
- delete This;
- break;
-}
-case 100: { // wxEvtHandler::Connect
- wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
+case 100: { // wxEvtHandler::Connect
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
int * lastId = (int *) bp; bp += 4;
@@ -91,20 +79,22 @@ case 100: { // wxEvtHandler::Connect
int * eventTypeLen = (int *) bp; bp += 4;
int * class_nameLen = (int *) bp; bp += 4;
- if(*haveUserData) {
+ if(*haveUserData) {
userData = new wxeErlTerm(Ecmd.bin[0]);
}
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
char *class_name = bp; bp+= *class_nameLen;
if(eventType > 0 ) {
- wxeCallbackData * Evt_cb = new wxeCallbackData(Ecmd.caller,getRef(This, memenv),
- class_name,*fun_cb,
- *skip, userData, Listener);
+ wxeEvtListener * Evt_cb = new wxeEvtListener(Ecmd.caller,getRef(This, memenv),
+ class_name,*fun_cb,
+ *skip, userData, Ecmd.port);
This->Connect((int) *winid,(int) *lastId,eventType,
(wxObjectEventFunction)(wxEventFunction) &wxeEvtListener::forward,
- Evt_cb, Listener);
+ Evt_cb, Evt_cb);
rt.addAtom("ok");
+ rt.addRef(getRef((void *)Evt_cb,memenv), "wxeEvtListener");
+ rt.addTupleCount(2);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
@@ -112,7 +102,7 @@ case 100: { // wxEvtHandler::Connect
}
break;
}
-case 101: { // wxEvtHandler::Disconnect
+case 101: { // wxEvtHandler::Disconnect
wxeEvtListener *Listener = (wxeEvtListener *) getPtr(bp,memenv); bp += 4;
wxEvtHandler *This = (wxEvtHandler *) getPtr(bp, memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
@@ -122,14 +112,14 @@ case 101: { // wxEvtHandler::Disconnect
int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen;
if(eventType > 0) {
bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType,
- (wxObjectEventFunction)(wxEventFunction)
- &wxeEvtListener::forward,
- NULL, Listener);
+ (wxObjectEventFunction)(wxEventFunction)
+ &wxeEvtListener::forward,
+ NULL, Listener);
rt.addBool(Result);
} else {
rt.addAtom("badarg");
rt.addAtom("event_type");
- rt.addTupleCount(2);
+ rt.addTupleCount(2);
}
break;
}
@@ -15885,7 +15875,7 @@ case wxListCtrl_SortItems: { // wxListCtrl::SortItems taylormade
callbackInfo* cb = new callbackInfo();
cb->port = Ecmd.port;
cb->callbackID = sortCallback;
- bool Result = This->SortItems(wxEListCtrlCompare, (long)cb);
+ bool Result = This->SortItems(wxEListCtrlCompare, (wxeIntPtr)cb);
delete cb;
/* Destroy the callback, see wxEPrintout::clear_cb */
@@ -26740,6 +26730,20 @@ case utils_wxIsPlatform64Bit: { // utils::wxIsPlatform64Bit
rt.addBool(Result);
break;
}
+case gdicmn_wxDisplaySize: { // gdicmn::wxDisplaySize
+ int width;
+ int height;
+ ::wxDisplaySize(&width,&height);
+ rt.addInt(width);
+ rt.addInt(height);
+ rt.addTupleCount(2);
+ break;
+}
+case gdicmn_wxSetCursor: { // gdicmn::wxSetCursor
+ wxCursor *cursor = (wxCursor *) getPtr(bp,memenv); bp += 4;
+ ::wxSetCursor(*cursor);
+ break;
+}
case wxPrintout_new: { // wxPrintout::wxPrintout taylormade
int onPreparePrinting=0,onBeginPrinting=0,onEndPrinting=0,onBeginDocument=0,
@@ -30452,7 +30456,7 @@ case wxNotebookEvent_SetSelection: { // wxNotebookEvent::SetSelection
}
case wxFileDataObject_new: { // wxFileDataObject::wxFileDataObject
wxFileDataObject * Result = new wxFileDataObject();
- newPtr((void *) Result, 211, memenv);
+ newPtr((void *) Result, 212, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFileDataObject");
break;
}
@@ -30488,7 +30492,7 @@ case wxTextDataObject_new: { // wxTextDataObject::wxTextDataObject
} break;
}};
wxTextDataObject * Result = new wxTextDataObject(text);
- newPtr((void *) Result, 212, memenv);
+ newPtr((void *) Result, 213, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxTextDataObject");
break;
}
@@ -30524,7 +30528,7 @@ case wxTextDataObject_destroy: { // wxTextDataObject::destroy
case wxBitmapDataObject_new_1_1: { // wxBitmapDataObject::wxBitmapDataObject
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
wxBitmapDataObject * Result = new wxBitmapDataObject(*bitmap);
- newPtr((void *) Result, 213, memenv);
+ newPtr((void *) Result, 214, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapDataObject");
break;
}
@@ -30536,7 +30540,7 @@ bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
} break;
}};
wxBitmapDataObject * Result = new wxBitmapDataObject(*bitmap);
- newPtr((void *) Result, 213, memenv);
+ newPtr((void *) Result, 214, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapDataObject");
break;
}
@@ -31369,7 +31373,7 @@ case wxAuiManagerEvent_CanVeto: { // wxAuiManagerEvent::CanVeto
}
case wxLogNull_new: { // wxLogNull::wxLogNull
wxLogNull * Result = new wxLogNull();
- newPtr((void *) Result, 225, memenv);
+ newPtr((void *) Result, 226, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxLogNull");
break;
}
@@ -31416,6 +31420,213 @@ case wxTaskBarIcon_SetIcon: { // wxTaskBarIcon::SetIcon
rt.addBool(Result);
break;
}
+case wxLocale_new_0: { // wxLocale::wxLocale
+ wxLocale * Result = new EwxLocale();
+ newPtr((void *) Result, 230, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxLocale");
+ break;
+}
+case wxLocale_new_2: { // wxLocale::wxLocale
+ int flags=wxLOCALE_LOAD_DEFAULT|wxLOCALE_CONV_ENCODING;
+ int * language = (int *) bp; bp += 4;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ flags = (int)*(int *) bp; bp += 4;
+ } break;
+ }};
+ wxLocale * Result = new EwxLocale(*language,flags);
+ newPtr((void *) Result, 230, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxLocale");
+ break;
+}
+case wxLocale_destruct: { // wxLocale::~wxLocale
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(This) { ((WxeApp *) wxTheApp)->clearPtr((void *) This);
+ delete This;}
+ break;
+}
+case wxLocale_Init: { // wxLocale::Init
+ int language=wxLANGUAGE_DEFAULT;
+ int flags=wxLOCALE_LOAD_DEFAULT|wxLOCALE_CONV_ENCODING;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ language = (int)*(int *) bp; bp += 4;
+ } break;
+ case 2: {bp += 4;
+ flags = (int)*(int *) bp; bp += 4;
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->Init(language,flags);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_AddCatalog_1: { // wxLocale::AddCatalog
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ wxString szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->AddCatalog(szDomain);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_AddCatalog_3: { // wxLocale::AddCatalog
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ wxString szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ wxLanguage msgIdLanguage = *(wxLanguage *) bp; bp += 4;;
+ int * msgIdCharsetLen = (int *) bp; bp += 4;
+ wxString msgIdCharset = wxString(bp, wxConvUTF8);
+ bp += *msgIdCharsetLen+((8-((0+ *msgIdCharsetLen) & 7)) & 7);
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->AddCatalog(szDomain,msgIdLanguage,msgIdCharset);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_AddCatalogLookupPathPrefix: { // wxLocale::AddCatalogLookupPathPrefix
+ int * prefixLen = (int *) bp; bp += 4;
+ wxString prefix = wxString(bp, wxConvUTF8);
+ bp += *prefixLen+((8-((4+ *prefixLen) & 7)) & 7);
+ wxLocale::AddCatalogLookupPathPrefix(prefix);
+ break;
+}
+case wxLocale_GetCanonicalName: { // wxLocale::GetCanonicalName
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetCanonicalName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetLanguage: { // wxLocale::GetLanguage
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ int Result = This->GetLanguage();
+ rt.addInt(Result);
+ break;
+}
+case wxLocale_GetLanguageName: { // wxLocale::GetLanguageName
+ int * lang = (int *) bp; bp += 4;
+ wxString Result = wxLocale::GetLanguageName(*lang);
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetLocale: { // wxLocale::GetLocale
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ const wxeLocaleC Result = This->GetLocale();
+ rt.add(wxeLocaleC2String(Result));
+ break;
+}
+case wxLocale_GetName: { // wxLocale::GetName
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ const wxString * Result = &This->GetName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetString_2: { // wxLocale::GetString
+ wxString szDomain= wxEmptyString;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szOrigStringLen = (int *) bp; bp += 4;
+ wxString szOrigString = wxString(bp, wxConvUTF8);
+ bp += *szOrigStringLen+((8-((0+ *szOrigStringLen) & 7)) & 7);
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ const wxeLocaleC Result = This->GetString(szOrigString,szDomain);
+ rt.add(wxeLocaleC2String(Result));
+ break;
+}
+case wxLocale_GetString_4: { // wxLocale::GetString
+ wxString szDomain= wxEmptyString;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szOrigStringLen = (int *) bp; bp += 4;
+ wxString szOrigString = wxString(bp, wxConvUTF8);
+ bp += *szOrigStringLen+((8-((0+ *szOrigStringLen) & 7)) & 7);
+ int * szOrigString2Len = (int *) bp; bp += 4;
+ wxString szOrigString2 = wxString(bp, wxConvUTF8);
+ bp += *szOrigString2Len+((8-((4+ *szOrigString2Len) & 7)) & 7);
+ int * n = (int *) bp; bp += 4;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ const wxeLocaleC Result = This->GetString(szOrigString,szOrigString2,*n,szDomain);
+ rt.add(wxeLocaleC2String(Result));
+ break;
+}
+case wxLocale_GetHeaderValue: { // wxLocale::GetHeaderValue
+ wxString szDomain= wxEmptyString;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szHeaderLen = (int *) bp; bp += 4;
+ wxString szHeader = wxString(bp, wxConvUTF8);
+ bp += *szHeaderLen+((8-((0+ *szHeaderLen) & 7)) & 7);
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetHeaderValue(szHeader,szDomain);
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetSysName: { // wxLocale::GetSysName
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetSysName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetSystemEncoding: { // wxLocale::GetSystemEncoding
+ int Result = wxLocale::GetSystemEncoding();
+ rt.addInt(Result);
+ break;
+}
+case wxLocale_GetSystemEncodingName: { // wxLocale::GetSystemEncodingName
+ wxString Result = wxLocale::GetSystemEncodingName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetSystemLanguage: { // wxLocale::GetSystemLanguage
+ int Result = wxLocale::GetSystemLanguage();
+ rt.addInt(Result);
+ break;
+}
+case wxLocale_IsLoaded: { // wxLocale::IsLoaded
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ wxString szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->IsLoaded(szDomain);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_IsOk: { // wxLocale::IsOk
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->IsOk();
+ rt.addBool(Result);
+ break;
+}
default: {
wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_");
error.addInt((int) Ecmd.op);
@@ -31437,7 +31648,7 @@ case wxTaskBarIcon_SetIcon: { // wxTaskBarIcon::SetIcon
}} /* The End */
-void WxeApp::delete_object(void *ptr, wxeRefData *refd) {
+bool WxeApp::delete_object(void *ptr, wxeRefData *refd) {
switch(refd->type) {
case 24: delete (wxGridCellBoolRenderer *) ptr; break;
case 25: delete (wxGridCellBoolEditor *) ptr; break;
@@ -31456,10 +31667,13 @@ void WxeApp::delete_object(void *ptr, wxeRefData *refd) {
case 101: delete (wxListItemAttr *) ptr; break;
case 103: delete (wxTextAttr *) ptr; break;
case 155: delete (wxAuiPaneInfo *) ptr; break;
- case 211: /* delete (wxFileDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 212: /* delete (wxTextDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 213: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 225: delete (wxLogNull *) ptr; break;
- default: delete (wxObject *) ptr;
-}}
+ case 212: /* delete (wxFileDataObject *) ptr;These objects must be deleted by owner object */ break;
+ case 213: /* delete (wxTextDataObject *) ptr;These objects must be deleted by owner object */ break;
+ case 214: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break;
+ case 226: delete (wxLogNull *) ptr; break;
+ case 230: delete (EwxLocale *) ptr; return false;
+ default: delete (wxObject *) ptr; return false;
+ }
+ return true;
+}
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index a1da6500d5..2da24f5d5e 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2014. 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
@@ -2756,601 +2756,624 @@
#define utils_wxGetOsDescription 2924
#define utils_wxIsPlatformLittleEndian 2925
#define utils_wxIsPlatform64Bit 2926
-#define wxPrintout_new 2927
-#define wxPrintout_destruct 2928
-#define wxPrintout_GetDC 2929
-#define wxPrintout_GetPageSizeMM 2930
-#define wxPrintout_GetPageSizePixels 2931
-#define wxPrintout_GetPaperRectPixels 2932
-#define wxPrintout_GetPPIPrinter 2933
-#define wxPrintout_GetPPIScreen 2934
-#define wxPrintout_GetTitle 2935
-#define wxPrintout_IsPreview 2936
-#define wxPrintout_FitThisSizeToPaper 2937
-#define wxPrintout_FitThisSizeToPage 2938
-#define wxPrintout_FitThisSizeToPageMargins 2939
-#define wxPrintout_MapScreenSizeToPaper 2940
-#define wxPrintout_MapScreenSizeToPage 2941
-#define wxPrintout_MapScreenSizeToPageMargins 2942
-#define wxPrintout_MapScreenSizeToDevice 2943
-#define wxPrintout_GetLogicalPaperRect 2944
-#define wxPrintout_GetLogicalPageRect 2945
-#define wxPrintout_GetLogicalPageMarginsRect 2946
-#define wxPrintout_SetLogicalOrigin 2947
-#define wxPrintout_OffsetLogicalOrigin 2948
-#define wxStyledTextCtrl_new_2 2949
-#define wxStyledTextCtrl_new_0 2950
-#define wxStyledTextCtrl_destruct 2951
-#define wxStyledTextCtrl_Create 2952
-#define wxStyledTextCtrl_AddText 2953
-#define wxStyledTextCtrl_AddStyledText 2954
-#define wxStyledTextCtrl_InsertText 2955
-#define wxStyledTextCtrl_ClearAll 2956
-#define wxStyledTextCtrl_ClearDocumentStyle 2957
-#define wxStyledTextCtrl_GetLength 2958
-#define wxStyledTextCtrl_GetCharAt 2959
-#define wxStyledTextCtrl_GetCurrentPos 2960
-#define wxStyledTextCtrl_GetAnchor 2961
-#define wxStyledTextCtrl_GetStyleAt 2962
-#define wxStyledTextCtrl_Redo 2963
-#define wxStyledTextCtrl_SetUndoCollection 2964
-#define wxStyledTextCtrl_SelectAll 2965
-#define wxStyledTextCtrl_SetSavePoint 2966
-#define wxStyledTextCtrl_GetStyledText 2967
-#define wxStyledTextCtrl_CanRedo 2968
-#define wxStyledTextCtrl_MarkerLineFromHandle 2969
-#define wxStyledTextCtrl_MarkerDeleteHandle 2970
-#define wxStyledTextCtrl_GetUndoCollection 2971
-#define wxStyledTextCtrl_GetViewWhiteSpace 2972
-#define wxStyledTextCtrl_SetViewWhiteSpace 2973
-#define wxStyledTextCtrl_PositionFromPoint 2974
-#define wxStyledTextCtrl_PositionFromPointClose 2975
-#define wxStyledTextCtrl_GotoLine 2976
-#define wxStyledTextCtrl_GotoPos 2977
-#define wxStyledTextCtrl_SetAnchor 2978
-#define wxStyledTextCtrl_GetCurLine 2979
-#define wxStyledTextCtrl_GetEndStyled 2980
-#define wxStyledTextCtrl_ConvertEOLs 2981
-#define wxStyledTextCtrl_GetEOLMode 2982
-#define wxStyledTextCtrl_SetEOLMode 2983
-#define wxStyledTextCtrl_StartStyling 2984
-#define wxStyledTextCtrl_SetStyling 2985
-#define wxStyledTextCtrl_GetBufferedDraw 2986
-#define wxStyledTextCtrl_SetBufferedDraw 2987
-#define wxStyledTextCtrl_SetTabWidth 2988
-#define wxStyledTextCtrl_GetTabWidth 2989
-#define wxStyledTextCtrl_SetCodePage 2990
-#define wxStyledTextCtrl_MarkerDefine 2991
-#define wxStyledTextCtrl_MarkerSetForeground 2992
-#define wxStyledTextCtrl_MarkerSetBackground 2993
-#define wxStyledTextCtrl_MarkerAdd 2994
-#define wxStyledTextCtrl_MarkerDelete 2995
-#define wxStyledTextCtrl_MarkerDeleteAll 2996
-#define wxStyledTextCtrl_MarkerGet 2997
-#define wxStyledTextCtrl_MarkerNext 2998
-#define wxStyledTextCtrl_MarkerPrevious 2999
-#define wxStyledTextCtrl_MarkerDefineBitmap 3000
-#define wxStyledTextCtrl_MarkerAddSet 3001
-#define wxStyledTextCtrl_MarkerSetAlpha 3002
-#define wxStyledTextCtrl_SetMarginType 3003
-#define wxStyledTextCtrl_GetMarginType 3004
-#define wxStyledTextCtrl_SetMarginWidth 3005
-#define wxStyledTextCtrl_GetMarginWidth 3006
-#define wxStyledTextCtrl_SetMarginMask 3007
-#define wxStyledTextCtrl_GetMarginMask 3008
-#define wxStyledTextCtrl_SetMarginSensitive 3009
-#define wxStyledTextCtrl_GetMarginSensitive 3010
-#define wxStyledTextCtrl_StyleClearAll 3011
-#define wxStyledTextCtrl_StyleSetForeground 3012
-#define wxStyledTextCtrl_StyleSetBackground 3013
-#define wxStyledTextCtrl_StyleSetBold 3014
-#define wxStyledTextCtrl_StyleSetItalic 3015
-#define wxStyledTextCtrl_StyleSetSize 3016
-#define wxStyledTextCtrl_StyleSetFaceName 3017
-#define wxStyledTextCtrl_StyleSetEOLFilled 3018
-#define wxStyledTextCtrl_StyleResetDefault 3019
-#define wxStyledTextCtrl_StyleSetUnderline 3020
-#define wxStyledTextCtrl_StyleSetCase 3021
-#define wxStyledTextCtrl_StyleSetHotSpot 3022
-#define wxStyledTextCtrl_SetSelForeground 3023
-#define wxStyledTextCtrl_SetSelBackground 3024
-#define wxStyledTextCtrl_GetSelAlpha 3025
-#define wxStyledTextCtrl_SetSelAlpha 3026
-#define wxStyledTextCtrl_SetCaretForeground 3027
-#define wxStyledTextCtrl_CmdKeyAssign 3028
-#define wxStyledTextCtrl_CmdKeyClear 3029
-#define wxStyledTextCtrl_CmdKeyClearAll 3030
-#define wxStyledTextCtrl_SetStyleBytes 3031
-#define wxStyledTextCtrl_StyleSetVisible 3032
-#define wxStyledTextCtrl_GetCaretPeriod 3033
-#define wxStyledTextCtrl_SetCaretPeriod 3034
-#define wxStyledTextCtrl_SetWordChars 3035
-#define wxStyledTextCtrl_BeginUndoAction 3036
-#define wxStyledTextCtrl_EndUndoAction 3037
-#define wxStyledTextCtrl_IndicatorSetStyle 3038
-#define wxStyledTextCtrl_IndicatorGetStyle 3039
-#define wxStyledTextCtrl_IndicatorSetForeground 3040
-#define wxStyledTextCtrl_IndicatorGetForeground 3041
-#define wxStyledTextCtrl_SetWhitespaceForeground 3042
-#define wxStyledTextCtrl_SetWhitespaceBackground 3043
-#define wxStyledTextCtrl_GetStyleBits 3044
-#define wxStyledTextCtrl_SetLineState 3045
-#define wxStyledTextCtrl_GetLineState 3046
-#define wxStyledTextCtrl_GetMaxLineState 3047
-#define wxStyledTextCtrl_GetCaretLineVisible 3048
-#define wxStyledTextCtrl_SetCaretLineVisible 3049
-#define wxStyledTextCtrl_GetCaretLineBackground 3050
-#define wxStyledTextCtrl_SetCaretLineBackground 3051
-#define wxStyledTextCtrl_AutoCompShow 3052
-#define wxStyledTextCtrl_AutoCompCancel 3053
-#define wxStyledTextCtrl_AutoCompActive 3054
-#define wxStyledTextCtrl_AutoCompPosStart 3055
-#define wxStyledTextCtrl_AutoCompComplete 3056
-#define wxStyledTextCtrl_AutoCompStops 3057
-#define wxStyledTextCtrl_AutoCompSetSeparator 3058
-#define wxStyledTextCtrl_AutoCompGetSeparator 3059
-#define wxStyledTextCtrl_AutoCompSelect 3060
-#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3061
-#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3062
-#define wxStyledTextCtrl_AutoCompSetFillUps 3063
-#define wxStyledTextCtrl_AutoCompSetChooseSingle 3064
-#define wxStyledTextCtrl_AutoCompGetChooseSingle 3065
-#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3066
-#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3067
-#define wxStyledTextCtrl_UserListShow 3068
-#define wxStyledTextCtrl_AutoCompSetAutoHide 3069
-#define wxStyledTextCtrl_AutoCompGetAutoHide 3070
-#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3071
-#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3072
-#define wxStyledTextCtrl_RegisterImage 3073
-#define wxStyledTextCtrl_ClearRegisteredImages 3074
-#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3075
-#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3076
-#define wxStyledTextCtrl_AutoCompSetMaxWidth 3077
-#define wxStyledTextCtrl_AutoCompGetMaxWidth 3078
-#define wxStyledTextCtrl_AutoCompSetMaxHeight 3079
-#define wxStyledTextCtrl_AutoCompGetMaxHeight 3080
-#define wxStyledTextCtrl_SetIndent 3081
-#define wxStyledTextCtrl_GetIndent 3082
-#define wxStyledTextCtrl_SetUseTabs 3083
-#define wxStyledTextCtrl_GetUseTabs 3084
-#define wxStyledTextCtrl_SetLineIndentation 3085
-#define wxStyledTextCtrl_GetLineIndentation 3086
-#define wxStyledTextCtrl_GetLineIndentPosition 3087
-#define wxStyledTextCtrl_GetColumn 3088
-#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3089
-#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3090
-#define wxStyledTextCtrl_SetIndentationGuides 3091
-#define wxStyledTextCtrl_GetIndentationGuides 3092
-#define wxStyledTextCtrl_SetHighlightGuide 3093
-#define wxStyledTextCtrl_GetHighlightGuide 3094
-#define wxStyledTextCtrl_GetLineEndPosition 3095
-#define wxStyledTextCtrl_GetCodePage 3096
-#define wxStyledTextCtrl_GetCaretForeground 3097
-#define wxStyledTextCtrl_GetReadOnly 3098
-#define wxStyledTextCtrl_SetCurrentPos 3099
-#define wxStyledTextCtrl_SetSelectionStart 3100
-#define wxStyledTextCtrl_GetSelectionStart 3101
-#define wxStyledTextCtrl_SetSelectionEnd 3102
-#define wxStyledTextCtrl_GetSelectionEnd 3103
-#define wxStyledTextCtrl_SetPrintMagnification 3104
-#define wxStyledTextCtrl_GetPrintMagnification 3105
-#define wxStyledTextCtrl_SetPrintColourMode 3106
-#define wxStyledTextCtrl_GetPrintColourMode 3107
-#define wxStyledTextCtrl_FindText 3108
-#define wxStyledTextCtrl_FormatRange 3109
-#define wxStyledTextCtrl_GetFirstVisibleLine 3110
-#define wxStyledTextCtrl_GetLine 3111
-#define wxStyledTextCtrl_GetLineCount 3112
-#define wxStyledTextCtrl_SetMarginLeft 3113
-#define wxStyledTextCtrl_GetMarginLeft 3114
-#define wxStyledTextCtrl_SetMarginRight 3115
-#define wxStyledTextCtrl_GetMarginRight 3116
-#define wxStyledTextCtrl_GetModify 3117
-#define wxStyledTextCtrl_SetSelection 3118
-#define wxStyledTextCtrl_GetSelectedText 3119
-#define wxStyledTextCtrl_GetTextRange 3120
-#define wxStyledTextCtrl_HideSelection 3121
-#define wxStyledTextCtrl_LineFromPosition 3122
-#define wxStyledTextCtrl_PositionFromLine 3123
-#define wxStyledTextCtrl_LineScroll 3124
-#define wxStyledTextCtrl_EnsureCaretVisible 3125
-#define wxStyledTextCtrl_ReplaceSelection 3126
-#define wxStyledTextCtrl_SetReadOnly 3127
-#define wxStyledTextCtrl_CanPaste 3128
-#define wxStyledTextCtrl_CanUndo 3129
-#define wxStyledTextCtrl_EmptyUndoBuffer 3130
-#define wxStyledTextCtrl_Undo 3131
-#define wxStyledTextCtrl_Cut 3132
-#define wxStyledTextCtrl_Copy 3133
-#define wxStyledTextCtrl_Paste 3134
-#define wxStyledTextCtrl_Clear 3135
-#define wxStyledTextCtrl_SetText 3136
-#define wxStyledTextCtrl_GetText 3137
-#define wxStyledTextCtrl_GetTextLength 3138
-#define wxStyledTextCtrl_GetOvertype 3139
-#define wxStyledTextCtrl_SetCaretWidth 3140
-#define wxStyledTextCtrl_GetCaretWidth 3141
-#define wxStyledTextCtrl_SetTargetStart 3142
-#define wxStyledTextCtrl_GetTargetStart 3143
-#define wxStyledTextCtrl_SetTargetEnd 3144
-#define wxStyledTextCtrl_GetTargetEnd 3145
-#define wxStyledTextCtrl_ReplaceTarget 3146
-#define wxStyledTextCtrl_SearchInTarget 3147
-#define wxStyledTextCtrl_SetSearchFlags 3148
-#define wxStyledTextCtrl_GetSearchFlags 3149
-#define wxStyledTextCtrl_CallTipShow 3150
-#define wxStyledTextCtrl_CallTipCancel 3151
-#define wxStyledTextCtrl_CallTipActive 3152
-#define wxStyledTextCtrl_CallTipPosAtStart 3153
-#define wxStyledTextCtrl_CallTipSetHighlight 3154
-#define wxStyledTextCtrl_CallTipSetBackground 3155
-#define wxStyledTextCtrl_CallTipSetForeground 3156
-#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3157
-#define wxStyledTextCtrl_CallTipUseStyle 3158
-#define wxStyledTextCtrl_VisibleFromDocLine 3159
-#define wxStyledTextCtrl_DocLineFromVisible 3160
-#define wxStyledTextCtrl_WrapCount 3161
-#define wxStyledTextCtrl_SetFoldLevel 3162
-#define wxStyledTextCtrl_GetFoldLevel 3163
-#define wxStyledTextCtrl_GetLastChild 3164
-#define wxStyledTextCtrl_GetFoldParent 3165
-#define wxStyledTextCtrl_ShowLines 3166
-#define wxStyledTextCtrl_HideLines 3167
-#define wxStyledTextCtrl_GetLineVisible 3168
-#define wxStyledTextCtrl_SetFoldExpanded 3169
-#define wxStyledTextCtrl_GetFoldExpanded 3170
-#define wxStyledTextCtrl_ToggleFold 3171
-#define wxStyledTextCtrl_EnsureVisible 3172
-#define wxStyledTextCtrl_SetFoldFlags 3173
-#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3174
-#define wxStyledTextCtrl_SetTabIndents 3175
-#define wxStyledTextCtrl_GetTabIndents 3176
-#define wxStyledTextCtrl_SetBackSpaceUnIndents 3177
-#define wxStyledTextCtrl_GetBackSpaceUnIndents 3178
-#define wxStyledTextCtrl_SetMouseDwellTime 3179
-#define wxStyledTextCtrl_GetMouseDwellTime 3180
-#define wxStyledTextCtrl_WordStartPosition 3181
-#define wxStyledTextCtrl_WordEndPosition 3182
-#define wxStyledTextCtrl_SetWrapMode 3183
-#define wxStyledTextCtrl_GetWrapMode 3184
-#define wxStyledTextCtrl_SetWrapVisualFlags 3185
-#define wxStyledTextCtrl_GetWrapVisualFlags 3186
-#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3187
-#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3188
-#define wxStyledTextCtrl_SetWrapStartIndent 3189
-#define wxStyledTextCtrl_GetWrapStartIndent 3190
-#define wxStyledTextCtrl_SetLayoutCache 3191
-#define wxStyledTextCtrl_GetLayoutCache 3192
-#define wxStyledTextCtrl_SetScrollWidth 3193
-#define wxStyledTextCtrl_GetScrollWidth 3194
-#define wxStyledTextCtrl_TextWidth 3195
-#define wxStyledTextCtrl_GetEndAtLastLine 3196
-#define wxStyledTextCtrl_TextHeight 3197
-#define wxStyledTextCtrl_SetUseVerticalScrollBar 3198
-#define wxStyledTextCtrl_GetUseVerticalScrollBar 3199
-#define wxStyledTextCtrl_AppendText 3200
-#define wxStyledTextCtrl_GetTwoPhaseDraw 3201
-#define wxStyledTextCtrl_SetTwoPhaseDraw 3202
-#define wxStyledTextCtrl_TargetFromSelection 3203
-#define wxStyledTextCtrl_LinesJoin 3204
-#define wxStyledTextCtrl_LinesSplit 3205
-#define wxStyledTextCtrl_SetFoldMarginColour 3206
-#define wxStyledTextCtrl_SetFoldMarginHiColour 3207
-#define wxStyledTextCtrl_LineDown 3208
-#define wxStyledTextCtrl_LineDownExtend 3209
-#define wxStyledTextCtrl_LineUp 3210
-#define wxStyledTextCtrl_LineUpExtend 3211
-#define wxStyledTextCtrl_CharLeft 3212
-#define wxStyledTextCtrl_CharLeftExtend 3213
-#define wxStyledTextCtrl_CharRight 3214
-#define wxStyledTextCtrl_CharRightExtend 3215
-#define wxStyledTextCtrl_WordLeft 3216
-#define wxStyledTextCtrl_WordLeftExtend 3217
-#define wxStyledTextCtrl_WordRight 3218
-#define wxStyledTextCtrl_WordRightExtend 3219
-#define wxStyledTextCtrl_Home 3220
-#define wxStyledTextCtrl_HomeExtend 3221
-#define wxStyledTextCtrl_LineEnd 3222
-#define wxStyledTextCtrl_LineEndExtend 3223
-#define wxStyledTextCtrl_DocumentStart 3224
-#define wxStyledTextCtrl_DocumentStartExtend 3225
-#define wxStyledTextCtrl_DocumentEnd 3226
-#define wxStyledTextCtrl_DocumentEndExtend 3227
-#define wxStyledTextCtrl_PageUp 3228
-#define wxStyledTextCtrl_PageUpExtend 3229
-#define wxStyledTextCtrl_PageDown 3230
-#define wxStyledTextCtrl_PageDownExtend 3231
-#define wxStyledTextCtrl_EditToggleOvertype 3232
-#define wxStyledTextCtrl_Cancel 3233
-#define wxStyledTextCtrl_DeleteBack 3234
-#define wxStyledTextCtrl_Tab 3235
-#define wxStyledTextCtrl_BackTab 3236
-#define wxStyledTextCtrl_NewLine 3237
-#define wxStyledTextCtrl_FormFeed 3238
-#define wxStyledTextCtrl_VCHome 3239
-#define wxStyledTextCtrl_VCHomeExtend 3240
-#define wxStyledTextCtrl_ZoomIn 3241
-#define wxStyledTextCtrl_ZoomOut 3242
-#define wxStyledTextCtrl_DelWordLeft 3243
-#define wxStyledTextCtrl_DelWordRight 3244
-#define wxStyledTextCtrl_LineCut 3245
-#define wxStyledTextCtrl_LineDelete 3246
-#define wxStyledTextCtrl_LineTranspose 3247
-#define wxStyledTextCtrl_LineDuplicate 3248
-#define wxStyledTextCtrl_LowerCase 3249
-#define wxStyledTextCtrl_UpperCase 3250
-#define wxStyledTextCtrl_LineScrollDown 3251
-#define wxStyledTextCtrl_LineScrollUp 3252
-#define wxStyledTextCtrl_DeleteBackNotLine 3253
-#define wxStyledTextCtrl_HomeDisplay 3254
-#define wxStyledTextCtrl_HomeDisplayExtend 3255
-#define wxStyledTextCtrl_LineEndDisplay 3256
-#define wxStyledTextCtrl_LineEndDisplayExtend 3257
-#define wxStyledTextCtrl_HomeWrapExtend 3258
-#define wxStyledTextCtrl_LineEndWrap 3259
-#define wxStyledTextCtrl_LineEndWrapExtend 3260
-#define wxStyledTextCtrl_VCHomeWrap 3261
-#define wxStyledTextCtrl_VCHomeWrapExtend 3262
-#define wxStyledTextCtrl_LineCopy 3263
-#define wxStyledTextCtrl_MoveCaretInsideView 3264
-#define wxStyledTextCtrl_LineLength 3265
-#define wxStyledTextCtrl_BraceHighlight 3266
-#define wxStyledTextCtrl_BraceBadLight 3267
-#define wxStyledTextCtrl_BraceMatch 3268
-#define wxStyledTextCtrl_GetViewEOL 3269
-#define wxStyledTextCtrl_SetViewEOL 3270
-#define wxStyledTextCtrl_SetModEventMask 3271
-#define wxStyledTextCtrl_GetEdgeColumn 3272
-#define wxStyledTextCtrl_SetEdgeColumn 3273
-#define wxStyledTextCtrl_SetEdgeMode 3274
-#define wxStyledTextCtrl_GetEdgeMode 3275
-#define wxStyledTextCtrl_GetEdgeColour 3276
-#define wxStyledTextCtrl_SetEdgeColour 3277
-#define wxStyledTextCtrl_SearchAnchor 3278
-#define wxStyledTextCtrl_SearchNext 3279
-#define wxStyledTextCtrl_SearchPrev 3280
-#define wxStyledTextCtrl_LinesOnScreen 3281
-#define wxStyledTextCtrl_UsePopUp 3282
-#define wxStyledTextCtrl_SelectionIsRectangle 3283
-#define wxStyledTextCtrl_SetZoom 3284
-#define wxStyledTextCtrl_GetZoom 3285
-#define wxStyledTextCtrl_GetModEventMask 3286
-#define wxStyledTextCtrl_SetSTCFocus 3287
-#define wxStyledTextCtrl_GetSTCFocus 3288
-#define wxStyledTextCtrl_SetStatus 3289
-#define wxStyledTextCtrl_GetStatus 3290
-#define wxStyledTextCtrl_SetMouseDownCaptures 3291
-#define wxStyledTextCtrl_GetMouseDownCaptures 3292
-#define wxStyledTextCtrl_SetSTCCursor 3293
-#define wxStyledTextCtrl_GetSTCCursor 3294
-#define wxStyledTextCtrl_SetControlCharSymbol 3295
-#define wxStyledTextCtrl_GetControlCharSymbol 3296
-#define wxStyledTextCtrl_WordPartLeft 3297
-#define wxStyledTextCtrl_WordPartLeftExtend 3298
-#define wxStyledTextCtrl_WordPartRight 3299
-#define wxStyledTextCtrl_WordPartRightExtend 3300
-#define wxStyledTextCtrl_SetVisiblePolicy 3301
-#define wxStyledTextCtrl_DelLineLeft 3302
-#define wxStyledTextCtrl_DelLineRight 3303
-#define wxStyledTextCtrl_GetXOffset 3304
-#define wxStyledTextCtrl_ChooseCaretX 3305
-#define wxStyledTextCtrl_SetXCaretPolicy 3306
-#define wxStyledTextCtrl_SetYCaretPolicy 3307
-#define wxStyledTextCtrl_GetPrintWrapMode 3308
-#define wxStyledTextCtrl_SetHotspotActiveForeground 3309
-#define wxStyledTextCtrl_SetHotspotActiveBackground 3310
-#define wxStyledTextCtrl_SetHotspotActiveUnderline 3311
-#define wxStyledTextCtrl_SetHotspotSingleLine 3312
-#define wxStyledTextCtrl_ParaDownExtend 3313
-#define wxStyledTextCtrl_ParaUp 3314
-#define wxStyledTextCtrl_ParaUpExtend 3315
-#define wxStyledTextCtrl_PositionBefore 3316
-#define wxStyledTextCtrl_PositionAfter 3317
-#define wxStyledTextCtrl_CopyRange 3318
-#define wxStyledTextCtrl_CopyText 3319
-#define wxStyledTextCtrl_SetSelectionMode 3320
-#define wxStyledTextCtrl_GetSelectionMode 3321
-#define wxStyledTextCtrl_LineDownRectExtend 3322
-#define wxStyledTextCtrl_LineUpRectExtend 3323
-#define wxStyledTextCtrl_CharLeftRectExtend 3324
-#define wxStyledTextCtrl_CharRightRectExtend 3325
-#define wxStyledTextCtrl_HomeRectExtend 3326
-#define wxStyledTextCtrl_VCHomeRectExtend 3327
-#define wxStyledTextCtrl_LineEndRectExtend 3328
-#define wxStyledTextCtrl_PageUpRectExtend 3329
-#define wxStyledTextCtrl_PageDownRectExtend 3330
-#define wxStyledTextCtrl_StutteredPageUp 3331
-#define wxStyledTextCtrl_StutteredPageUpExtend 3332
-#define wxStyledTextCtrl_StutteredPageDown 3333
-#define wxStyledTextCtrl_StutteredPageDownExtend 3334
-#define wxStyledTextCtrl_WordLeftEnd 3335
-#define wxStyledTextCtrl_WordLeftEndExtend 3336
-#define wxStyledTextCtrl_WordRightEnd 3337
-#define wxStyledTextCtrl_WordRightEndExtend 3338
-#define wxStyledTextCtrl_SetWhitespaceChars 3339
-#define wxStyledTextCtrl_SetCharsDefault 3340
-#define wxStyledTextCtrl_AutoCompGetCurrent 3341
-#define wxStyledTextCtrl_Allocate 3342
-#define wxStyledTextCtrl_FindColumn 3343
-#define wxStyledTextCtrl_GetCaretSticky 3344
-#define wxStyledTextCtrl_SetCaretSticky 3345
-#define wxStyledTextCtrl_ToggleCaretSticky 3346
-#define wxStyledTextCtrl_SetPasteConvertEndings 3347
-#define wxStyledTextCtrl_GetPasteConvertEndings 3348
-#define wxStyledTextCtrl_SelectionDuplicate 3349
-#define wxStyledTextCtrl_SetCaretLineBackAlpha 3350
-#define wxStyledTextCtrl_GetCaretLineBackAlpha 3351
-#define wxStyledTextCtrl_StartRecord 3352
-#define wxStyledTextCtrl_StopRecord 3353
-#define wxStyledTextCtrl_SetLexer 3354
-#define wxStyledTextCtrl_GetLexer 3355
-#define wxStyledTextCtrl_Colourise 3356
-#define wxStyledTextCtrl_SetProperty 3357
-#define wxStyledTextCtrl_SetKeyWords 3358
-#define wxStyledTextCtrl_SetLexerLanguage 3359
-#define wxStyledTextCtrl_GetProperty 3360
-#define wxStyledTextCtrl_GetStyleBitsNeeded 3361
-#define wxStyledTextCtrl_GetCurrentLine 3362
-#define wxStyledTextCtrl_StyleSetSpec 3363
-#define wxStyledTextCtrl_StyleSetFont 3364
-#define wxStyledTextCtrl_StyleSetFontAttr 3365
-#define wxStyledTextCtrl_StyleSetCharacterSet 3366
-#define wxStyledTextCtrl_StyleSetFontEncoding 3367
-#define wxStyledTextCtrl_CmdKeyExecute 3368
-#define wxStyledTextCtrl_SetMargins 3369
-#define wxStyledTextCtrl_GetSelection 3370
-#define wxStyledTextCtrl_PointFromPosition 3371
-#define wxStyledTextCtrl_ScrollToLine 3372
-#define wxStyledTextCtrl_ScrollToColumn 3373
-#define wxStyledTextCtrl_SetVScrollBar 3374
-#define wxStyledTextCtrl_SetHScrollBar 3375
-#define wxStyledTextCtrl_GetLastKeydownProcessed 3376
-#define wxStyledTextCtrl_SetLastKeydownProcessed 3377
-#define wxStyledTextCtrl_SaveFile 3378
-#define wxStyledTextCtrl_LoadFile 3379
-#define wxStyledTextCtrl_DoDragOver 3380
-#define wxStyledTextCtrl_DoDropText 3381
-#define wxStyledTextCtrl_GetUseAntiAliasing 3382
-#define wxStyledTextCtrl_AddTextRaw 3383
-#define wxStyledTextCtrl_InsertTextRaw 3384
-#define wxStyledTextCtrl_GetCurLineRaw 3385
-#define wxStyledTextCtrl_GetLineRaw 3386
-#define wxStyledTextCtrl_GetSelectedTextRaw 3387
-#define wxStyledTextCtrl_GetTextRangeRaw 3388
-#define wxStyledTextCtrl_SetTextRaw 3389
-#define wxStyledTextCtrl_GetTextRaw 3390
-#define wxStyledTextCtrl_AppendTextRaw 3391
-#define wxArtProvider_GetBitmap 3392
-#define wxArtProvider_GetIcon 3393
-#define wxTreeEvent_GetKeyCode 3394
-#define wxTreeEvent_GetItem 3395
-#define wxTreeEvent_GetKeyEvent 3396
-#define wxTreeEvent_GetLabel 3397
-#define wxTreeEvent_GetOldItem 3398
-#define wxTreeEvent_GetPoint 3399
-#define wxTreeEvent_IsEditCancelled 3400
-#define wxTreeEvent_SetToolTip 3401
-#define wxNotebookEvent_GetOldSelection 3402
-#define wxNotebookEvent_GetSelection 3403
-#define wxNotebookEvent_SetOldSelection 3404
-#define wxNotebookEvent_SetSelection 3405
-#define wxFileDataObject_new 3406
-#define wxFileDataObject_AddFile 3407
-#define wxFileDataObject_GetFilenames 3408
-#define wxFileDataObject_destroy 3409
-#define wxTextDataObject_new 3410
-#define wxTextDataObject_GetTextLength 3411
-#define wxTextDataObject_GetText 3412
-#define wxTextDataObject_SetText 3413
-#define wxTextDataObject_destroy 3414
-#define wxBitmapDataObject_new_1_1 3415
-#define wxBitmapDataObject_new_1_0 3416
-#define wxBitmapDataObject_GetBitmap 3417
-#define wxBitmapDataObject_SetBitmap 3418
-#define wxBitmapDataObject_destroy 3419
-#define wxClipboard_new 3421
-#define wxClipboard_destruct 3422
-#define wxClipboard_AddData 3423
-#define wxClipboard_Clear 3424
-#define wxClipboard_Close 3425
-#define wxClipboard_Flush 3426
-#define wxClipboard_GetData 3427
-#define wxClipboard_IsOpened 3428
-#define wxClipboard_Open 3429
-#define wxClipboard_SetData 3430
-#define wxClipboard_UsePrimarySelection 3432
-#define wxClipboard_IsSupported 3433
-#define wxClipboard_Get 3434
-#define wxSpinEvent_GetPosition 3435
-#define wxSpinEvent_SetPosition 3436
-#define wxSplitterWindow_new_0 3437
-#define wxSplitterWindow_new_2 3438
-#define wxSplitterWindow_destruct 3439
-#define wxSplitterWindow_Create 3440
-#define wxSplitterWindow_GetMinimumPaneSize 3441
-#define wxSplitterWindow_GetSashGravity 3442
-#define wxSplitterWindow_GetSashPosition 3443
-#define wxSplitterWindow_GetSplitMode 3444
-#define wxSplitterWindow_GetWindow1 3445
-#define wxSplitterWindow_GetWindow2 3446
-#define wxSplitterWindow_Initialize 3447
-#define wxSplitterWindow_IsSplit 3448
-#define wxSplitterWindow_ReplaceWindow 3449
-#define wxSplitterWindow_SetSashGravity 3450
-#define wxSplitterWindow_SetSashPosition 3451
-#define wxSplitterWindow_SetSashSize 3452
-#define wxSplitterWindow_SetMinimumPaneSize 3453
-#define wxSplitterWindow_SetSplitMode 3454
-#define wxSplitterWindow_SplitHorizontally 3455
-#define wxSplitterWindow_SplitVertically 3456
-#define wxSplitterWindow_Unsplit 3457
-#define wxSplitterWindow_UpdateSize 3458
-#define wxSplitterEvent_GetSashPosition 3459
-#define wxSplitterEvent_GetX 3460
-#define wxSplitterEvent_GetY 3461
-#define wxSplitterEvent_GetWindowBeingRemoved 3462
-#define wxSplitterEvent_SetSashPosition 3463
-#define wxHtmlWindow_new_0 3464
-#define wxHtmlWindow_new_2 3465
-#define wxHtmlWindow_AppendToPage 3466
-#define wxHtmlWindow_GetOpenedAnchor 3467
-#define wxHtmlWindow_GetOpenedPage 3468
-#define wxHtmlWindow_GetOpenedPageTitle 3469
-#define wxHtmlWindow_GetRelatedFrame 3470
-#define wxHtmlWindow_HistoryBack 3471
-#define wxHtmlWindow_HistoryCanBack 3472
-#define wxHtmlWindow_HistoryCanForward 3473
-#define wxHtmlWindow_HistoryClear 3474
-#define wxHtmlWindow_HistoryForward 3475
-#define wxHtmlWindow_LoadFile 3476
-#define wxHtmlWindow_LoadPage 3477
-#define wxHtmlWindow_SelectAll 3478
-#define wxHtmlWindow_SelectionToText 3479
-#define wxHtmlWindow_SelectLine 3480
-#define wxHtmlWindow_SelectWord 3481
-#define wxHtmlWindow_SetBorders 3482
-#define wxHtmlWindow_SetFonts 3483
-#define wxHtmlWindow_SetPage 3484
-#define wxHtmlWindow_SetRelatedFrame 3485
-#define wxHtmlWindow_SetRelatedStatusBar 3486
-#define wxHtmlWindow_ToText 3487
-#define wxHtmlWindow_destroy 3488
-#define wxHtmlLinkEvent_GetLinkInfo 3489
-#define wxSystemSettings_GetColour 3490
-#define wxSystemSettings_GetFont 3491
-#define wxSystemSettings_GetMetric 3492
-#define wxSystemSettings_GetScreenType 3493
-#define wxSystemOptions_GetOption 3494
-#define wxSystemOptions_GetOptionInt 3495
-#define wxSystemOptions_HasOption 3496
-#define wxSystemOptions_IsFalse 3497
-#define wxSystemOptions_SetOption_2_1 3498
-#define wxSystemOptions_SetOption_2_0 3499
-#define wxAuiNotebookEvent_SetSelection 3500
-#define wxAuiNotebookEvent_GetSelection 3501
-#define wxAuiNotebookEvent_SetOldSelection 3502
-#define wxAuiNotebookEvent_GetOldSelection 3503
-#define wxAuiNotebookEvent_SetDragSource 3504
-#define wxAuiNotebookEvent_GetDragSource 3505
-#define wxAuiManagerEvent_SetManager 3506
-#define wxAuiManagerEvent_GetManager 3507
-#define wxAuiManagerEvent_SetPane 3508
-#define wxAuiManagerEvent_GetPane 3509
-#define wxAuiManagerEvent_SetButton 3510
-#define wxAuiManagerEvent_GetButton 3511
-#define wxAuiManagerEvent_SetDC 3512
-#define wxAuiManagerEvent_GetDC 3513
-#define wxAuiManagerEvent_Veto 3514
-#define wxAuiManagerEvent_GetVeto 3515
-#define wxAuiManagerEvent_SetCanVeto 3516
-#define wxAuiManagerEvent_CanVeto 3517
-#define wxLogNull_new 3518
-#define wxLogNull_destroy 3519
-#define wxTaskBarIcon_new 3520
-#define wxTaskBarIcon_destruct 3521
-#define wxTaskBarIcon_PopupMenu 3522
-#define wxTaskBarIcon_RemoveIcon 3523
-#define wxTaskBarIcon_SetIcon 3524
+#define gdicmn_wxDisplaySize 2927
+#define gdicmn_wxSetCursor 2928
+#define wxPrintout_new 2929
+#define wxPrintout_destruct 2930
+#define wxPrintout_GetDC 2931
+#define wxPrintout_GetPageSizeMM 2932
+#define wxPrintout_GetPageSizePixels 2933
+#define wxPrintout_GetPaperRectPixels 2934
+#define wxPrintout_GetPPIPrinter 2935
+#define wxPrintout_GetPPIScreen 2936
+#define wxPrintout_GetTitle 2937
+#define wxPrintout_IsPreview 2938
+#define wxPrintout_FitThisSizeToPaper 2939
+#define wxPrintout_FitThisSizeToPage 2940
+#define wxPrintout_FitThisSizeToPageMargins 2941
+#define wxPrintout_MapScreenSizeToPaper 2942
+#define wxPrintout_MapScreenSizeToPage 2943
+#define wxPrintout_MapScreenSizeToPageMargins 2944
+#define wxPrintout_MapScreenSizeToDevice 2945
+#define wxPrintout_GetLogicalPaperRect 2946
+#define wxPrintout_GetLogicalPageRect 2947
+#define wxPrintout_GetLogicalPageMarginsRect 2948
+#define wxPrintout_SetLogicalOrigin 2949
+#define wxPrintout_OffsetLogicalOrigin 2950
+#define wxStyledTextCtrl_new_2 2951
+#define wxStyledTextCtrl_new_0 2952
+#define wxStyledTextCtrl_destruct 2953
+#define wxStyledTextCtrl_Create 2954
+#define wxStyledTextCtrl_AddText 2955
+#define wxStyledTextCtrl_AddStyledText 2956
+#define wxStyledTextCtrl_InsertText 2957
+#define wxStyledTextCtrl_ClearAll 2958
+#define wxStyledTextCtrl_ClearDocumentStyle 2959
+#define wxStyledTextCtrl_GetLength 2960
+#define wxStyledTextCtrl_GetCharAt 2961
+#define wxStyledTextCtrl_GetCurrentPos 2962
+#define wxStyledTextCtrl_GetAnchor 2963
+#define wxStyledTextCtrl_GetStyleAt 2964
+#define wxStyledTextCtrl_Redo 2965
+#define wxStyledTextCtrl_SetUndoCollection 2966
+#define wxStyledTextCtrl_SelectAll 2967
+#define wxStyledTextCtrl_SetSavePoint 2968
+#define wxStyledTextCtrl_GetStyledText 2969
+#define wxStyledTextCtrl_CanRedo 2970
+#define wxStyledTextCtrl_MarkerLineFromHandle 2971
+#define wxStyledTextCtrl_MarkerDeleteHandle 2972
+#define wxStyledTextCtrl_GetUndoCollection 2973
+#define wxStyledTextCtrl_GetViewWhiteSpace 2974
+#define wxStyledTextCtrl_SetViewWhiteSpace 2975
+#define wxStyledTextCtrl_PositionFromPoint 2976
+#define wxStyledTextCtrl_PositionFromPointClose 2977
+#define wxStyledTextCtrl_GotoLine 2978
+#define wxStyledTextCtrl_GotoPos 2979
+#define wxStyledTextCtrl_SetAnchor 2980
+#define wxStyledTextCtrl_GetCurLine 2981
+#define wxStyledTextCtrl_GetEndStyled 2982
+#define wxStyledTextCtrl_ConvertEOLs 2983
+#define wxStyledTextCtrl_GetEOLMode 2984
+#define wxStyledTextCtrl_SetEOLMode 2985
+#define wxStyledTextCtrl_StartStyling 2986
+#define wxStyledTextCtrl_SetStyling 2987
+#define wxStyledTextCtrl_GetBufferedDraw 2988
+#define wxStyledTextCtrl_SetBufferedDraw 2989
+#define wxStyledTextCtrl_SetTabWidth 2990
+#define wxStyledTextCtrl_GetTabWidth 2991
+#define wxStyledTextCtrl_SetCodePage 2992
+#define wxStyledTextCtrl_MarkerDefine 2993
+#define wxStyledTextCtrl_MarkerSetForeground 2994
+#define wxStyledTextCtrl_MarkerSetBackground 2995
+#define wxStyledTextCtrl_MarkerAdd 2996
+#define wxStyledTextCtrl_MarkerDelete 2997
+#define wxStyledTextCtrl_MarkerDeleteAll 2998
+#define wxStyledTextCtrl_MarkerGet 2999
+#define wxStyledTextCtrl_MarkerNext 3000
+#define wxStyledTextCtrl_MarkerPrevious 3001
+#define wxStyledTextCtrl_MarkerDefineBitmap 3002
+#define wxStyledTextCtrl_MarkerAddSet 3003
+#define wxStyledTextCtrl_MarkerSetAlpha 3004
+#define wxStyledTextCtrl_SetMarginType 3005
+#define wxStyledTextCtrl_GetMarginType 3006
+#define wxStyledTextCtrl_SetMarginWidth 3007
+#define wxStyledTextCtrl_GetMarginWidth 3008
+#define wxStyledTextCtrl_SetMarginMask 3009
+#define wxStyledTextCtrl_GetMarginMask 3010
+#define wxStyledTextCtrl_SetMarginSensitive 3011
+#define wxStyledTextCtrl_GetMarginSensitive 3012
+#define wxStyledTextCtrl_StyleClearAll 3013
+#define wxStyledTextCtrl_StyleSetForeground 3014
+#define wxStyledTextCtrl_StyleSetBackground 3015
+#define wxStyledTextCtrl_StyleSetBold 3016
+#define wxStyledTextCtrl_StyleSetItalic 3017
+#define wxStyledTextCtrl_StyleSetSize 3018
+#define wxStyledTextCtrl_StyleSetFaceName 3019
+#define wxStyledTextCtrl_StyleSetEOLFilled 3020
+#define wxStyledTextCtrl_StyleResetDefault 3021
+#define wxStyledTextCtrl_StyleSetUnderline 3022
+#define wxStyledTextCtrl_StyleSetCase 3023
+#define wxStyledTextCtrl_StyleSetHotSpot 3024
+#define wxStyledTextCtrl_SetSelForeground 3025
+#define wxStyledTextCtrl_SetSelBackground 3026
+#define wxStyledTextCtrl_GetSelAlpha 3027
+#define wxStyledTextCtrl_SetSelAlpha 3028
+#define wxStyledTextCtrl_SetCaretForeground 3029
+#define wxStyledTextCtrl_CmdKeyAssign 3030
+#define wxStyledTextCtrl_CmdKeyClear 3031
+#define wxStyledTextCtrl_CmdKeyClearAll 3032
+#define wxStyledTextCtrl_SetStyleBytes 3033
+#define wxStyledTextCtrl_StyleSetVisible 3034
+#define wxStyledTextCtrl_GetCaretPeriod 3035
+#define wxStyledTextCtrl_SetCaretPeriod 3036
+#define wxStyledTextCtrl_SetWordChars 3037
+#define wxStyledTextCtrl_BeginUndoAction 3038
+#define wxStyledTextCtrl_EndUndoAction 3039
+#define wxStyledTextCtrl_IndicatorSetStyle 3040
+#define wxStyledTextCtrl_IndicatorGetStyle 3041
+#define wxStyledTextCtrl_IndicatorSetForeground 3042
+#define wxStyledTextCtrl_IndicatorGetForeground 3043
+#define wxStyledTextCtrl_SetWhitespaceForeground 3044
+#define wxStyledTextCtrl_SetWhitespaceBackground 3045
+#define wxStyledTextCtrl_GetStyleBits 3046
+#define wxStyledTextCtrl_SetLineState 3047
+#define wxStyledTextCtrl_GetLineState 3048
+#define wxStyledTextCtrl_GetMaxLineState 3049
+#define wxStyledTextCtrl_GetCaretLineVisible 3050
+#define wxStyledTextCtrl_SetCaretLineVisible 3051
+#define wxStyledTextCtrl_GetCaretLineBackground 3052
+#define wxStyledTextCtrl_SetCaretLineBackground 3053
+#define wxStyledTextCtrl_AutoCompShow 3054
+#define wxStyledTextCtrl_AutoCompCancel 3055
+#define wxStyledTextCtrl_AutoCompActive 3056
+#define wxStyledTextCtrl_AutoCompPosStart 3057
+#define wxStyledTextCtrl_AutoCompComplete 3058
+#define wxStyledTextCtrl_AutoCompStops 3059
+#define wxStyledTextCtrl_AutoCompSetSeparator 3060
+#define wxStyledTextCtrl_AutoCompGetSeparator 3061
+#define wxStyledTextCtrl_AutoCompSelect 3062
+#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3063
+#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3064
+#define wxStyledTextCtrl_AutoCompSetFillUps 3065
+#define wxStyledTextCtrl_AutoCompSetChooseSingle 3066
+#define wxStyledTextCtrl_AutoCompGetChooseSingle 3067
+#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3068
+#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3069
+#define wxStyledTextCtrl_UserListShow 3070
+#define wxStyledTextCtrl_AutoCompSetAutoHide 3071
+#define wxStyledTextCtrl_AutoCompGetAutoHide 3072
+#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3073
+#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3074
+#define wxStyledTextCtrl_RegisterImage 3075
+#define wxStyledTextCtrl_ClearRegisteredImages 3076
+#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3077
+#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3078
+#define wxStyledTextCtrl_AutoCompSetMaxWidth 3079
+#define wxStyledTextCtrl_AutoCompGetMaxWidth 3080
+#define wxStyledTextCtrl_AutoCompSetMaxHeight 3081
+#define wxStyledTextCtrl_AutoCompGetMaxHeight 3082
+#define wxStyledTextCtrl_SetIndent 3083
+#define wxStyledTextCtrl_GetIndent 3084
+#define wxStyledTextCtrl_SetUseTabs 3085
+#define wxStyledTextCtrl_GetUseTabs 3086
+#define wxStyledTextCtrl_SetLineIndentation 3087
+#define wxStyledTextCtrl_GetLineIndentation 3088
+#define wxStyledTextCtrl_GetLineIndentPosition 3089
+#define wxStyledTextCtrl_GetColumn 3090
+#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3091
+#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3092
+#define wxStyledTextCtrl_SetIndentationGuides 3093
+#define wxStyledTextCtrl_GetIndentationGuides 3094
+#define wxStyledTextCtrl_SetHighlightGuide 3095
+#define wxStyledTextCtrl_GetHighlightGuide 3096
+#define wxStyledTextCtrl_GetLineEndPosition 3097
+#define wxStyledTextCtrl_GetCodePage 3098
+#define wxStyledTextCtrl_GetCaretForeground 3099
+#define wxStyledTextCtrl_GetReadOnly 3100
+#define wxStyledTextCtrl_SetCurrentPos 3101
+#define wxStyledTextCtrl_SetSelectionStart 3102
+#define wxStyledTextCtrl_GetSelectionStart 3103
+#define wxStyledTextCtrl_SetSelectionEnd 3104
+#define wxStyledTextCtrl_GetSelectionEnd 3105
+#define wxStyledTextCtrl_SetPrintMagnification 3106
+#define wxStyledTextCtrl_GetPrintMagnification 3107
+#define wxStyledTextCtrl_SetPrintColourMode 3108
+#define wxStyledTextCtrl_GetPrintColourMode 3109
+#define wxStyledTextCtrl_FindText 3110
+#define wxStyledTextCtrl_FormatRange 3111
+#define wxStyledTextCtrl_GetFirstVisibleLine 3112
+#define wxStyledTextCtrl_GetLine 3113
+#define wxStyledTextCtrl_GetLineCount 3114
+#define wxStyledTextCtrl_SetMarginLeft 3115
+#define wxStyledTextCtrl_GetMarginLeft 3116
+#define wxStyledTextCtrl_SetMarginRight 3117
+#define wxStyledTextCtrl_GetMarginRight 3118
+#define wxStyledTextCtrl_GetModify 3119
+#define wxStyledTextCtrl_SetSelection 3120
+#define wxStyledTextCtrl_GetSelectedText 3121
+#define wxStyledTextCtrl_GetTextRange 3122
+#define wxStyledTextCtrl_HideSelection 3123
+#define wxStyledTextCtrl_LineFromPosition 3124
+#define wxStyledTextCtrl_PositionFromLine 3125
+#define wxStyledTextCtrl_LineScroll 3126
+#define wxStyledTextCtrl_EnsureCaretVisible 3127
+#define wxStyledTextCtrl_ReplaceSelection 3128
+#define wxStyledTextCtrl_SetReadOnly 3129
+#define wxStyledTextCtrl_CanPaste 3130
+#define wxStyledTextCtrl_CanUndo 3131
+#define wxStyledTextCtrl_EmptyUndoBuffer 3132
+#define wxStyledTextCtrl_Undo 3133
+#define wxStyledTextCtrl_Cut 3134
+#define wxStyledTextCtrl_Copy 3135
+#define wxStyledTextCtrl_Paste 3136
+#define wxStyledTextCtrl_Clear 3137
+#define wxStyledTextCtrl_SetText 3138
+#define wxStyledTextCtrl_GetText 3139
+#define wxStyledTextCtrl_GetTextLength 3140
+#define wxStyledTextCtrl_GetOvertype 3141
+#define wxStyledTextCtrl_SetCaretWidth 3142
+#define wxStyledTextCtrl_GetCaretWidth 3143
+#define wxStyledTextCtrl_SetTargetStart 3144
+#define wxStyledTextCtrl_GetTargetStart 3145
+#define wxStyledTextCtrl_SetTargetEnd 3146
+#define wxStyledTextCtrl_GetTargetEnd 3147
+#define wxStyledTextCtrl_ReplaceTarget 3148
+#define wxStyledTextCtrl_SearchInTarget 3149
+#define wxStyledTextCtrl_SetSearchFlags 3150
+#define wxStyledTextCtrl_GetSearchFlags 3151
+#define wxStyledTextCtrl_CallTipShow 3152
+#define wxStyledTextCtrl_CallTipCancel 3153
+#define wxStyledTextCtrl_CallTipActive 3154
+#define wxStyledTextCtrl_CallTipPosAtStart 3155
+#define wxStyledTextCtrl_CallTipSetHighlight 3156
+#define wxStyledTextCtrl_CallTipSetBackground 3157
+#define wxStyledTextCtrl_CallTipSetForeground 3158
+#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3159
+#define wxStyledTextCtrl_CallTipUseStyle 3160
+#define wxStyledTextCtrl_VisibleFromDocLine 3161
+#define wxStyledTextCtrl_DocLineFromVisible 3162
+#define wxStyledTextCtrl_WrapCount 3163
+#define wxStyledTextCtrl_SetFoldLevel 3164
+#define wxStyledTextCtrl_GetFoldLevel 3165
+#define wxStyledTextCtrl_GetLastChild 3166
+#define wxStyledTextCtrl_GetFoldParent 3167
+#define wxStyledTextCtrl_ShowLines 3168
+#define wxStyledTextCtrl_HideLines 3169
+#define wxStyledTextCtrl_GetLineVisible 3170
+#define wxStyledTextCtrl_SetFoldExpanded 3171
+#define wxStyledTextCtrl_GetFoldExpanded 3172
+#define wxStyledTextCtrl_ToggleFold 3173
+#define wxStyledTextCtrl_EnsureVisible 3174
+#define wxStyledTextCtrl_SetFoldFlags 3175
+#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3176
+#define wxStyledTextCtrl_SetTabIndents 3177
+#define wxStyledTextCtrl_GetTabIndents 3178
+#define wxStyledTextCtrl_SetBackSpaceUnIndents 3179
+#define wxStyledTextCtrl_GetBackSpaceUnIndents 3180
+#define wxStyledTextCtrl_SetMouseDwellTime 3181
+#define wxStyledTextCtrl_GetMouseDwellTime 3182
+#define wxStyledTextCtrl_WordStartPosition 3183
+#define wxStyledTextCtrl_WordEndPosition 3184
+#define wxStyledTextCtrl_SetWrapMode 3185
+#define wxStyledTextCtrl_GetWrapMode 3186
+#define wxStyledTextCtrl_SetWrapVisualFlags 3187
+#define wxStyledTextCtrl_GetWrapVisualFlags 3188
+#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3189
+#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3190
+#define wxStyledTextCtrl_SetWrapStartIndent 3191
+#define wxStyledTextCtrl_GetWrapStartIndent 3192
+#define wxStyledTextCtrl_SetLayoutCache 3193
+#define wxStyledTextCtrl_GetLayoutCache 3194
+#define wxStyledTextCtrl_SetScrollWidth 3195
+#define wxStyledTextCtrl_GetScrollWidth 3196
+#define wxStyledTextCtrl_TextWidth 3197
+#define wxStyledTextCtrl_GetEndAtLastLine 3198
+#define wxStyledTextCtrl_TextHeight 3199
+#define wxStyledTextCtrl_SetUseVerticalScrollBar 3200
+#define wxStyledTextCtrl_GetUseVerticalScrollBar 3201
+#define wxStyledTextCtrl_AppendText 3202
+#define wxStyledTextCtrl_GetTwoPhaseDraw 3203
+#define wxStyledTextCtrl_SetTwoPhaseDraw 3204
+#define wxStyledTextCtrl_TargetFromSelection 3205
+#define wxStyledTextCtrl_LinesJoin 3206
+#define wxStyledTextCtrl_LinesSplit 3207
+#define wxStyledTextCtrl_SetFoldMarginColour 3208
+#define wxStyledTextCtrl_SetFoldMarginHiColour 3209
+#define wxStyledTextCtrl_LineDown 3210
+#define wxStyledTextCtrl_LineDownExtend 3211
+#define wxStyledTextCtrl_LineUp 3212
+#define wxStyledTextCtrl_LineUpExtend 3213
+#define wxStyledTextCtrl_CharLeft 3214
+#define wxStyledTextCtrl_CharLeftExtend 3215
+#define wxStyledTextCtrl_CharRight 3216
+#define wxStyledTextCtrl_CharRightExtend 3217
+#define wxStyledTextCtrl_WordLeft 3218
+#define wxStyledTextCtrl_WordLeftExtend 3219
+#define wxStyledTextCtrl_WordRight 3220
+#define wxStyledTextCtrl_WordRightExtend 3221
+#define wxStyledTextCtrl_Home 3222
+#define wxStyledTextCtrl_HomeExtend 3223
+#define wxStyledTextCtrl_LineEnd 3224
+#define wxStyledTextCtrl_LineEndExtend 3225
+#define wxStyledTextCtrl_DocumentStart 3226
+#define wxStyledTextCtrl_DocumentStartExtend 3227
+#define wxStyledTextCtrl_DocumentEnd 3228
+#define wxStyledTextCtrl_DocumentEndExtend 3229
+#define wxStyledTextCtrl_PageUp 3230
+#define wxStyledTextCtrl_PageUpExtend 3231
+#define wxStyledTextCtrl_PageDown 3232
+#define wxStyledTextCtrl_PageDownExtend 3233
+#define wxStyledTextCtrl_EditToggleOvertype 3234
+#define wxStyledTextCtrl_Cancel 3235
+#define wxStyledTextCtrl_DeleteBack 3236
+#define wxStyledTextCtrl_Tab 3237
+#define wxStyledTextCtrl_BackTab 3238
+#define wxStyledTextCtrl_NewLine 3239
+#define wxStyledTextCtrl_FormFeed 3240
+#define wxStyledTextCtrl_VCHome 3241
+#define wxStyledTextCtrl_VCHomeExtend 3242
+#define wxStyledTextCtrl_ZoomIn 3243
+#define wxStyledTextCtrl_ZoomOut 3244
+#define wxStyledTextCtrl_DelWordLeft 3245
+#define wxStyledTextCtrl_DelWordRight 3246
+#define wxStyledTextCtrl_LineCut 3247
+#define wxStyledTextCtrl_LineDelete 3248
+#define wxStyledTextCtrl_LineTranspose 3249
+#define wxStyledTextCtrl_LineDuplicate 3250
+#define wxStyledTextCtrl_LowerCase 3251
+#define wxStyledTextCtrl_UpperCase 3252
+#define wxStyledTextCtrl_LineScrollDown 3253
+#define wxStyledTextCtrl_LineScrollUp 3254
+#define wxStyledTextCtrl_DeleteBackNotLine 3255
+#define wxStyledTextCtrl_HomeDisplay 3256
+#define wxStyledTextCtrl_HomeDisplayExtend 3257
+#define wxStyledTextCtrl_LineEndDisplay 3258
+#define wxStyledTextCtrl_LineEndDisplayExtend 3259
+#define wxStyledTextCtrl_HomeWrapExtend 3260
+#define wxStyledTextCtrl_LineEndWrap 3261
+#define wxStyledTextCtrl_LineEndWrapExtend 3262
+#define wxStyledTextCtrl_VCHomeWrap 3263
+#define wxStyledTextCtrl_VCHomeWrapExtend 3264
+#define wxStyledTextCtrl_LineCopy 3265
+#define wxStyledTextCtrl_MoveCaretInsideView 3266
+#define wxStyledTextCtrl_LineLength 3267
+#define wxStyledTextCtrl_BraceHighlight 3268
+#define wxStyledTextCtrl_BraceBadLight 3269
+#define wxStyledTextCtrl_BraceMatch 3270
+#define wxStyledTextCtrl_GetViewEOL 3271
+#define wxStyledTextCtrl_SetViewEOL 3272
+#define wxStyledTextCtrl_SetModEventMask 3273
+#define wxStyledTextCtrl_GetEdgeColumn 3274
+#define wxStyledTextCtrl_SetEdgeColumn 3275
+#define wxStyledTextCtrl_SetEdgeMode 3276
+#define wxStyledTextCtrl_GetEdgeMode 3277
+#define wxStyledTextCtrl_GetEdgeColour 3278
+#define wxStyledTextCtrl_SetEdgeColour 3279
+#define wxStyledTextCtrl_SearchAnchor 3280
+#define wxStyledTextCtrl_SearchNext 3281
+#define wxStyledTextCtrl_SearchPrev 3282
+#define wxStyledTextCtrl_LinesOnScreen 3283
+#define wxStyledTextCtrl_UsePopUp 3284
+#define wxStyledTextCtrl_SelectionIsRectangle 3285
+#define wxStyledTextCtrl_SetZoom 3286
+#define wxStyledTextCtrl_GetZoom 3287
+#define wxStyledTextCtrl_GetModEventMask 3288
+#define wxStyledTextCtrl_SetSTCFocus 3289
+#define wxStyledTextCtrl_GetSTCFocus 3290
+#define wxStyledTextCtrl_SetStatus 3291
+#define wxStyledTextCtrl_GetStatus 3292
+#define wxStyledTextCtrl_SetMouseDownCaptures 3293
+#define wxStyledTextCtrl_GetMouseDownCaptures 3294
+#define wxStyledTextCtrl_SetSTCCursor 3295
+#define wxStyledTextCtrl_GetSTCCursor 3296
+#define wxStyledTextCtrl_SetControlCharSymbol 3297
+#define wxStyledTextCtrl_GetControlCharSymbol 3298
+#define wxStyledTextCtrl_WordPartLeft 3299
+#define wxStyledTextCtrl_WordPartLeftExtend 3300
+#define wxStyledTextCtrl_WordPartRight 3301
+#define wxStyledTextCtrl_WordPartRightExtend 3302
+#define wxStyledTextCtrl_SetVisiblePolicy 3303
+#define wxStyledTextCtrl_DelLineLeft 3304
+#define wxStyledTextCtrl_DelLineRight 3305
+#define wxStyledTextCtrl_GetXOffset 3306
+#define wxStyledTextCtrl_ChooseCaretX 3307
+#define wxStyledTextCtrl_SetXCaretPolicy 3308
+#define wxStyledTextCtrl_SetYCaretPolicy 3309
+#define wxStyledTextCtrl_GetPrintWrapMode 3310
+#define wxStyledTextCtrl_SetHotspotActiveForeground 3311
+#define wxStyledTextCtrl_SetHotspotActiveBackground 3312
+#define wxStyledTextCtrl_SetHotspotActiveUnderline 3313
+#define wxStyledTextCtrl_SetHotspotSingleLine 3314
+#define wxStyledTextCtrl_ParaDownExtend 3315
+#define wxStyledTextCtrl_ParaUp 3316
+#define wxStyledTextCtrl_ParaUpExtend 3317
+#define wxStyledTextCtrl_PositionBefore 3318
+#define wxStyledTextCtrl_PositionAfter 3319
+#define wxStyledTextCtrl_CopyRange 3320
+#define wxStyledTextCtrl_CopyText 3321
+#define wxStyledTextCtrl_SetSelectionMode 3322
+#define wxStyledTextCtrl_GetSelectionMode 3323
+#define wxStyledTextCtrl_LineDownRectExtend 3324
+#define wxStyledTextCtrl_LineUpRectExtend 3325
+#define wxStyledTextCtrl_CharLeftRectExtend 3326
+#define wxStyledTextCtrl_CharRightRectExtend 3327
+#define wxStyledTextCtrl_HomeRectExtend 3328
+#define wxStyledTextCtrl_VCHomeRectExtend 3329
+#define wxStyledTextCtrl_LineEndRectExtend 3330
+#define wxStyledTextCtrl_PageUpRectExtend 3331
+#define wxStyledTextCtrl_PageDownRectExtend 3332
+#define wxStyledTextCtrl_StutteredPageUp 3333
+#define wxStyledTextCtrl_StutteredPageUpExtend 3334
+#define wxStyledTextCtrl_StutteredPageDown 3335
+#define wxStyledTextCtrl_StutteredPageDownExtend 3336
+#define wxStyledTextCtrl_WordLeftEnd 3337
+#define wxStyledTextCtrl_WordLeftEndExtend 3338
+#define wxStyledTextCtrl_WordRightEnd 3339
+#define wxStyledTextCtrl_WordRightEndExtend 3340
+#define wxStyledTextCtrl_SetWhitespaceChars 3341
+#define wxStyledTextCtrl_SetCharsDefault 3342
+#define wxStyledTextCtrl_AutoCompGetCurrent 3343
+#define wxStyledTextCtrl_Allocate 3344
+#define wxStyledTextCtrl_FindColumn 3345
+#define wxStyledTextCtrl_GetCaretSticky 3346
+#define wxStyledTextCtrl_SetCaretSticky 3347
+#define wxStyledTextCtrl_ToggleCaretSticky 3348
+#define wxStyledTextCtrl_SetPasteConvertEndings 3349
+#define wxStyledTextCtrl_GetPasteConvertEndings 3350
+#define wxStyledTextCtrl_SelectionDuplicate 3351
+#define wxStyledTextCtrl_SetCaretLineBackAlpha 3352
+#define wxStyledTextCtrl_GetCaretLineBackAlpha 3353
+#define wxStyledTextCtrl_StartRecord 3354
+#define wxStyledTextCtrl_StopRecord 3355
+#define wxStyledTextCtrl_SetLexer 3356
+#define wxStyledTextCtrl_GetLexer 3357
+#define wxStyledTextCtrl_Colourise 3358
+#define wxStyledTextCtrl_SetProperty 3359
+#define wxStyledTextCtrl_SetKeyWords 3360
+#define wxStyledTextCtrl_SetLexerLanguage 3361
+#define wxStyledTextCtrl_GetProperty 3362
+#define wxStyledTextCtrl_GetStyleBitsNeeded 3363
+#define wxStyledTextCtrl_GetCurrentLine 3364
+#define wxStyledTextCtrl_StyleSetSpec 3365
+#define wxStyledTextCtrl_StyleSetFont 3366
+#define wxStyledTextCtrl_StyleSetFontAttr 3367
+#define wxStyledTextCtrl_StyleSetCharacterSet 3368
+#define wxStyledTextCtrl_StyleSetFontEncoding 3369
+#define wxStyledTextCtrl_CmdKeyExecute 3370
+#define wxStyledTextCtrl_SetMargins 3371
+#define wxStyledTextCtrl_GetSelection 3372
+#define wxStyledTextCtrl_PointFromPosition 3373
+#define wxStyledTextCtrl_ScrollToLine 3374
+#define wxStyledTextCtrl_ScrollToColumn 3375
+#define wxStyledTextCtrl_SetVScrollBar 3376
+#define wxStyledTextCtrl_SetHScrollBar 3377
+#define wxStyledTextCtrl_GetLastKeydownProcessed 3378
+#define wxStyledTextCtrl_SetLastKeydownProcessed 3379
+#define wxStyledTextCtrl_SaveFile 3380
+#define wxStyledTextCtrl_LoadFile 3381
+#define wxStyledTextCtrl_DoDragOver 3382
+#define wxStyledTextCtrl_DoDropText 3383
+#define wxStyledTextCtrl_GetUseAntiAliasing 3384
+#define wxStyledTextCtrl_AddTextRaw 3385
+#define wxStyledTextCtrl_InsertTextRaw 3386
+#define wxStyledTextCtrl_GetCurLineRaw 3387
+#define wxStyledTextCtrl_GetLineRaw 3388
+#define wxStyledTextCtrl_GetSelectedTextRaw 3389
+#define wxStyledTextCtrl_GetTextRangeRaw 3390
+#define wxStyledTextCtrl_SetTextRaw 3391
+#define wxStyledTextCtrl_GetTextRaw 3392
+#define wxStyledTextCtrl_AppendTextRaw 3393
+#define wxArtProvider_GetBitmap 3394
+#define wxArtProvider_GetIcon 3395
+#define wxTreeEvent_GetKeyCode 3396
+#define wxTreeEvent_GetItem 3397
+#define wxTreeEvent_GetKeyEvent 3398
+#define wxTreeEvent_GetLabel 3399
+#define wxTreeEvent_GetOldItem 3400
+#define wxTreeEvent_GetPoint 3401
+#define wxTreeEvent_IsEditCancelled 3402
+#define wxTreeEvent_SetToolTip 3403
+#define wxNotebookEvent_GetOldSelection 3404
+#define wxNotebookEvent_GetSelection 3405
+#define wxNotebookEvent_SetOldSelection 3406
+#define wxNotebookEvent_SetSelection 3407
+#define wxFileDataObject_new 3408
+#define wxFileDataObject_AddFile 3409
+#define wxFileDataObject_GetFilenames 3410
+#define wxFileDataObject_destroy 3411
+#define wxTextDataObject_new 3412
+#define wxTextDataObject_GetTextLength 3413
+#define wxTextDataObject_GetText 3414
+#define wxTextDataObject_SetText 3415
+#define wxTextDataObject_destroy 3416
+#define wxBitmapDataObject_new_1_1 3417
+#define wxBitmapDataObject_new_1_0 3418
+#define wxBitmapDataObject_GetBitmap 3419
+#define wxBitmapDataObject_SetBitmap 3420
+#define wxBitmapDataObject_destroy 3421
+#define wxClipboard_new 3423
+#define wxClipboard_destruct 3424
+#define wxClipboard_AddData 3425
+#define wxClipboard_Clear 3426
+#define wxClipboard_Close 3427
+#define wxClipboard_Flush 3428
+#define wxClipboard_GetData 3429
+#define wxClipboard_IsOpened 3430
+#define wxClipboard_Open 3431
+#define wxClipboard_SetData 3432
+#define wxClipboard_UsePrimarySelection 3434
+#define wxClipboard_IsSupported 3435
+#define wxClipboard_Get 3436
+#define wxSpinEvent_GetPosition 3437
+#define wxSpinEvent_SetPosition 3438
+#define wxSplitterWindow_new_0 3439
+#define wxSplitterWindow_new_2 3440
+#define wxSplitterWindow_destruct 3441
+#define wxSplitterWindow_Create 3442
+#define wxSplitterWindow_GetMinimumPaneSize 3443
+#define wxSplitterWindow_GetSashGravity 3444
+#define wxSplitterWindow_GetSashPosition 3445
+#define wxSplitterWindow_GetSplitMode 3446
+#define wxSplitterWindow_GetWindow1 3447
+#define wxSplitterWindow_GetWindow2 3448
+#define wxSplitterWindow_Initialize 3449
+#define wxSplitterWindow_IsSplit 3450
+#define wxSplitterWindow_ReplaceWindow 3451
+#define wxSplitterWindow_SetSashGravity 3452
+#define wxSplitterWindow_SetSashPosition 3453
+#define wxSplitterWindow_SetSashSize 3454
+#define wxSplitterWindow_SetMinimumPaneSize 3455
+#define wxSplitterWindow_SetSplitMode 3456
+#define wxSplitterWindow_SplitHorizontally 3457
+#define wxSplitterWindow_SplitVertically 3458
+#define wxSplitterWindow_Unsplit 3459
+#define wxSplitterWindow_UpdateSize 3460
+#define wxSplitterEvent_GetSashPosition 3461
+#define wxSplitterEvent_GetX 3462
+#define wxSplitterEvent_GetY 3463
+#define wxSplitterEvent_GetWindowBeingRemoved 3464
+#define wxSplitterEvent_SetSashPosition 3465
+#define wxHtmlWindow_new_0 3466
+#define wxHtmlWindow_new_2 3467
+#define wxHtmlWindow_AppendToPage 3468
+#define wxHtmlWindow_GetOpenedAnchor 3469
+#define wxHtmlWindow_GetOpenedPage 3470
+#define wxHtmlWindow_GetOpenedPageTitle 3471
+#define wxHtmlWindow_GetRelatedFrame 3472
+#define wxHtmlWindow_HistoryBack 3473
+#define wxHtmlWindow_HistoryCanBack 3474
+#define wxHtmlWindow_HistoryCanForward 3475
+#define wxHtmlWindow_HistoryClear 3476
+#define wxHtmlWindow_HistoryForward 3477
+#define wxHtmlWindow_LoadFile 3478
+#define wxHtmlWindow_LoadPage 3479
+#define wxHtmlWindow_SelectAll 3480
+#define wxHtmlWindow_SelectionToText 3481
+#define wxHtmlWindow_SelectLine 3482
+#define wxHtmlWindow_SelectWord 3483
+#define wxHtmlWindow_SetBorders 3484
+#define wxHtmlWindow_SetFonts 3485
+#define wxHtmlWindow_SetPage 3486
+#define wxHtmlWindow_SetRelatedFrame 3487
+#define wxHtmlWindow_SetRelatedStatusBar 3488
+#define wxHtmlWindow_ToText 3489
+#define wxHtmlWindow_destroy 3490
+#define wxHtmlLinkEvent_GetLinkInfo 3491
+#define wxSystemSettings_GetColour 3492
+#define wxSystemSettings_GetFont 3493
+#define wxSystemSettings_GetMetric 3494
+#define wxSystemSettings_GetScreenType 3495
+#define wxSystemOptions_GetOption 3496
+#define wxSystemOptions_GetOptionInt 3497
+#define wxSystemOptions_HasOption 3498
+#define wxSystemOptions_IsFalse 3499
+#define wxSystemOptions_SetOption_2_1 3500
+#define wxSystemOptions_SetOption_2_0 3501
+#define wxAuiNotebookEvent_SetSelection 3502
+#define wxAuiNotebookEvent_GetSelection 3503
+#define wxAuiNotebookEvent_SetOldSelection 3504
+#define wxAuiNotebookEvent_GetOldSelection 3505
+#define wxAuiNotebookEvent_SetDragSource 3506
+#define wxAuiNotebookEvent_GetDragSource 3507
+#define wxAuiManagerEvent_SetManager 3508
+#define wxAuiManagerEvent_GetManager 3509
+#define wxAuiManagerEvent_SetPane 3510
+#define wxAuiManagerEvent_GetPane 3511
+#define wxAuiManagerEvent_SetButton 3512
+#define wxAuiManagerEvent_GetButton 3513
+#define wxAuiManagerEvent_SetDC 3514
+#define wxAuiManagerEvent_GetDC 3515
+#define wxAuiManagerEvent_Veto 3516
+#define wxAuiManagerEvent_GetVeto 3517
+#define wxAuiManagerEvent_SetCanVeto 3518
+#define wxAuiManagerEvent_CanVeto 3519
+#define wxLogNull_new 3520
+#define wxLogNull_destroy 3521
+#define wxTaskBarIcon_new 3522
+#define wxTaskBarIcon_destruct 3523
+#define wxTaskBarIcon_PopupMenu 3524
+#define wxTaskBarIcon_RemoveIcon 3525
+#define wxTaskBarIcon_SetIcon 3526
+#define wxLocale_new_0 3527
+#define wxLocale_new_2 3529
+#define wxLocale_destruct 3530
+#define wxLocale_Init 3532
+#define wxLocale_AddCatalog_1 3533
+#define wxLocale_AddCatalog_3 3534
+#define wxLocale_AddCatalogLookupPathPrefix 3535
+#define wxLocale_GetCanonicalName 3536
+#define wxLocale_GetLanguage 3537
+#define wxLocale_GetLanguageName 3538
+#define wxLocale_GetLocale 3539
+#define wxLocale_GetName 3540
+#define wxLocale_GetString_2 3541
+#define wxLocale_GetString_4 3542
+#define wxLocale_GetHeaderValue 3543
+#define wxLocale_GetSysName 3544
+#define wxLocale_GetSystemEncoding 3545
+#define wxLocale_GetSystemEncodingName 3546
+#define wxLocale_GetSystemLanguage 3547
+#define wxLocale_IsLoaded 3548
+#define wxLocale_IsOk 3549
diff --git a/lib/wx/c_src/wxe_callback_impl.cpp b/lib/wx/c_src/wxe_callback_impl.cpp
index 8ba2557d82..3a59004eb0 100644
--- a/lib/wx/c_src/wxe_callback_impl.cpp
+++ b/lib/wx/c_src/wxe_callback_impl.cpp
@@ -30,27 +30,27 @@
* CallbackData *
* ****************************************************************************/
-wxeCallbackData::wxeCallbackData(ErlDrvTermData caller, int req, char *req_type,
- int funcb, int skip_ev, wxeErlTerm * userData,
- wxeEvtListener *handler_cb)
- : wxObject()
+wxeEvtListener::wxeEvtListener(ErlDrvTermData caller, int req, char *req_type,
+ int funcb, int skip_ev, wxeErlTerm * userData,
+ ErlDrvTermData from_port)
+ : wxEvtHandler()
{
+ port=from_port;
listener = caller;
obj = req;
fun_id = funcb;
strcpy(class_name, req_type);
skip = skip_ev;
user_data = userData;
- handler = handler_cb;
}
-wxeCallbackData::~wxeCallbackData() {
- // fprintf(stderr, "CBD Deleteing %p %s\r\n", this, class_name); fflush(stderr);
+wxeEvtListener::~wxeEvtListener() {
+ // fprintf(stderr, "CBD Deleteing %p %s\r\n", this, class_name); fflush(stderr);
if(user_data) {
delete user_data;
}
ptrMap::iterator it;
- it = ((WxeApp *)wxTheApp)->ptr2ref.find(handler);
+ it = ((WxeApp *)wxTheApp)->ptr2ref.find(this);
if(it != ((WxeApp *)wxTheApp)->ptr2ref.end()) {
wxeRefData *refd = it->second;
wxeReturn rt = wxeReturn(WXE_DRV_PORT, refd->memenv->owner, false);
@@ -61,6 +61,17 @@ wxeCallbackData::~wxeCallbackData() {
rt.addTupleCount(4);
rt.send();
}
+ ((WxeApp *)wxTheApp)->clearPtr(this);
+}
+
+void wxeEvtListener::forward(wxEvent& event)
+{
+#ifdef DEBUG
+ if(!sendevent(&event, port))
+ fprintf(stderr, "Couldn't send event!\r\n");
+#else
+sendevent(&event, port);
+#endif
}
/* *****************************************************************/
@@ -273,7 +284,7 @@ EwxListCtrl::~EwxListCtrl() {
* wxListCtrlCompare wrapper
* ****************************************************************************/
-int wxCALLBACK wxEListCtrlCompare(long item1, long item2, long callbackInfoPtr)
+int wxCALLBACK wxEListCtrlCompare(wxeIntPtr item1, wxeIntPtr item2, wxeIntPtr callbackInfoPtr)
{
callbackInfo * cb = (callbackInfo *)callbackInfoPtr;
wxeMemEnv * memenv = ((WxeApp *) wxTheApp)->getMemEnv(cb->port);
diff --git a/lib/wx/c_src/wxe_callback_impl.h b/lib/wx/c_src/wxe_callback_impl.h
index c7243e23a4..ecfcd3db41 100644
--- a/lib/wx/c_src/wxe_callback_impl.h
+++ b/lib/wx/c_src/wxe_callback_impl.h
@@ -23,6 +23,31 @@
void pre_callback();
void handle_event_callback(ErlDrvPort port, ErlDrvTermData process);
+#if wxCHECK_VERSION(2,9,0)
+ #define wxeIntPtr wxIntPtr
+#else
+ // This is bad but how it was in wx-2.8
+ #define wxeIntPtr long
+#endif
+
+/* Fun Callback id */
+class wxeEvtListener : public wxEvtHandler
+{
+public:
+ wxeEvtListener(ErlDrvTermData caller, int req, char *req_type,
+ int funcb, int skip_ev, wxeErlTerm * userData,
+ ErlDrvTermData Thisport);
+ ~wxeEvtListener();
+ void forward(wxEvent& event);
+ ErlDrvTermData port;
+ ErlDrvTermData listener;
+ int fun_id;
+ int obj;
+ char class_name[40];
+ int skip;
+ wxeErlTerm * user_data;
+};
+
class wxEPrintout : public wxPrintout
{
public:
@@ -70,6 +95,6 @@ struct callbackInfo {
int callbackID;
};
-int wxCALLBACK wxEListCtrlCompare(long item1, long item2, long callbackInfoPtr);
+int wxCALLBACK wxEListCtrlCompare(wxeIntPtr item1, wxeIntPtr item2, wxeIntPtr callbackInfoPtr);
#endif
diff --git a/lib/wx/c_src/wxe_events.h b/lib/wx/c_src/wxe_events.h
index 22a737d854..93b5551123 100644
--- a/lib/wx/c_src/wxe_events.h
+++ b/lib/wx/c_src/wxe_events.h
@@ -32,38 +32,7 @@ public:
int cID;
};
-/* One EvtListener per listening erlang process */
-/* If callbacks are used the receiver is wxe_master process */
-/* and a wxeEvtListener pre callback is registered */
-class wxeEvtListener : public wxEvtHandler
-{
- public:
- wxeEvtListener(ErlDrvTermData Thisport) : port(Thisport)
- {}
- // {fprintf(stderr, "Creating %x\r\n", (unsigned int) this); fflush(stderr);}
- ~wxeEvtListener() {}
- void forward(wxEvent& event);
- ErlDrvTermData port;
-};
-
void initEventTable();
int wxeEventTypeFromAtom(char *etype_atom);
-/* Fun Callback id */
-class wxeCallbackData : public wxObject
-{
-public:
- wxeCallbackData(ErlDrvTermData caller, int req, char *req_type,
- int funcb, int skip_ev, wxeErlTerm * userData,
- wxeEvtListener *handler_cb);
- ~wxeCallbackData();
- wxeEvtListener * handler;
- ErlDrvTermData listener;
- int fun_id;
- int obj;
- char class_name[40];
- int skip;
- wxeErlTerm * user_data;
-};
-
#endif
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index 5964ccfd00..f617aaf349 100644
--- a/lib/wx/c_src/wxe_impl.cpp
+++ b/lib/wx/c_src/wxe_impl.cpp
@@ -104,7 +104,7 @@ void meta_command(int what, wxe_data *sd) {
}
}
-void send_msg(const char * type, wxString * msg) {
+void send_msg(const char * type, const wxString * msg) {
wxeReturn rt = wxeReturn(WXE_DRV_PORT, init_caller);
rt.addAtom((char *) "wxe_driver");
rt.addAtom((char *) type);
@@ -160,6 +160,13 @@ bool WxeApp::OnInit()
return TRUE;
}
+
+#ifdef _MACOSX
+void WxeApp::MacOpenFile(const wxString &filename) {
+ send_msg("open_file", &filename);
+}
+#endif
+
void WxeApp::shutdown(wxeMetaCommand& Ecmd) {
ExitMainLoop();
}
@@ -411,6 +418,13 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
wxWindow *parent = NULL;
wxeMemEnv * memenv = refmap[Ecmd.port];
+ if(!memenv) {
+ wxString msg;
+ msg.Printf(wxT("MemEnv already deleted"));
+ send_msg("debug", &msg);
+ return;
+ }
+
if(wxe_debug) {
wxString msg;
msg.Printf(wxT("Destroying all memory "));
@@ -439,7 +453,6 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
}
if(recurse_level > 0) {
// Delay delete until we are out of dispatch*
- delayed_cleanup->Append(Ecmd.Clone());
} else {
delete win;
}
@@ -448,9 +461,10 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
}
}
- if(recurse_level > 0)
+ if(recurse_level > 0) {
+ delayed_cleanup->Append(Ecmd.Clone());
return;
-
+ }
// First pass, delete all top parents/windows of all linked objects
// fprintf(stderr, "close port %x\r\n", Ecmd.port);fflush(stderr);
@@ -486,20 +500,20 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
if(refd->alloc_in_erl) {
- int type = refd->type;
if((refd->type == 1) && ((wxObject *)ptr)->IsKindOf(CLASSINFO(wxBufferedDC))) {
((wxBufferedDC *)ptr)->m_dc = NULL; // Workaround
}
wxString msg;
- if((refd->type == 0)) { // Maybe also class 1
+ bool cleanup_ref=true;
+ if(refd->type == 0) { // Maybe also class 1
wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
msg.Printf(wxT("Memory leak: {wx_ref, %d, %s}"),
refd->ref, cinfo->GetClassName());
send_msg("error", &msg);
} else {
- delete_object(ptr, refd);
+ cleanup_ref = delete_object(ptr, refd);
}
- if(type == 0 || type > 2) {
+ if(cleanup_ref) {
// Delete refs for leaks and non overridden allocs
delete refd;
ptr2ref.erase(it);
@@ -607,7 +621,7 @@ void WxeApp::clearPtr(void * ptr) {
if(((int) refd->pid) != -1) {
// Send terminate pid to owner
- wxeReturn rt = wxeReturn(WXE_DRV_PORT,refd->memenv->owner, false);
+ wxeReturn rt = wxeReturn(WXE_DRV_PORT,refd->pid, false);
rt.addAtom("_wxe_destroy_");
rt.add(ERL_DRV_PID, refd->pid);
rt.addTupleCount(2);
diff --git a/lib/wx/c_src/wxe_impl.h b/lib/wx/c_src/wxe_impl.h
index bb54961edd..5b23e1cbbd 100644
--- a/lib/wx/c_src/wxe_impl.h
+++ b/lib/wx/c_src/wxe_impl.h
@@ -36,17 +36,28 @@ extern "C" {
#include "wxe_callback_impl.h"
#include "wxe_memory.h"
+#if !wxCHECK_VERSION(2,9,0)
+#define wxeLocaleC wxChar *
+#define wxeLocaleC2String(Str) wxString(Str)
+#else
+typedef wxString wxeLocaleC;
+#define wxeLocaleC2String(Str) Str
+#endif
+
#define WXE_NOT_INITIATED 0
#define WXE_INITIATED 1
#define WXE_EXITED 2
#define WXE_ERROR -1
-void send_msg(const char *, wxString *); // For debugging and error msgs
+void send_msg(const char *, const wxString *); // For debugging and error msgs
class WxeApp : public wxApp
{
public:
- virtual bool OnInit();
+ virtual bool OnInit();
+#ifdef _MACOSX
+ virtual void MacOpenFile(const wxString &filename);
+#endif
void shutdown(wxeMetaCommand& event);
int dispatch(wxList *, int, int);
@@ -73,7 +84,7 @@ public:
void init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller);
// Code found in gen/wxe_derived_dest.h
- void delete_object(void *ptr, wxeRefData *refd);
+ bool delete_object(void *ptr, wxeRefData *refd);
wxeMemMap refmap;
ptrMap ptr2ref;
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 3756786831..a96f1f2632 100755
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -441,12 +441,12 @@ else
else
CWXWIN_PROG=`cygpath -d "$PROGRAMFILES" | cygpath -f - 2>/dev/null`
fi
- CWXWIN3=$CWXWIN_PROG/wxWidgets-2.*.*
- CWXWIN4=$CWXWIN_PROG/wxMSW-2.*.*
- CWX_DOCUMENTED="/opt/local/pgm/wxMSW-2.*.* /opt/local/pgm/wxWidgets-2.*.*"
+ CWXWIN3=$CWXWIN_PROG/wxWidgets-?.*.*
+ CWXWIN4=$CWXWIN_PROG/wxMSW-?.*.*
+ CWX_DOCUMENTED="/opt/local/pgm/wxMSW-?.*.* /opt/local/pgm/wxWidgets-?.*.*"
case $ac_cv_sizeof_void_p in
8)
- CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-2.*.* /opt/local64/pgm/wxWidgets-2.*.* $CWX_DOCUMENTED"
+ CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-?.*.* /opt/local64/pgm/wxWidgets-?.*.* $CWX_DOCUMENTED"
;;
*)
true
@@ -464,12 +464,14 @@ else
WX_CFLAGS="-EHsc -D_UNICODE -DUNICODE -I$WXINCLUDE_MSVC -I$WXINCLUDE_PLAIN -I$WXINCLUDE_CONTRIB -D__WXMSW__"
WX_CXXFLAGS="-TP $WX_CFLAGS"
WX_LIBDIR=$dir/lib/vc_lib
+ WX_LIBDIR64=$dir/lib/vc_x64_lib
WX_RESCOMP="rc.sh -I$WXINCLUDE_PLAIN -D __WIN32__"
RC_FILE_TYPE=res
- for lib in $WX_LIBDIR/wxbase*.lib $WX_LIBDIR2/wxbase*.lib; do
- maybe=`echo $lib | egrep 'wxbase[[0-9]]*u\.lib'`
+ for lib in $WX_LIBDIR $WX_LIBDIR64; do
+ maybe=`ls $lib/wxbase*.lib | egrep 'wxbase[[0-9]]*u\.lib'`
if test '!' -z "$maybe"; then
corelib_number=`echo $maybe | sed 's,.*\([[0-9]].\)u\.lib,\1,'`
+ WX_LIBDIR=$lib
break
fi
done
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index 2dc1791cce..ac852ce054 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -1980,10 +1980,255 @@
-define(wxIMAGELIST_DRAW_SELECTED, 4).
-define(wxIMAGELIST_DRAW_TRANSPARENT, 2).
-define(wxIMAGELIST_DRAW_NORMAL, 1).
+% From "intl.h": wxLanguage
+-define(wxLANGUAGE_DEFAULT, 0).
+-define(wxLANGUAGE_UNKNOWN, 1).
+-define(wxLANGUAGE_ABKHAZIAN, 2).
+-define(wxLANGUAGE_AFAR, 3).
+-define(wxLANGUAGE_AFRIKAANS, 4).
+-define(wxLANGUAGE_ALBANIAN, 5).
+-define(wxLANGUAGE_AMHARIC, 6).
+-define(wxLANGUAGE_ARABIC, 7).
+-define(wxLANGUAGE_ARABIC_ALGERIA, 8).
+-define(wxLANGUAGE_ARABIC_BAHRAIN, 9).
+-define(wxLANGUAGE_ARABIC_EGYPT, 10).
+-define(wxLANGUAGE_ARABIC_IRAQ, 11).
+-define(wxLANGUAGE_ARABIC_JORDAN, 12).
+-define(wxLANGUAGE_ARABIC_KUWAIT, 13).
+-define(wxLANGUAGE_ARABIC_LEBANON, 14).
+-define(wxLANGUAGE_ARABIC_LIBYA, 15).
+-define(wxLANGUAGE_ARABIC_MOROCCO, 16).
+-define(wxLANGUAGE_ARABIC_OMAN, 17).
+-define(wxLANGUAGE_ARABIC_QATAR, 18).
+-define(wxLANGUAGE_ARABIC_SAUDI_ARABIA, 19).
+-define(wxLANGUAGE_ARABIC_SUDAN, 20).
+-define(wxLANGUAGE_ARABIC_SYRIA, 21).
+-define(wxLANGUAGE_ARABIC_TUNISIA, 22).
+-define(wxLANGUAGE_ARABIC_UAE, 23).
+-define(wxLANGUAGE_ARABIC_YEMEN, 24).
+-define(wxLANGUAGE_ARMENIAN, 25).
+-define(wxLANGUAGE_ASSAMESE, 26).
+-define(wxLANGUAGE_AYMARA, 27).
+-define(wxLANGUAGE_AZERI, 28).
+-define(wxLANGUAGE_AZERI_CYRILLIC, 29).
+-define(wxLANGUAGE_AZERI_LATIN, 30).
+-define(wxLANGUAGE_BASHKIR, 31).
+-define(wxLANGUAGE_BASQUE, 32).
+-define(wxLANGUAGE_BELARUSIAN, 33).
+-define(wxLANGUAGE_BENGALI, 34).
+-define(wxLANGUAGE_BHUTANI, 35).
+-define(wxLANGUAGE_BIHARI, 36).
+-define(wxLANGUAGE_BISLAMA, 37).
+-define(wxLANGUAGE_BRETON, 38).
+-define(wxLANGUAGE_BULGARIAN, 39).
+-define(wxLANGUAGE_BURMESE, 40).
+-define(wxLANGUAGE_CAMBODIAN, 41).
+-define(wxLANGUAGE_CATALAN, 42).
+-define(wxLANGUAGE_CHINESE, 43).
+-define(wxLANGUAGE_CHINESE_SIMPLIFIED, 44).
+-define(wxLANGUAGE_CHINESE_TRADITIONAL, 45).
+-define(wxLANGUAGE_CHINESE_HONGKONG, 46).
+-define(wxLANGUAGE_CHINESE_MACAU, 47).
+-define(wxLANGUAGE_CHINESE_SINGAPORE, 48).
+-define(wxLANGUAGE_CHINESE_TAIWAN, 49).
+-define(wxLANGUAGE_CORSICAN, 50).
+-define(wxLANGUAGE_CROATIAN, 51).
+-define(wxLANGUAGE_CZECH, 52).
+-define(wxLANGUAGE_DANISH, 53).
+-define(wxLANGUAGE_DUTCH, 54).
+-define(wxLANGUAGE_DUTCH_BELGIAN, 55).
+-define(wxLANGUAGE_ENGLISH, 56).
+-define(wxLANGUAGE_ENGLISH_UK, 57).
+-define(wxLANGUAGE_ENGLISH_US, 58).
+-define(wxLANGUAGE_ENGLISH_AUSTRALIA, 59).
+-define(wxLANGUAGE_ENGLISH_BELIZE, 60).
+-define(wxLANGUAGE_ENGLISH_BOTSWANA, 61).
+-define(wxLANGUAGE_ENGLISH_CANADA, 62).
+-define(wxLANGUAGE_ENGLISH_CARIBBEAN, 63).
+-define(wxLANGUAGE_ENGLISH_DENMARK, 64).
+-define(wxLANGUAGE_ENGLISH_EIRE, 65).
+-define(wxLANGUAGE_ENGLISH_JAMAICA, 66).
+-define(wxLANGUAGE_ENGLISH_NEW_ZEALAND, 67).
+-define(wxLANGUAGE_ENGLISH_PHILIPPINES, 68).
+-define(wxLANGUAGE_ENGLISH_SOUTH_AFRICA, 69).
+-define(wxLANGUAGE_ENGLISH_TRINIDAD, 70).
+-define(wxLANGUAGE_ENGLISH_ZIMBABWE, 71).
+-define(wxLANGUAGE_ESPERANTO, 72).
+-define(wxLANGUAGE_ESTONIAN, 73).
+-define(wxLANGUAGE_FAEROESE, 74).
+-define(wxLANGUAGE_FARSI, 75).
+-define(wxLANGUAGE_FIJI, 76).
+-define(wxLANGUAGE_FINNISH, 77).
+-define(wxLANGUAGE_FRENCH, 78).
+-define(wxLANGUAGE_FRENCH_BELGIAN, 79).
+-define(wxLANGUAGE_FRENCH_CANADIAN, 80).
+-define(wxLANGUAGE_FRENCH_LUXEMBOURG, 81).
+-define(wxLANGUAGE_FRENCH_MONACO, 82).
+-define(wxLANGUAGE_FRENCH_SWISS, 83).
+-define(wxLANGUAGE_FRISIAN, 84).
+-define(wxLANGUAGE_GALICIAN, 85).
+-define(wxLANGUAGE_GEORGIAN, 86).
+-define(wxLANGUAGE_GERMAN, 87).
+-define(wxLANGUAGE_GERMAN_AUSTRIAN, 88).
+-define(wxLANGUAGE_GERMAN_BELGIUM, 89).
+-define(wxLANGUAGE_GERMAN_LIECHTENSTEIN, 90).
+-define(wxLANGUAGE_GERMAN_LUXEMBOURG, 91).
+-define(wxLANGUAGE_GERMAN_SWISS, 92).
+-define(wxLANGUAGE_GREEK, 93).
+-define(wxLANGUAGE_GREENLANDIC, 94).
+-define(wxLANGUAGE_GUARANI, 95).
+-define(wxLANGUAGE_GUJARATI, 96).
+-define(wxLANGUAGE_HAUSA, 97).
+-define(wxLANGUAGE_HEBREW, 98).
+-define(wxLANGUAGE_HINDI, 99).
+-define(wxLANGUAGE_HUNGARIAN, 100).
+-define(wxLANGUAGE_ICELANDIC, 101).
+-define(wxLANGUAGE_INDONESIAN, 102).
+-define(wxLANGUAGE_INTERLINGUA, 103).
+-define(wxLANGUAGE_INTERLINGUE, 104).
+-define(wxLANGUAGE_INUKTITUT, 105).
+-define(wxLANGUAGE_INUPIAK, 106).
+-define(wxLANGUAGE_IRISH, 107).
+-define(wxLANGUAGE_ITALIAN, 108).
+-define(wxLANGUAGE_ITALIAN_SWISS, 109).
+-define(wxLANGUAGE_JAPANESE, 110).
+-define(wxLANGUAGE_JAVANESE, 111).
+-define(wxLANGUAGE_KANNADA, 112).
+-define(wxLANGUAGE_KASHMIRI, 113).
+-define(wxLANGUAGE_KASHMIRI_INDIA, 114).
+-define(wxLANGUAGE_KAZAKH, 115).
+-define(wxLANGUAGE_KERNEWEK, 116).
+-define(wxLANGUAGE_KINYARWANDA, 117).
+-define(wxLANGUAGE_KIRGHIZ, 118).
+-define(wxLANGUAGE_KIRUNDI, 119).
+-define(wxLANGUAGE_KONKANI, 120).
+-define(wxLANGUAGE_KOREAN, 121).
+-define(wxLANGUAGE_KURDISH, 122).
+-define(wxLANGUAGE_LAOTHIAN, 123).
+-define(wxLANGUAGE_LATIN, 124).
+-define(wxLANGUAGE_LATVIAN, 125).
+-define(wxLANGUAGE_LINGALA, 126).
+-define(wxLANGUAGE_LITHUANIAN, 127).
+-define(wxLANGUAGE_MACEDONIAN, 128).
+-define(wxLANGUAGE_MALAGASY, 129).
+-define(wxLANGUAGE_MALAY, 130).
+-define(wxLANGUAGE_MALAYALAM, 131).
+-define(wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM, 132).
+-define(wxLANGUAGE_MALAY_MALAYSIA, 133).
+-define(wxLANGUAGE_MALTESE, 134).
+-define(wxLANGUAGE_MANIPURI, 135).
+-define(wxLANGUAGE_MAORI, 136).
+-define(wxLANGUAGE_MARATHI, 137).
+-define(wxLANGUAGE_MOLDAVIAN, 138).
+-define(wxLANGUAGE_MONGOLIAN, 139).
+-define(wxLANGUAGE_NAURU, 140).
+-define(wxLANGUAGE_NEPALI, 141).
+-define(wxLANGUAGE_NEPALI_INDIA, 142).
+-define(wxLANGUAGE_NORWEGIAN_BOKMAL, 143).
+-define(wxLANGUAGE_NORWEGIAN_NYNORSK, 144).
+-define(wxLANGUAGE_OCCITAN, 145).
+-define(wxLANGUAGE_ORIYA, 146).
+-define(wxLANGUAGE_OROMO, 147).
+-define(wxLANGUAGE_PASHTO, 148).
+-define(wxLANGUAGE_POLISH, 149).
+-define(wxLANGUAGE_PORTUGUESE, 150).
+-define(wxLANGUAGE_PORTUGUESE_BRAZILIAN, 151).
+-define(wxLANGUAGE_PUNJABI, 152).
+-define(wxLANGUAGE_QUECHUA, 153).
+-define(wxLANGUAGE_RHAETO_ROMANCE, 154).
+-define(wxLANGUAGE_ROMANIAN, 155).
+-define(wxLANGUAGE_RUSSIAN, 156).
+-define(wxLANGUAGE_RUSSIAN_UKRAINE, 157).
+-define(wxLANGUAGE_SAMOAN, 158).
+-define(wxLANGUAGE_SANGHO, 159).
+-define(wxLANGUAGE_SANSKRIT, 160).
+-define(wxLANGUAGE_SCOTS_GAELIC, 161).
+-define(wxLANGUAGE_SERBIAN, 162).
+-define(wxLANGUAGE_SERBIAN_CYRILLIC, 163).
+-define(wxLANGUAGE_SERBIAN_LATIN, 164).
+-define(wxLANGUAGE_SERBO_CROATIAN, 165).
+-define(wxLANGUAGE_SESOTHO, 166).
+-define(wxLANGUAGE_SETSWANA, 167).
+-define(wxLANGUAGE_SHONA, 168).
+-define(wxLANGUAGE_SINDHI, 169).
+-define(wxLANGUAGE_SINHALESE, 170).
+-define(wxLANGUAGE_SISWATI, 171).
+-define(wxLANGUAGE_SLOVAK, 172).
+-define(wxLANGUAGE_SLOVENIAN, 173).
+-define(wxLANGUAGE_SOMALI, 174).
+-define(wxLANGUAGE_SPANISH, 175).
+-define(wxLANGUAGE_SPANISH_ARGENTINA, 176).
+-define(wxLANGUAGE_SPANISH_BOLIVIA, 177).
+-define(wxLANGUAGE_SPANISH_CHILE, 178).
+-define(wxLANGUAGE_SPANISH_COLOMBIA, 179).
+-define(wxLANGUAGE_SPANISH_COSTA_RICA, 180).
+-define(wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC, 181).
+-define(wxLANGUAGE_SPANISH_ECUADOR, 182).
+-define(wxLANGUAGE_SPANISH_EL_SALVADOR, 183).
+-define(wxLANGUAGE_SPANISH_GUATEMALA, 184).
+-define(wxLANGUAGE_SPANISH_HONDURAS, 185).
+-define(wxLANGUAGE_SPANISH_MEXICAN, 186).
+-define(wxLANGUAGE_SPANISH_MODERN, 187).
+-define(wxLANGUAGE_SPANISH_NICARAGUA, 188).
+-define(wxLANGUAGE_SPANISH_PANAMA, 189).
+-define(wxLANGUAGE_SPANISH_PARAGUAY, 190).
+-define(wxLANGUAGE_SPANISH_PERU, 191).
+-define(wxLANGUAGE_SPANISH_PUERTO_RICO, 192).
+-define(wxLANGUAGE_SPANISH_URUGUAY, 193).
+-define(wxLANGUAGE_SPANISH_US, 194).
+-define(wxLANGUAGE_SPANISH_VENEZUELA, 195).
+-define(wxLANGUAGE_SUNDANESE, 196).
+-define(wxLANGUAGE_SWAHILI, 197).
+-define(wxLANGUAGE_SWEDISH, 198).
+-define(wxLANGUAGE_SWEDISH_FINLAND, 199).
+-define(wxLANGUAGE_TAGALOG, 200).
+-define(wxLANGUAGE_TAJIK, 201).
+-define(wxLANGUAGE_TAMIL, 202).
+-define(wxLANGUAGE_TATAR, 203).
+-define(wxLANGUAGE_TELUGU, 204).
+-define(wxLANGUAGE_THAI, 205).
+-define(wxLANGUAGE_TIBETAN, 206).
+-define(wxLANGUAGE_TIGRINYA, 207).
+-define(wxLANGUAGE_TONGA, 208).
+-define(wxLANGUAGE_TSONGA, 209).
+-define(wxLANGUAGE_TURKISH, 210).
+-define(wxLANGUAGE_TURKMEN, 211).
+-define(wxLANGUAGE_TWI, 212).
+-define(wxLANGUAGE_UIGHUR, 213).
+-define(wxLANGUAGE_UKRAINIAN, 214).
+-define(wxLANGUAGE_URDU, 215).
+-define(wxLANGUAGE_URDU_INDIA, 216).
+-define(wxLANGUAGE_URDU_PAKISTAN, 217).
+-define(wxLANGUAGE_UZBEK, 218).
+-define(wxLANGUAGE_UZBEK_CYRILLIC, 219).
+-define(wxLANGUAGE_UZBEK_LATIN, 220).
+-define(wxLANGUAGE_VIETNAMESE, 221).
+-define(wxLANGUAGE_VOLAPUK, 222).
+-define(wxLANGUAGE_WELSH, 223).
+-define(wxLANGUAGE_WOLOF, 224).
+-define(wxLANGUAGE_XHOSA, 225).
+-define(wxLANGUAGE_YIDDISH, 226).
+-define(wxLANGUAGE_YORUBA, 227).
+-define(wxLANGUAGE_ZHUANG, 228).
+-define(wxLANGUAGE_ZULU, 229).
+-define(wxLANGUAGE_USER_DEFINED, 230).
+-define(wxLANGUAGE_VALENCIAN, 536870911).
+-define(wxLANGUAGE_SAMI, 536870912).
% From "intl.h": wxLayoutDirection
-define(wxLayout_Default, 0).
-define(wxLayout_LeftToRight, 1).
-define(wxLayout_RightToLeft, 2).
+% From "intl.h": wxLocaleCategory
+-define(wxLOCALE_CAT_NUMBER, 0).
+-define(wxLOCALE_CAT_DATE, 1).
+-define(wxLOCALE_CAT_MONEY, 2).
+-define(wxLOCALE_CAT_MAX, 3).
+% From "intl.h": wxLocaleInfo
+-define(wxLOCALE_THOUSANDS_SEP, 0).
+-define(wxLOCALE_DECIMAL_POINT, 1).
+% From "intl.h": wxLocaleInitFlags
+-define(wxLOCALE_LOAD_DEFAULT, 1).
+-define(wxLOCALE_CONV_ENCODING, 2).
% From "layout.h"
-define(wxLAYOUT_DEFAULT_MARGIN, 0).
% From "layout.h": wxEdge
diff --git a/lib/wx/src/gen/wxBufferedDC.erl b/lib/wx/src/gen/wxBufferedDC.erl
index c69a426d7f..e2504bbaaa 100644
--- a/lib/wx/src/gen/wxBufferedDC.erl
+++ b/lib/wx/src/gen/wxBufferedDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -54,6 +54,8 @@
setUserScale/3,startDoc/2,startPage/1]).
-export_type([wxBufferedDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxMemoryDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxBufferedPaintDC.erl b/lib/wx/src/gen/wxBufferedPaintDC.erl
index 0e11826da0..c3fa80703c 100644
--- a/lib/wx/src/gen/wxBufferedPaintDC.erl
+++ b/lib/wx/src/gen/wxBufferedPaintDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -56,6 +56,8 @@
startPage/1]).
-export_type([wxBufferedPaintDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxBufferedDC) -> true;
parent_class(wxMemoryDC) -> true;
diff --git a/lib/wx/src/gen/wxClientDC.erl b/lib/wx/src/gen/wxClientDC.erl
index 45909859ce..ae16196774 100644
--- a/lib/wx/src/gen/wxClientDC.erl
+++ b/lib/wx/src/gen/wxClientDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -56,6 +56,8 @@
-export_type([wxClientDC/0]).
-deprecated([new/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxWindowDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxEvtHandler.erl b/lib/wx/src/gen/wxEvtHandler.erl
index 22c203392c..44b7254cfb 100644
--- a/lib/wx/src/gen/wxEvtHandler.erl
+++ b/lib/wx/src/gen/wxEvtHandler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -46,15 +46,11 @@
-export([connect/2, connect/3, disconnect/1, disconnect/2, disconnect/3]).
%% internal exports
--export([connect_impl/3, disconnect_impl/2, disconnect_impl/3,
- new_evt_listener/0, destroy_evt_listener/1,
- get_callback/1, replace_fun_with_id/2]).
+-export([connect_impl/2, disconnect_impl/2]).
-export_type([wxEvtHandler/0, wx/0, event/0]).
-type wxEvtHandler() :: wx:wx_object().
--record(evh, {et=null,id=?wxID_ANY,lastId=?wxID_ANY,skip=undefined,userdata=[],cb=0}).
-
%% @doc Equivalent to {@link connect/3. connect(This, EventType, [])}
-spec connect(This::wxEvtHandler(), EventType::wxEventType()) -> ok.
connect(This, EventType) ->
@@ -149,55 +145,35 @@ disconnect(This=#wx_ref{type=ThisT,ref=_ThisRef}, EventType, Opts) ->
%% @hidden
-connect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType,
- skip=Skip, userdata=Userdata, cb=FunID})
+connect_impl(#wx_ref{type=ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ skip=Skip, userdata=Userdata, cb=FunID})
when is_integer(FunID)->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
ThisTypeBin = list_to_binary([atom_to_list(ThisT)|[0]]),
UD = if Userdata =:= [] -> 0;
- true ->
+ true ->
wxe_util:send_bin(term_to_binary(Userdata)),
1
end,
- wxe_util:call(100, <<EvtList:32/?UI,ThisRef:32/?UI,
+ wxe_util:call(100, <<ThisRef:32/?UI,
Winid:32/?UI,LastId:32/?UI,
(wxe_util:from_bool(Skip)):32/?UI,
UD:32/?UI,
FunID:32/?UI,
(size(EventTypeBin)):32/?UI,
- (size(ThisTypeBin)):32/?UI,
+ (size(ThisTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary,ThisTypeBin/binary>>).
%% @hidden
-disconnect_impl(Listener, Object) ->
- disconnect_impl(Listener, Object, #evh{}).
-%% @hidden
-disconnect_impl(#wx_ref{type=wxeEvtListener,ref=EvtList},
- #wx_ref{type=_ThisT,ref=ThisRef},
- #evh{id=Winid, lastId=LastId, et=EventType}) ->
+disconnect_impl(#wx_ref{type=_ThisT,ref=ThisRef},
+ #evh{id=Winid, lastId=LastId, et=EventType,
+ handler=#wx_ref{type=wxeEvtListener,ref=EvtList}}) ->
EventTypeBin = list_to_binary([atom_to_list(EventType)|[0]]),
- wxe_util:call(101, <<EvtList:32/?UI,
+ wxe_util:call(101, <<EvtList:32/?UI,
ThisRef:32/?UI,Winid:32/?UI,LastId:32/?UI,
(size(EventTypeBin)):32/?UI,
%% Note no alignment
EventTypeBin/binary>>).
-%% @hidden
-new_evt_listener() ->
- wxe_util:call(98, <<>>).
-
-%% @hidden
-destroy_evt_listener(#wx_ref{type=wxeEvtListener,ref=EvtList}) ->
- wxe_util:call(99, <<EvtList:32/?UI>>).
-
-%% @hidden
-get_callback(#evh{cb=Callback}) ->
- Callback.
-
-%% @hidden
-replace_fun_with_id(Evh, Id) ->
- Evh#evh{cb=Id}.
-
diff --git a/lib/wx/src/gen/wxGridCellBoolEditor.erl b/lib/wx/src/gen/wxGridCellBoolEditor.erl
index bf7e21d11d..c4d6d92618 100644
--- a/lib/wx/src/gen/wxGridCellBoolEditor.erl
+++ b/lib/wx/src/gen/wxGridCellBoolEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. 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
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellBoolEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellChoiceEditor.erl b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
index 08c5f9e147..a49680ffda 100644
--- a/lib/wx/src/gen/wxGridCellChoiceEditor.erl
+++ b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. 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
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellChoiceEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellFloatEditor.erl b/lib/wx/src/gen/wxGridCellFloatEditor.erl
index 4b6b3b46e1..5cd0bd6cb5 100644
--- a/lib/wx/src/gen/wxGridCellFloatEditor.erl
+++ b/lib/wx/src/gen/wxGridCellFloatEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. 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
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellFloatEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellNumberEditor.erl b/lib/wx/src/gen/wxGridCellNumberEditor.erl
index 04214be6b8..7cc682a10e 100644
--- a/lib/wx/src/gen/wxGridCellNumberEditor.erl
+++ b/lib/wx/src/gen/wxGridCellNumberEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. 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
@@ -35,6 +35,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellNumberEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellTextEditor) -> true;
parent_class(wxGridCellEditor) -> true;
diff --git a/lib/wx/src/gen/wxGridCellTextEditor.erl b/lib/wx/src/gen/wxGridCellTextEditor.erl
index 5755be8638..a024da56c4 100644
--- a/lib/wx/src/gen/wxGridCellTextEditor.erl
+++ b/lib/wx/src/gen/wxGridCellTextEditor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. 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
@@ -34,6 +34,8 @@
parent_class/1,reset/1,setSize/2,show/2,show/3,startingClick/1,startingKey/2]).
-export_type([wxGridCellTextEditor/0]).
+-compile([{nowarn_deprecated_function, {wxGridCellEditor,endEdit,4}},{nowarn_deprecated_function, {wxGridCellEditor,paintBackground,3}}]).
+
%% @hidden
parent_class(wxGridCellEditor) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxLocale.erl b/lib/wx/src/gen/wxLocale.erl
new file mode 100644
index 0000000000..a1a418bcee
--- /dev/null
+++ b/lib/wx/src/gen/wxLocale.erl
@@ -0,0 +1,278 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2014. 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 file is generated DO NOT EDIT
+
+%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html">wxLocale</a>.
+%% @type wxLocale(). An object reference, The representation is internal
+%% and can be changed without notice. It can't be used for comparsion
+%% stored on disc or distributed for use on other nodes.
+
+-module(wxLocale).
+-include("wxe.hrl").
+-export([addCatalog/2,addCatalog/4,addCatalogLookupPathPrefix/1,destroy/1,
+ getCanonicalName/1,getHeaderValue/2,getHeaderValue/3,getLanguage/1,
+ getLanguageName/1,getLocale/1,getName/1,getString/2,getString/3,getString/4,
+ getString/5,getSysName/1,getSystemEncoding/0,getSystemEncodingName/0,
+ getSystemLanguage/0,init/1,init/2,isLoaded/2,isOk/1,new/0,new/1,new/2]).
+
+%% inherited exports
+-export([parent_class/1]).
+
+-export_type([wxLocale/0]).
+%% @hidden
+parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
+
+-type wxLocale() :: wx:wx_object().
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalewxlocale">external documentation</a>.
+-spec new() -> wxLocale().
+new() ->
+ wxe_util:construct(?wxLocale_new_0,
+ <<>>).
+
+%% @equiv new(Language, [])
+-spec new(Language) -> wxLocale() when
+ Language::integer().
+
+new(Language)
+ when is_integer(Language) ->
+ new(Language, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalewxlocale">external documentation</a>.
+-spec new(Language, [Option]) -> wxLocale() when
+ Language::integer(),
+ Option :: {flags, integer()}.
+new(Language, Options)
+ when is_integer(Language),is_list(Options) ->
+ MOpts = fun({flags, Flags}, Acc) -> [<<1:32/?UI,Flags:32/?UI>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:construct(?wxLocale_new_2,
+ <<Language:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @equiv init(This, [])
+-spec init(This) -> boolean() when
+ This::wxLocale().
+
+init(This)
+ when is_record(This, wx_ref) ->
+ init(This, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleinit">external documentation</a>.
+-spec init(This, [Option]) -> boolean() when
+ This::wxLocale(),
+ Option :: {language, integer()}
+ | {flags, integer()}.
+init(#wx_ref{type=ThisT,ref=ThisRef}, Options)
+ when is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ MOpts = fun({language, Language}, Acc) -> [<<1:32/?UI,Language:32/?UI>>|Acc];
+ ({flags, Flags}, Acc) -> [<<2:32/?UI,Flags:32/?UI>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_Init,
+ <<ThisRef:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleaddcatalog">external documentation</a>.
+-spec addCatalog(This, SzDomain) -> boolean() when
+ This::wxLocale(), SzDomain::unicode:chardata().
+addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
+ when is_list(SzDomain) ->
+ ?CLASS(ThisT,wxLocale),
+ SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
+ wxe_util:call(?wxLocale_AddCatalog_1,
+ <<ThisRef:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleaddcatalog">external documentation</a>.
+%%<br /> MsgIdLanguage = ?wxLANGUAGE_DEFAULT | ?wxLANGUAGE_UNKNOWN | ?wxLANGUAGE_ABKHAZIAN | ?wxLANGUAGE_AFAR | ?wxLANGUAGE_AFRIKAANS | ?wxLANGUAGE_ALBANIAN | ?wxLANGUAGE_AMHARIC | ?wxLANGUAGE_ARABIC | ?wxLANGUAGE_ARABIC_ALGERIA | ?wxLANGUAGE_ARABIC_BAHRAIN | ?wxLANGUAGE_ARABIC_EGYPT | ?wxLANGUAGE_ARABIC_IRAQ | ?wxLANGUAGE_ARABIC_JORDAN | ?wxLANGUAGE_ARABIC_KUWAIT | ?wxLANGUAGE_ARABIC_LEBANON | ?wxLANGUAGE_ARABIC_LIBYA | ?wxLANGUAGE_ARABIC_MOROCCO | ?wxLANGUAGE_ARABIC_OMAN | ?wxLANGUAGE_ARABIC_QATAR | ?wxLANGUAGE_ARABIC_SAUDI_ARABIA | ?wxLANGUAGE_ARABIC_SUDAN | ?wxLANGUAGE_ARABIC_SYRIA | ?wxLANGUAGE_ARABIC_TUNISIA | ?wxLANGUAGE_ARABIC_UAE | ?wxLANGUAGE_ARABIC_YEMEN | ?wxLANGUAGE_ARMENIAN | ?wxLANGUAGE_ASSAMESE | ?wxLANGUAGE_AYMARA | ?wxLANGUAGE_AZERI | ?wxLANGUAGE_AZERI_CYRILLIC | ?wxLANGUAGE_AZERI_LATIN | ?wxLANGUAGE_BASHKIR | ?wxLANGUAGE_BASQUE | ?wxLANGUAGE_BELARUSIAN | ?wxLANGUAGE_BENGALI | ?wxLANGUAGE_BHUTANI | ?wxLANGUAGE_BIHARI | ?wxLANGUAGE_BISLAMA | ?wxLANGUAGE_BRETON | ?wxLANGUAGE_BULGARIAN | ?wxLANGUAGE_BURMESE | ?wxLANGUAGE_CAMBODIAN | ?wxLANGUAGE_CATALAN | ?wxLANGUAGE_CHINESE | ?wxLANGUAGE_CHINESE_SIMPLIFIED | ?wxLANGUAGE_CHINESE_TRADITIONAL | ?wxLANGUAGE_CHINESE_HONGKONG | ?wxLANGUAGE_CHINESE_MACAU | ?wxLANGUAGE_CHINESE_SINGAPORE | ?wxLANGUAGE_CHINESE_TAIWAN | ?wxLANGUAGE_CORSICAN | ?wxLANGUAGE_CROATIAN | ?wxLANGUAGE_CZECH | ?wxLANGUAGE_DANISH | ?wxLANGUAGE_DUTCH | ?wxLANGUAGE_DUTCH_BELGIAN | ?wxLANGUAGE_ENGLISH | ?wxLANGUAGE_ENGLISH_UK | ?wxLANGUAGE_ENGLISH_US | ?wxLANGUAGE_ENGLISH_AUSTRALIA | ?wxLANGUAGE_ENGLISH_BELIZE | ?wxLANGUAGE_ENGLISH_BOTSWANA | ?wxLANGUAGE_ENGLISH_CANADA | ?wxLANGUAGE_ENGLISH_CARIBBEAN | ?wxLANGUAGE_ENGLISH_DENMARK | ?wxLANGUAGE_ENGLISH_EIRE | ?wxLANGUAGE_ENGLISH_JAMAICA | ?wxLANGUAGE_ENGLISH_NEW_ZEALAND | ?wxLANGUAGE_ENGLISH_PHILIPPINES | ?wxLANGUAGE_ENGLISH_SOUTH_AFRICA | ?wxLANGUAGE_ENGLISH_TRINIDAD | ?wxLANGUAGE_ENGLISH_ZIMBABWE | ?wxLANGUAGE_ESPERANTO | ?wxLANGUAGE_ESTONIAN | ?wxLANGUAGE_FAEROESE | ?wxLANGUAGE_FARSI | ?wxLANGUAGE_FIJI | ?wxLANGUAGE_FINNISH | ?wxLANGUAGE_FRENCH | ?wxLANGUAGE_FRENCH_BELGIAN | ?wxLANGUAGE_FRENCH_CANADIAN | ?wxLANGUAGE_FRENCH_LUXEMBOURG | ?wxLANGUAGE_FRENCH_MONACO | ?wxLANGUAGE_FRENCH_SWISS | ?wxLANGUAGE_FRISIAN | ?wxLANGUAGE_GALICIAN | ?wxLANGUAGE_GEORGIAN | ?wxLANGUAGE_GERMAN | ?wxLANGUAGE_GERMAN_AUSTRIAN | ?wxLANGUAGE_GERMAN_BELGIUM | ?wxLANGUAGE_GERMAN_LIECHTENSTEIN | ?wxLANGUAGE_GERMAN_LUXEMBOURG | ?wxLANGUAGE_GERMAN_SWISS | ?wxLANGUAGE_GREEK | ?wxLANGUAGE_GREENLANDIC | ?wxLANGUAGE_GUARANI | ?wxLANGUAGE_GUJARATI | ?wxLANGUAGE_HAUSA | ?wxLANGUAGE_HEBREW | ?wxLANGUAGE_HINDI | ?wxLANGUAGE_HUNGARIAN | ?wxLANGUAGE_ICELANDIC | ?wxLANGUAGE_INDONESIAN | ?wxLANGUAGE_INTERLINGUA | ?wxLANGUAGE_INTERLINGUE | ?wxLANGUAGE_INUKTITUT | ?wxLANGUAGE_INUPIAK | ?wxLANGUAGE_IRISH | ?wxLANGUAGE_ITALIAN | ?wxLANGUAGE_ITALIAN_SWISS | ?wxLANGUAGE_JAPANESE | ?wxLANGUAGE_JAVANESE | ?wxLANGUAGE_KANNADA | ?wxLANGUAGE_KASHMIRI | ?wxLANGUAGE_KASHMIRI_INDIA | ?wxLANGUAGE_KAZAKH | ?wxLANGUAGE_KERNEWEK | ?wxLANGUAGE_KINYARWANDA | ?wxLANGUAGE_KIRGHIZ | ?wxLANGUAGE_KIRUNDI | ?wxLANGUAGE_KONKANI | ?wxLANGUAGE_KOREAN | ?wxLANGUAGE_KURDISH | ?wxLANGUAGE_LAOTHIAN | ?wxLANGUAGE_LATIN | ?wxLANGUAGE_LATVIAN | ?wxLANGUAGE_LINGALA | ?wxLANGUAGE_LITHUANIAN | ?wxLANGUAGE_MACEDONIAN | ?wxLANGUAGE_MALAGASY | ?wxLANGUAGE_MALAY | ?wxLANGUAGE_MALAYALAM | ?wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM | ?wxLANGUAGE_MALAY_MALAYSIA | ?wxLANGUAGE_MALTESE | ?wxLANGUAGE_MANIPURI | ?wxLANGUAGE_MAORI | ?wxLANGUAGE_MARATHI | ?wxLANGUAGE_MOLDAVIAN | ?wxLANGUAGE_MONGOLIAN | ?wxLANGUAGE_NAURU | ?wxLANGUAGE_NEPALI | ?wxLANGUAGE_NEPALI_INDIA | ?wxLANGUAGE_NORWEGIAN_BOKMAL | ?wxLANGUAGE_NORWEGIAN_NYNORSK | ?wxLANGUAGE_OCCITAN | ?wxLANGUAGE_ORIYA | ?wxLANGUAGE_OROMO | ?wxLANGUAGE_PASHTO | ?wxLANGUAGE_POLISH | ?wxLANGUAGE_PORTUGUESE | ?wxLANGUAGE_PORTUGUESE_BRAZILIAN | ?wxLANGUAGE_PUNJABI | ?wxLANGUAGE_QUECHUA | ?wxLANGUAGE_RHAETO_ROMANCE | ?wxLANGUAGE_ROMANIAN | ?wxLANGUAGE_RUSSIAN | ?wxLANGUAGE_RUSSIAN_UKRAINE | ?wxLANGUAGE_SAMOAN | ?wxLANGUAGE_SANGHO | ?wxLANGUAGE_SANSKRIT | ?wxLANGUAGE_SCOTS_GAELIC | ?wxLANGUAGE_SERBIAN | ?wxLANGUAGE_SERBIAN_CYRILLIC | ?wxLANGUAGE_SERBIAN_LATIN | ?wxLANGUAGE_SERBO_CROATIAN | ?wxLANGUAGE_SESOTHO | ?wxLANGUAGE_SETSWANA | ?wxLANGUAGE_SHONA | ?wxLANGUAGE_SINDHI | ?wxLANGUAGE_SINHALESE | ?wxLANGUAGE_SISWATI | ?wxLANGUAGE_SLOVAK | ?wxLANGUAGE_SLOVENIAN | ?wxLANGUAGE_SOMALI | ?wxLANGUAGE_SPANISH | ?wxLANGUAGE_SPANISH_ARGENTINA | ?wxLANGUAGE_SPANISH_BOLIVIA | ?wxLANGUAGE_SPANISH_CHILE | ?wxLANGUAGE_SPANISH_COLOMBIA | ?wxLANGUAGE_SPANISH_COSTA_RICA | ?wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC | ?wxLANGUAGE_SPANISH_ECUADOR | ?wxLANGUAGE_SPANISH_EL_SALVADOR | ?wxLANGUAGE_SPANISH_GUATEMALA | ?wxLANGUAGE_SPANISH_HONDURAS | ?wxLANGUAGE_SPANISH_MEXICAN | ?wxLANGUAGE_SPANISH_MODERN | ?wxLANGUAGE_SPANISH_NICARAGUA | ?wxLANGUAGE_SPANISH_PANAMA | ?wxLANGUAGE_SPANISH_PARAGUAY | ?wxLANGUAGE_SPANISH_PERU | ?wxLANGUAGE_SPANISH_PUERTO_RICO | ?wxLANGUAGE_SPANISH_URUGUAY | ?wxLANGUAGE_SPANISH_US | ?wxLANGUAGE_SPANISH_VENEZUELA | ?wxLANGUAGE_SUNDANESE | ?wxLANGUAGE_SWAHILI | ?wxLANGUAGE_SWEDISH | ?wxLANGUAGE_SWEDISH_FINLAND | ?wxLANGUAGE_TAGALOG | ?wxLANGUAGE_TAJIK | ?wxLANGUAGE_TAMIL | ?wxLANGUAGE_TATAR | ?wxLANGUAGE_TELUGU | ?wxLANGUAGE_THAI | ?wxLANGUAGE_TIBETAN | ?wxLANGUAGE_TIGRINYA | ?wxLANGUAGE_TONGA | ?wxLANGUAGE_TSONGA | ?wxLANGUAGE_TURKISH | ?wxLANGUAGE_TURKMEN | ?wxLANGUAGE_TWI | ?wxLANGUAGE_UIGHUR | ?wxLANGUAGE_UKRAINIAN | ?wxLANGUAGE_URDU | ?wxLANGUAGE_URDU_INDIA | ?wxLANGUAGE_URDU_PAKISTAN | ?wxLANGUAGE_UZBEK | ?wxLANGUAGE_UZBEK_CYRILLIC | ?wxLANGUAGE_UZBEK_LATIN | ?wxLANGUAGE_VIETNAMESE | ?wxLANGUAGE_VOLAPUK | ?wxLANGUAGE_WELSH | ?wxLANGUAGE_WOLOF | ?wxLANGUAGE_XHOSA | ?wxLANGUAGE_YIDDISH | ?wxLANGUAGE_YORUBA | ?wxLANGUAGE_ZHUANG | ?wxLANGUAGE_ZULU | ?wxLANGUAGE_USER_DEFINED | ?wxLANGUAGE_VALENCIAN | ?wxLANGUAGE_SAMI
+-spec addCatalog(This, SzDomain, MsgIdLanguage, MsgIdCharset) -> boolean() when
+ This::wxLocale(), SzDomain::unicode:chardata(), MsgIdLanguage::wx:wx_enum(), MsgIdCharset::unicode:chardata().
+addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain,MsgIdLanguage,MsgIdCharset)
+ when is_list(SzDomain),is_integer(MsgIdLanguage),is_list(MsgIdCharset) ->
+ ?CLASS(ThisT,wxLocale),
+ SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
+ MsgIdCharset_UC = unicode:characters_to_binary([MsgIdCharset,0]),
+ wxe_util:call(?wxLocale_AddCatalog_3,
+ <<ThisRef:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8,MsgIdLanguage:32/?UI,(byte_size(MsgIdCharset_UC)):32/?UI,(MsgIdCharset_UC)/binary, 0:(((8- ((0+byte_size(MsgIdCharset_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleaddcataloglookuppathprefix">external documentation</a>.
+-spec addCatalogLookupPathPrefix(Prefix) -> ok when
+ Prefix::unicode:chardata().
+addCatalogLookupPathPrefix(Prefix)
+ when is_list(Prefix) ->
+ Prefix_UC = unicode:characters_to_binary([Prefix,0]),
+ wxe_util:cast(?wxLocale_AddCatalogLookupPathPrefix,
+ <<(byte_size(Prefix_UC)):32/?UI,(Prefix_UC)/binary, 0:(((8- ((4+byte_size(Prefix_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetcanonicalname">external documentation</a>.
+-spec getCanonicalName(This) -> unicode:charlist() when
+ This::wxLocale().
+getCanonicalName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetCanonicalName,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetlanguage">external documentation</a>.
+-spec getLanguage(This) -> integer() when
+ This::wxLocale().
+getLanguage(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetLanguage,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetlanguagename">external documentation</a>.
+-spec getLanguageName(Lang) -> unicode:charlist() when
+ Lang::integer().
+getLanguageName(Lang)
+ when is_integer(Lang) ->
+ wxe_util:call(?wxLocale_GetLanguageName,
+ <<Lang:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetlocale">external documentation</a>.
+-spec getLocale(This) -> unicode:charlist() when
+ This::wxLocale().
+getLocale(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetLocale,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetname">external documentation</a>.
+-spec getName(This) -> unicode:charlist() when
+ This::wxLocale().
+getName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetName,
+ <<ThisRef:32/?UI>>).
+
+%% @equiv getString(This,SzOrigString, [])
+-spec getString(This, SzOrigString) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata().
+
+getString(This,SzOrigString)
+ when is_record(This, wx_ref),is_list(SzOrigString) ->
+ getString(This,SzOrigString, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetstring">external documentation</a>.
+-spec getString(This, SzOrigString, [Option]) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata(),
+ Option :: {szDomain, unicode:chardata()}.
+getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString, Options)
+ when is_list(SzOrigString),is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ SzOrigString_UC = unicode:characters_to_binary([SzOrigString,0]),
+ MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_GetString_2,
+ <<ThisRef:32/?UI,(byte_size(SzOrigString_UC)):32/?UI,(SzOrigString_UC)/binary, 0:(((8- ((0+byte_size(SzOrigString_UC)) band 16#7)) band 16#7))/unit:8, BinOpt/binary>>).
+
+%% @equiv getString(This,SzOrigString,SzOrigString2,N, [])
+-spec getString(This, SzOrigString, SzOrigString2, N) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata(), SzOrigString2::unicode:chardata(), N::integer().
+
+getString(This,SzOrigString,SzOrigString2,N)
+ when is_record(This, wx_ref),is_list(SzOrigString),is_list(SzOrigString2),is_integer(N) ->
+ getString(This,SzOrigString,SzOrigString2,N, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetstring">external documentation</a>.
+-spec getString(This, SzOrigString, SzOrigString2, N, [Option]) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata(), SzOrigString2::unicode:chardata(), N::integer(),
+ Option :: {szDomain, unicode:chardata()}.
+getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString,SzOrigString2,N, Options)
+ when is_list(SzOrigString),is_list(SzOrigString2),is_integer(N),is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ SzOrigString_UC = unicode:characters_to_binary([SzOrigString,0]),
+ SzOrigString2_UC = unicode:characters_to_binary([SzOrigString2,0]),
+ MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_GetString_4,
+ <<ThisRef:32/?UI,(byte_size(SzOrigString_UC)):32/?UI,(SzOrigString_UC)/binary, 0:(((8- ((0+byte_size(SzOrigString_UC)) band 16#7)) band 16#7))/unit:8,(byte_size(SzOrigString2_UC)):32/?UI,(SzOrigString2_UC)/binary, 0:(((8- ((4+byte_size(SzOrigString2_UC)) band 16#7)) band 16#7))/unit:8,N:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @equiv getHeaderValue(This,SzHeader, [])
+-spec getHeaderValue(This, SzHeader) -> unicode:charlist() when
+ This::wxLocale(), SzHeader::unicode:chardata().
+
+getHeaderValue(This,SzHeader)
+ when is_record(This, wx_ref),is_list(SzHeader) ->
+ getHeaderValue(This,SzHeader, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetheadervalue">external documentation</a>.
+-spec getHeaderValue(This, SzHeader, [Option]) -> unicode:charlist() when
+ This::wxLocale(), SzHeader::unicode:chardata(),
+ Option :: {szDomain, unicode:chardata()}.
+getHeaderValue(#wx_ref{type=ThisT,ref=ThisRef},SzHeader, Options)
+ when is_list(SzHeader),is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ SzHeader_UC = unicode:characters_to_binary([SzHeader,0]),
+ MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_GetHeaderValue,
+ <<ThisRef:32/?UI,(byte_size(SzHeader_UC)):32/?UI,(SzHeader_UC)/binary, 0:(((8- ((0+byte_size(SzHeader_UC)) band 16#7)) band 16#7))/unit:8, BinOpt/binary>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsysname">external documentation</a>.
+-spec getSysName(This) -> unicode:charlist() when
+ This::wxLocale().
+getSysName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetSysName,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsystemencoding">external documentation</a>.
+%%<br /> Res = ?wxFONTENCODING_SYSTEM | ?wxFONTENCODING_DEFAULT | ?wxFONTENCODING_ISO8859_1 | ?wxFONTENCODING_ISO8859_2 | ?wxFONTENCODING_ISO8859_3 | ?wxFONTENCODING_ISO8859_4 | ?wxFONTENCODING_ISO8859_5 | ?wxFONTENCODING_ISO8859_6 | ?wxFONTENCODING_ISO8859_7 | ?wxFONTENCODING_ISO8859_8 | ?wxFONTENCODING_ISO8859_9 | ?wxFONTENCODING_ISO8859_10 | ?wxFONTENCODING_ISO8859_11 | ?wxFONTENCODING_ISO8859_12 | ?wxFONTENCODING_ISO8859_13 | ?wxFONTENCODING_ISO8859_14 | ?wxFONTENCODING_ISO8859_15 | ?wxFONTENCODING_ISO8859_MAX | ?wxFONTENCODING_KOI8 | ?wxFONTENCODING_KOI8_U | ?wxFONTENCODING_ALTERNATIVE | ?wxFONTENCODING_BULGARIAN | ?wxFONTENCODING_CP437 | ?wxFONTENCODING_CP850 | ?wxFONTENCODING_CP852 | ?wxFONTENCODING_CP855 | ?wxFONTENCODING_CP866 | ?wxFONTENCODING_CP874 | ?wxFONTENCODING_CP932 | ?wxFONTENCODING_CP936 | ?wxFONTENCODING_CP949 | ?wxFONTENCODING_CP950 | ?wxFONTENCODING_CP1250 | ?wxFONTENCODING_CP1251 | ?wxFONTENCODING_CP1252 | ?wxFONTENCODING_CP1253 | ?wxFONTENCODING_CP1254 | ?wxFONTENCODING_CP1255 | ?wxFONTENCODING_CP1256 | ?wxFONTENCODING_CP1257 | ?wxFONTENCODING_CP12_MAX | ?wxFONTENCODING_UTF7 | ?wxFONTENCODING_UTF8 | ?wxFONTENCODING_EUC_JP | ?wxFONTENCODING_UTF16BE | ?wxFONTENCODING_UTF16LE | ?wxFONTENCODING_UTF32BE | ?wxFONTENCODING_UTF32LE | ?wxFONTENCODING_MACROMAN | ?wxFONTENCODING_MACJAPANESE | ?wxFONTENCODING_MACCHINESETRAD | ?wxFONTENCODING_MACKOREAN | ?wxFONTENCODING_MACARABIC | ?wxFONTENCODING_MACHEBREW | ?wxFONTENCODING_MACGREEK | ?wxFONTENCODING_MACCYRILLIC | ?wxFONTENCODING_MACDEVANAGARI | ?wxFONTENCODING_MACGURMUKHI | ?wxFONTENCODING_MACGUJARATI | ?wxFONTENCODING_MACORIYA | ?wxFONTENCODING_MACBENGALI | ?wxFONTENCODING_MACTAMIL | ?wxFONTENCODING_MACTELUGU | ?wxFONTENCODING_MACKANNADA | ?wxFONTENCODING_MACMALAJALAM | ?wxFONTENCODING_MACSINHALESE | ?wxFONTENCODING_MACBURMESE | ?wxFONTENCODING_MACKHMER | ?wxFONTENCODING_MACTHAI | ?wxFONTENCODING_MACLAOTIAN | ?wxFONTENCODING_MACGEORGIAN | ?wxFONTENCODING_MACARMENIAN | ?wxFONTENCODING_MACCHINESESIMP | ?wxFONTENCODING_MACTIBETAN | ?wxFONTENCODING_MACMONGOLIAN | ?wxFONTENCODING_MACETHIOPIC | ?wxFONTENCODING_MACCENTRALEUR | ?wxFONTENCODING_MACVIATNAMESE | ?wxFONTENCODING_MACARABICEXT | ?wxFONTENCODING_MACSYMBOL | ?wxFONTENCODING_MACDINGBATS | ?wxFONTENCODING_MACTURKISH | ?wxFONTENCODING_MACCROATIAN | ?wxFONTENCODING_MACICELANDIC | ?wxFONTENCODING_MACROMANIAN | ?wxFONTENCODING_MACCELTIC | ?wxFONTENCODING_MACGAELIC | ?wxFONTENCODING_MACKEYBOARD | ?wxFONTENCODING_MAX | ?wxFONTENCODING_MACMIN | ?wxFONTENCODING_MACMAX | ?wxFONTENCODING_UTF16 | ?wxFONTENCODING_UTF32 | ?wxFONTENCODING_UNICODE | ?wxFONTENCODING_GB2312 | ?wxFONTENCODING_BIG5 | ?wxFONTENCODING_SHIFT_JIS
+-spec getSystemEncoding() -> wx:wx_enum().
+getSystemEncoding() ->
+ wxe_util:call(?wxLocale_GetSystemEncoding,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsystemencodingname">external documentation</a>.
+-spec getSystemEncodingName() -> unicode:charlist().
+getSystemEncodingName() ->
+ wxe_util:call(?wxLocale_GetSystemEncodingName,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsystemlanguage">external documentation</a>.
+-spec getSystemLanguage() -> integer().
+getSystemLanguage() ->
+ wxe_util:call(?wxLocale_GetSystemLanguage,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleisloaded">external documentation</a>.
+-spec isLoaded(This, SzDomain) -> boolean() when
+ This::wxLocale(), SzDomain::unicode:chardata().
+isLoaded(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
+ when is_list(SzDomain) ->
+ ?CLASS(ThisT,wxLocale),
+ SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
+ wxe_util:call(?wxLocale_IsLoaded,
+ <<ThisRef:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleisok">external documentation</a>.
+-spec isOk(This) -> boolean() when
+ This::wxLocale().
+isOk(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_IsOk,
+ <<ThisRef:32/?UI>>).
+
+%% @doc Destroys this object, do not use object again
+-spec destroy(This::wxLocale()) -> ok.
+destroy(Obj=#wx_ref{type=Type}) ->
+ ?CLASS(Type,wxLocale),
+ wxe_util:destroy(?wxLocale_destruct,Obj),
+ ok.
diff --git a/lib/wx/src/gen/wxMemoryDC.erl b/lib/wx/src/gen/wxMemoryDC.erl
index c123f0e35d..8de412bdc7 100644
--- a/lib/wx/src/gen/wxMemoryDC.erl
+++ b/lib/wx/src/gen/wxMemoryDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxMemoryDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxMirrorDC.erl b/lib/wx/src/gen/wxMirrorDC.erl
index cfae34cb36..9a681bff2e 100644
--- a/lib/wx/src/gen/wxMirrorDC.erl
+++ b/lib/wx/src/gen/wxMirrorDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxMirrorDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxPaintDC.erl b/lib/wx/src/gen/wxPaintDC.erl
index 3c9b321496..0ff27a8c7a 100644
--- a/lib/wx/src/gen/wxPaintDC.erl
+++ b/lib/wx/src/gen/wxPaintDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -56,6 +56,8 @@
-export_type([wxPaintDC/0]).
-deprecated([new/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxWindowDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxPostScriptDC.erl b/lib/wx/src/gen/wxPostScriptDC.erl
index e0b22c87eb..e7e498efa1 100644
--- a/lib/wx/src/gen/wxPostScriptDC.erl
+++ b/lib/wx/src/gen/wxPostScriptDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -55,6 +55,8 @@
-export_type([wxPostScriptDC/0]).
-deprecated([getResolution/0,setResolution/1]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxScreenDC.erl b/lib/wx/src/gen/wxScreenDC.erl
index 54bdc2d351..21ca4bacfc 100644
--- a/lib/wx/src/gen/wxScreenDC.erl
+++ b/lib/wx/src/gen/wxScreenDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxScreenDC/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxWindowDC.erl b/lib/wx/src/gen/wxWindowDC.erl
index f5c482d8ca..6bb303cfe6 100644
--- a/lib/wx/src/gen/wxWindowDC.erl
+++ b/lib/wx/src/gen/wxWindowDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -55,6 +55,8 @@
-export_type([wxWindowDC/0]).
-deprecated([new/0]).
+-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wx_misc.erl b/lib/wx/src/gen/wx_misc.erl
index 96912ce651..397d48b727 100644
--- a/lib/wx/src/gen/wx_misc.erl
+++ b/lib/wx/src/gen/wx_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -30,6 +30,8 @@
launchDefaultBrowser/2,newId/0,registerId/1,setDetectableAutoRepeat/1,
shell/0,shell/1,shutdown/1]).
+-export([displaySize/0,setCursor/1]).
+
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_miscellany.html#wxgetkeystate">external documentation</a>.
%%<br /> Key = ?WXK_BACK | ?WXK_TAB | ?WXK_RETURN | ?WXK_ESCAPE | ?WXK_SPACE | ?WXK_DELETE | ?WXK_START | ?WXK_LBUTTON | ?WXK_RBUTTON | ?WXK_CANCEL | ?WXK_MBUTTON | ?WXK_CLEAR | ?WXK_SHIFT | ?WXK_ALT | ?WXK_CONTROL | ?WXK_MENU | ?WXK_PAUSE | ?WXK_CAPITAL | ?WXK_END | ?WXK_HOME | ?WXK_LEFT | ?WXK_UP | ?WXK_RIGHT | ?WXK_DOWN | ?WXK_SELECT | ?WXK_PRINT | ?WXK_EXECUTE | ?WXK_SNAPSHOT | ?WXK_INSERT | ?WXK_HELP | ?WXK_NUMPAD0 | ?WXK_NUMPAD1 | ?WXK_NUMPAD2 | ?WXK_NUMPAD3 | ?WXK_NUMPAD4 | ?WXK_NUMPAD5 | ?WXK_NUMPAD6 | ?WXK_NUMPAD7 | ?WXK_NUMPAD8 | ?WXK_NUMPAD9 | ?WXK_MULTIPLY | ?WXK_ADD | ?WXK_SEPARATOR | ?WXK_SUBTRACT | ?WXK_DECIMAL | ?WXK_DIVIDE | ?WXK_F1 | ?WXK_F2 | ?WXK_F3 | ?WXK_F4 | ?WXK_F5 | ?WXK_F6 | ?WXK_F7 | ?WXK_F8 | ?WXK_F9 | ?WXK_F10 | ?WXK_F11 | ?WXK_F12 | ?WXK_F13 | ?WXK_F14 | ?WXK_F15 | ?WXK_F16 | ?WXK_F17 | ?WXK_F18 | ?WXK_F19 | ?WXK_F20 | ?WXK_F21 | ?WXK_F22 | ?WXK_F23 | ?WXK_F24 | ?WXK_NUMLOCK | ?WXK_SCROLL | ?WXK_PAGEUP | ?WXK_PAGEDOWN | ?WXK_NUMPAD_SPACE | ?WXK_NUMPAD_TAB | ?WXK_NUMPAD_ENTER | ?WXK_NUMPAD_F1 | ?WXK_NUMPAD_F2 | ?WXK_NUMPAD_F3 | ?WXK_NUMPAD_F4 | ?WXK_NUMPAD_HOME | ?WXK_NUMPAD_LEFT | ?WXK_NUMPAD_UP | ?WXK_NUMPAD_RIGHT | ?WXK_NUMPAD_DOWN | ?WXK_NUMPAD_PAGEUP | ?WXK_NUMPAD_PAGEDOWN | ?WXK_NUMPAD_END | ?WXK_NUMPAD_BEGIN | ?WXK_NUMPAD_INSERT | ?WXK_NUMPAD_DELETE | ?WXK_NUMPAD_EQUAL | ?WXK_NUMPAD_MULTIPLY | ?WXK_NUMPAD_ADD | ?WXK_NUMPAD_SEPARATOR | ?WXK_NUMPAD_SUBTRACT | ?WXK_NUMPAD_DECIMAL | ?WXK_NUMPAD_DIVIDE | ?WXK_WINDOWS_LEFT | ?WXK_WINDOWS_RIGHT | ?WXK_WINDOWS_MENU | ?WXK_COMMAND | ?WXK_SPECIAL1 | ?WXK_SPECIAL2 | ?WXK_SPECIAL3 | ?WXK_SPECIAL4 | ?WXK_SPECIAL5 | ?WXK_SPECIAL6 | ?WXK_SPECIAL7 | ?WXK_SPECIAL8 | ?WXK_SPECIAL9 | ?WXK_SPECIAL10 | ?WXK_SPECIAL11 | ?WXK_SPECIAL12 | ?WXK_SPECIAL13 | ?WXK_SPECIAL14 | ?WXK_SPECIAL15 | ?WXK_SPECIAL16 | ?WXK_SPECIAL17 | ?WXK_SPECIAL18 | ?WXK_SPECIAL19 | ?WXK_SPECIAL20
-spec getKeyState(Key) -> boolean() when
@@ -224,3 +226,17 @@ isPlatform64Bit() ->
wxe_util:call(?utils_wxIsPlatform64Bit,
<<>>).
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_gdicmn.html#gdicmnwxdisplaysize">external documentation</a>.
+-spec displaySize() -> {Width::integer(), Height::integer()}.
+displaySize() ->
+ wxe_util:call(?gdicmn_wxDisplaySize,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_gdicmn.html#gdicmnwxsetcursor">external documentation</a>.
+-spec setCursor(Cursor) -> ok when
+ Cursor::wxCursor:wxCursor().
+setCursor(#wx_ref{type=CursorT,ref=CursorRef}) ->
+ ?CLASS(CursorT,wxCursor),
+ wxe_util:cast(?gdicmn_wxSetCursor,
+ <<CursorRef:32/?UI>>).
+
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index 1f4f22f704..e208b081e8 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -2709,602 +2709,625 @@ wxdebug_table() ->
{2924, {utils, getOsDescription, 0}},
{2925, {utils, isPlatformLittleEndian, 0}},
{2926, {utils, isPlatform64Bit, 0}},
- {2927, {wxPrintout, new, 1}},
- {2928, {wxPrintout, destruct, 0}},
- {2929, {wxPrintout, getDC, 0}},
- {2930, {wxPrintout, getPageSizeMM, 2}},
- {2931, {wxPrintout, getPageSizePixels, 2}},
- {2932, {wxPrintout, getPaperRectPixels, 0}},
- {2933, {wxPrintout, getPPIPrinter, 2}},
- {2934, {wxPrintout, getPPIScreen, 2}},
- {2935, {wxPrintout, getTitle, 0}},
- {2936, {wxPrintout, isPreview, 0}},
- {2937, {wxPrintout, fitThisSizeToPaper, 1}},
- {2938, {wxPrintout, fitThisSizeToPage, 1}},
- {2939, {wxPrintout, fitThisSizeToPageMargins, 2}},
- {2940, {wxPrintout, mapScreenSizeToPaper, 0}},
- {2941, {wxPrintout, mapScreenSizeToPage, 0}},
- {2942, {wxPrintout, mapScreenSizeToPageMargins, 1}},
- {2943, {wxPrintout, mapScreenSizeToDevice, 0}},
- {2944, {wxPrintout, getLogicalPaperRect, 0}},
- {2945, {wxPrintout, getLogicalPageRect, 0}},
- {2946, {wxPrintout, getLogicalPageMarginsRect, 1}},
- {2947, {wxPrintout, setLogicalOrigin, 2}},
- {2948, {wxPrintout, offsetLogicalOrigin, 2}},
- {2949, {wxStyledTextCtrl, new_2, 2}},
- {2950, {wxStyledTextCtrl, new_0, 0}},
- {2951, {wxStyledTextCtrl, destruct, 0}},
- {2952, {wxStyledTextCtrl, create, 2}},
- {2953, {wxStyledTextCtrl, addText, 1}},
- {2954, {wxStyledTextCtrl, addStyledText, 1}},
- {2955, {wxStyledTextCtrl, insertText, 2}},
- {2956, {wxStyledTextCtrl, clearAll, 0}},
- {2957, {wxStyledTextCtrl, clearDocumentStyle, 0}},
- {2958, {wxStyledTextCtrl, getLength, 0}},
- {2959, {wxStyledTextCtrl, getCharAt, 1}},
- {2960, {wxStyledTextCtrl, getCurrentPos, 0}},
- {2961, {wxStyledTextCtrl, getAnchor, 0}},
- {2962, {wxStyledTextCtrl, getStyleAt, 1}},
- {2963, {wxStyledTextCtrl, redo, 0}},
- {2964, {wxStyledTextCtrl, setUndoCollection, 1}},
- {2965, {wxStyledTextCtrl, selectAll, 0}},
- {2966, {wxStyledTextCtrl, setSavePoint, 0}},
- {2967, {wxStyledTextCtrl, getStyledText, 2}},
- {2968, {wxStyledTextCtrl, canRedo, 0}},
- {2969, {wxStyledTextCtrl, markerLineFromHandle, 1}},
- {2970, {wxStyledTextCtrl, markerDeleteHandle, 1}},
- {2971, {wxStyledTextCtrl, getUndoCollection, 0}},
- {2972, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
- {2973, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
- {2974, {wxStyledTextCtrl, positionFromPoint, 1}},
- {2975, {wxStyledTextCtrl, positionFromPointClose, 2}},
- {2976, {wxStyledTextCtrl, gotoLine, 1}},
- {2977, {wxStyledTextCtrl, gotoPos, 1}},
- {2978, {wxStyledTextCtrl, setAnchor, 1}},
- {2979, {wxStyledTextCtrl, getCurLine, 1}},
- {2980, {wxStyledTextCtrl, getEndStyled, 0}},
- {2981, {wxStyledTextCtrl, convertEOLs, 1}},
- {2982, {wxStyledTextCtrl, getEOLMode, 0}},
- {2983, {wxStyledTextCtrl, setEOLMode, 1}},
- {2984, {wxStyledTextCtrl, startStyling, 2}},
- {2985, {wxStyledTextCtrl, setStyling, 2}},
- {2986, {wxStyledTextCtrl, getBufferedDraw, 0}},
- {2987, {wxStyledTextCtrl, setBufferedDraw, 1}},
- {2988, {wxStyledTextCtrl, setTabWidth, 1}},
- {2989, {wxStyledTextCtrl, getTabWidth, 0}},
- {2990, {wxStyledTextCtrl, setCodePage, 1}},
- {2991, {wxStyledTextCtrl, markerDefine, 3}},
- {2992, {wxStyledTextCtrl, markerSetForeground, 2}},
- {2993, {wxStyledTextCtrl, markerSetBackground, 2}},
- {2994, {wxStyledTextCtrl, markerAdd, 2}},
- {2995, {wxStyledTextCtrl, markerDelete, 2}},
- {2996, {wxStyledTextCtrl, markerDeleteAll, 1}},
- {2997, {wxStyledTextCtrl, markerGet, 1}},
- {2998, {wxStyledTextCtrl, markerNext, 2}},
- {2999, {wxStyledTextCtrl, markerPrevious, 2}},
- {3000, {wxStyledTextCtrl, markerDefineBitmap, 2}},
- {3001, {wxStyledTextCtrl, markerAddSet, 2}},
- {3002, {wxStyledTextCtrl, markerSetAlpha, 2}},
- {3003, {wxStyledTextCtrl, setMarginType, 2}},
- {3004, {wxStyledTextCtrl, getMarginType, 1}},
- {3005, {wxStyledTextCtrl, setMarginWidth, 2}},
- {3006, {wxStyledTextCtrl, getMarginWidth, 1}},
- {3007, {wxStyledTextCtrl, setMarginMask, 2}},
- {3008, {wxStyledTextCtrl, getMarginMask, 1}},
- {3009, {wxStyledTextCtrl, setMarginSensitive, 2}},
- {3010, {wxStyledTextCtrl, getMarginSensitive, 1}},
- {3011, {wxStyledTextCtrl, styleClearAll, 0}},
- {3012, {wxStyledTextCtrl, styleSetForeground, 2}},
- {3013, {wxStyledTextCtrl, styleSetBackground, 2}},
- {3014, {wxStyledTextCtrl, styleSetBold, 2}},
- {3015, {wxStyledTextCtrl, styleSetItalic, 2}},
- {3016, {wxStyledTextCtrl, styleSetSize, 2}},
- {3017, {wxStyledTextCtrl, styleSetFaceName, 2}},
- {3018, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
- {3019, {wxStyledTextCtrl, styleResetDefault, 0}},
- {3020, {wxStyledTextCtrl, styleSetUnderline, 2}},
- {3021, {wxStyledTextCtrl, styleSetCase, 2}},
- {3022, {wxStyledTextCtrl, styleSetHotSpot, 2}},
- {3023, {wxStyledTextCtrl, setSelForeground, 2}},
- {3024, {wxStyledTextCtrl, setSelBackground, 2}},
- {3025, {wxStyledTextCtrl, getSelAlpha, 0}},
- {3026, {wxStyledTextCtrl, setSelAlpha, 1}},
- {3027, {wxStyledTextCtrl, setCaretForeground, 1}},
- {3028, {wxStyledTextCtrl, cmdKeyAssign, 3}},
- {3029, {wxStyledTextCtrl, cmdKeyClear, 2}},
- {3030, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
- {3031, {wxStyledTextCtrl, setStyleBytes, 2}},
- {3032, {wxStyledTextCtrl, styleSetVisible, 2}},
- {3033, {wxStyledTextCtrl, getCaretPeriod, 0}},
- {3034, {wxStyledTextCtrl, setCaretPeriod, 1}},
- {3035, {wxStyledTextCtrl, setWordChars, 1}},
- {3036, {wxStyledTextCtrl, beginUndoAction, 0}},
- {3037, {wxStyledTextCtrl, endUndoAction, 0}},
- {3038, {wxStyledTextCtrl, indicatorSetStyle, 2}},
- {3039, {wxStyledTextCtrl, indicatorGetStyle, 1}},
- {3040, {wxStyledTextCtrl, indicatorSetForeground, 2}},
- {3041, {wxStyledTextCtrl, indicatorGetForeground, 1}},
- {3042, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
- {3043, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
- {3044, {wxStyledTextCtrl, getStyleBits, 0}},
- {3045, {wxStyledTextCtrl, setLineState, 2}},
- {3046, {wxStyledTextCtrl, getLineState, 1}},
- {3047, {wxStyledTextCtrl, getMaxLineState, 0}},
- {3048, {wxStyledTextCtrl, getCaretLineVisible, 0}},
- {3049, {wxStyledTextCtrl, setCaretLineVisible, 1}},
- {3050, {wxStyledTextCtrl, getCaretLineBackground, 0}},
- {3051, {wxStyledTextCtrl, setCaretLineBackground, 1}},
- {3052, {wxStyledTextCtrl, autoCompShow, 2}},
- {3053, {wxStyledTextCtrl, autoCompCancel, 0}},
- {3054, {wxStyledTextCtrl, autoCompActive, 0}},
- {3055, {wxStyledTextCtrl, autoCompPosStart, 0}},
- {3056, {wxStyledTextCtrl, autoCompComplete, 0}},
- {3057, {wxStyledTextCtrl, autoCompStops, 1}},
- {3058, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
- {3059, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
- {3060, {wxStyledTextCtrl, autoCompSelect, 1}},
- {3061, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
- {3062, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
- {3063, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
- {3064, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
- {3065, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
- {3066, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
- {3067, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
- {3068, {wxStyledTextCtrl, userListShow, 2}},
- {3069, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
- {3070, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
- {3071, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
- {3072, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
- {3073, {wxStyledTextCtrl, registerImage, 2}},
- {3074, {wxStyledTextCtrl, clearRegisteredImages, 0}},
- {3075, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
- {3076, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
- {3077, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
- {3078, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
- {3079, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
- {3080, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
- {3081, {wxStyledTextCtrl, setIndent, 1}},
- {3082, {wxStyledTextCtrl, getIndent, 0}},
- {3083, {wxStyledTextCtrl, setUseTabs, 1}},
- {3084, {wxStyledTextCtrl, getUseTabs, 0}},
- {3085, {wxStyledTextCtrl, setLineIndentation, 2}},
- {3086, {wxStyledTextCtrl, getLineIndentation, 1}},
- {3087, {wxStyledTextCtrl, getLineIndentPosition, 1}},
- {3088, {wxStyledTextCtrl, getColumn, 1}},
- {3089, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
- {3090, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
- {3091, {wxStyledTextCtrl, setIndentationGuides, 1}},
- {3092, {wxStyledTextCtrl, getIndentationGuides, 0}},
- {3093, {wxStyledTextCtrl, setHighlightGuide, 1}},
- {3094, {wxStyledTextCtrl, getHighlightGuide, 0}},
- {3095, {wxStyledTextCtrl, getLineEndPosition, 1}},
- {3096, {wxStyledTextCtrl, getCodePage, 0}},
- {3097, {wxStyledTextCtrl, getCaretForeground, 0}},
- {3098, {wxStyledTextCtrl, getReadOnly, 0}},
- {3099, {wxStyledTextCtrl, setCurrentPos, 1}},
- {3100, {wxStyledTextCtrl, setSelectionStart, 1}},
- {3101, {wxStyledTextCtrl, getSelectionStart, 0}},
- {3102, {wxStyledTextCtrl, setSelectionEnd, 1}},
- {3103, {wxStyledTextCtrl, getSelectionEnd, 0}},
- {3104, {wxStyledTextCtrl, setPrintMagnification, 1}},
- {3105, {wxStyledTextCtrl, getPrintMagnification, 0}},
- {3106, {wxStyledTextCtrl, setPrintColourMode, 1}},
- {3107, {wxStyledTextCtrl, getPrintColourMode, 0}},
- {3108, {wxStyledTextCtrl, findText, 4}},
- {3109, {wxStyledTextCtrl, formatRange, 7}},
- {3110, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
- {3111, {wxStyledTextCtrl, getLine, 1}},
- {3112, {wxStyledTextCtrl, getLineCount, 0}},
- {3113, {wxStyledTextCtrl, setMarginLeft, 1}},
- {3114, {wxStyledTextCtrl, getMarginLeft, 0}},
- {3115, {wxStyledTextCtrl, setMarginRight, 1}},
- {3116, {wxStyledTextCtrl, getMarginRight, 0}},
- {3117, {wxStyledTextCtrl, getModify, 0}},
- {3118, {wxStyledTextCtrl, setSelection, 2}},
- {3119, {wxStyledTextCtrl, getSelectedText, 0}},
- {3120, {wxStyledTextCtrl, getTextRange, 2}},
- {3121, {wxStyledTextCtrl, hideSelection, 1}},
- {3122, {wxStyledTextCtrl, lineFromPosition, 1}},
- {3123, {wxStyledTextCtrl, positionFromLine, 1}},
- {3124, {wxStyledTextCtrl, lineScroll, 2}},
- {3125, {wxStyledTextCtrl, ensureCaretVisible, 0}},
- {3126, {wxStyledTextCtrl, replaceSelection, 1}},
- {3127, {wxStyledTextCtrl, setReadOnly, 1}},
- {3128, {wxStyledTextCtrl, canPaste, 0}},
- {3129, {wxStyledTextCtrl, canUndo, 0}},
- {3130, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
- {3131, {wxStyledTextCtrl, undo, 0}},
- {3132, {wxStyledTextCtrl, cut, 0}},
- {3133, {wxStyledTextCtrl, copy, 0}},
- {3134, {wxStyledTextCtrl, paste, 0}},
- {3135, {wxStyledTextCtrl, clear, 0}},
- {3136, {wxStyledTextCtrl, setText, 1}},
- {3137, {wxStyledTextCtrl, getText, 0}},
- {3138, {wxStyledTextCtrl, getTextLength, 0}},
- {3139, {wxStyledTextCtrl, getOvertype, 0}},
- {3140, {wxStyledTextCtrl, setCaretWidth, 1}},
- {3141, {wxStyledTextCtrl, getCaretWidth, 0}},
- {3142, {wxStyledTextCtrl, setTargetStart, 1}},
- {3143, {wxStyledTextCtrl, getTargetStart, 0}},
- {3144, {wxStyledTextCtrl, setTargetEnd, 1}},
- {3145, {wxStyledTextCtrl, getTargetEnd, 0}},
- {3146, {wxStyledTextCtrl, replaceTarget, 1}},
- {3147, {wxStyledTextCtrl, searchInTarget, 1}},
- {3148, {wxStyledTextCtrl, setSearchFlags, 1}},
- {3149, {wxStyledTextCtrl, getSearchFlags, 0}},
- {3150, {wxStyledTextCtrl, callTipShow, 2}},
- {3151, {wxStyledTextCtrl, callTipCancel, 0}},
- {3152, {wxStyledTextCtrl, callTipActive, 0}},
- {3153, {wxStyledTextCtrl, callTipPosAtStart, 0}},
- {3154, {wxStyledTextCtrl, callTipSetHighlight, 2}},
- {3155, {wxStyledTextCtrl, callTipSetBackground, 1}},
- {3156, {wxStyledTextCtrl, callTipSetForeground, 1}},
- {3157, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
- {3158, {wxStyledTextCtrl, callTipUseStyle, 1}},
- {3159, {wxStyledTextCtrl, visibleFromDocLine, 1}},
- {3160, {wxStyledTextCtrl, docLineFromVisible, 1}},
- {3161, {wxStyledTextCtrl, wrapCount, 1}},
- {3162, {wxStyledTextCtrl, setFoldLevel, 2}},
- {3163, {wxStyledTextCtrl, getFoldLevel, 1}},
- {3164, {wxStyledTextCtrl, getLastChild, 2}},
- {3165, {wxStyledTextCtrl, getFoldParent, 1}},
- {3166, {wxStyledTextCtrl, showLines, 2}},
- {3167, {wxStyledTextCtrl, hideLines, 2}},
- {3168, {wxStyledTextCtrl, getLineVisible, 1}},
- {3169, {wxStyledTextCtrl, setFoldExpanded, 2}},
- {3170, {wxStyledTextCtrl, getFoldExpanded, 1}},
- {3171, {wxStyledTextCtrl, toggleFold, 1}},
- {3172, {wxStyledTextCtrl, ensureVisible, 1}},
- {3173, {wxStyledTextCtrl, setFoldFlags, 1}},
- {3174, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
- {3175, {wxStyledTextCtrl, setTabIndents, 1}},
- {3176, {wxStyledTextCtrl, getTabIndents, 0}},
- {3177, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
- {3178, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
- {3179, {wxStyledTextCtrl, setMouseDwellTime, 1}},
- {3180, {wxStyledTextCtrl, getMouseDwellTime, 0}},
- {3181, {wxStyledTextCtrl, wordStartPosition, 2}},
- {3182, {wxStyledTextCtrl, wordEndPosition, 2}},
- {3183, {wxStyledTextCtrl, setWrapMode, 1}},
- {3184, {wxStyledTextCtrl, getWrapMode, 0}},
- {3185, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
- {3186, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
- {3187, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
- {3188, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
- {3189, {wxStyledTextCtrl, setWrapStartIndent, 1}},
- {3190, {wxStyledTextCtrl, getWrapStartIndent, 0}},
- {3191, {wxStyledTextCtrl, setLayoutCache, 1}},
- {3192, {wxStyledTextCtrl, getLayoutCache, 0}},
- {3193, {wxStyledTextCtrl, setScrollWidth, 1}},
- {3194, {wxStyledTextCtrl, getScrollWidth, 0}},
- {3195, {wxStyledTextCtrl, textWidth, 2}},
- {3196, {wxStyledTextCtrl, getEndAtLastLine, 0}},
- {3197, {wxStyledTextCtrl, textHeight, 1}},
- {3198, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
- {3199, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
- {3200, {wxStyledTextCtrl, appendText, 1}},
- {3201, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
- {3202, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
- {3203, {wxStyledTextCtrl, targetFromSelection, 0}},
- {3204, {wxStyledTextCtrl, linesJoin, 0}},
- {3205, {wxStyledTextCtrl, linesSplit, 1}},
- {3206, {wxStyledTextCtrl, setFoldMarginColour, 2}},
- {3207, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
- {3208, {wxStyledTextCtrl, lineDown, 0}},
- {3209, {wxStyledTextCtrl, lineDownExtend, 0}},
- {3210, {wxStyledTextCtrl, lineUp, 0}},
- {3211, {wxStyledTextCtrl, lineUpExtend, 0}},
- {3212, {wxStyledTextCtrl, charLeft, 0}},
- {3213, {wxStyledTextCtrl, charLeftExtend, 0}},
- {3214, {wxStyledTextCtrl, charRight, 0}},
- {3215, {wxStyledTextCtrl, charRightExtend, 0}},
- {3216, {wxStyledTextCtrl, wordLeft, 0}},
- {3217, {wxStyledTextCtrl, wordLeftExtend, 0}},
- {3218, {wxStyledTextCtrl, wordRight, 0}},
- {3219, {wxStyledTextCtrl, wordRightExtend, 0}},
- {3220, {wxStyledTextCtrl, home, 0}},
- {3221, {wxStyledTextCtrl, homeExtend, 0}},
- {3222, {wxStyledTextCtrl, lineEnd, 0}},
- {3223, {wxStyledTextCtrl, lineEndExtend, 0}},
- {3224, {wxStyledTextCtrl, documentStart, 0}},
- {3225, {wxStyledTextCtrl, documentStartExtend, 0}},
- {3226, {wxStyledTextCtrl, documentEnd, 0}},
- {3227, {wxStyledTextCtrl, documentEndExtend, 0}},
- {3228, {wxStyledTextCtrl, pageUp, 0}},
- {3229, {wxStyledTextCtrl, pageUpExtend, 0}},
- {3230, {wxStyledTextCtrl, pageDown, 0}},
- {3231, {wxStyledTextCtrl, pageDownExtend, 0}},
- {3232, {wxStyledTextCtrl, editToggleOvertype, 0}},
- {3233, {wxStyledTextCtrl, cancel, 0}},
- {3234, {wxStyledTextCtrl, deleteBack, 0}},
- {3235, {wxStyledTextCtrl, tab, 0}},
- {3236, {wxStyledTextCtrl, backTab, 0}},
- {3237, {wxStyledTextCtrl, newLine, 0}},
- {3238, {wxStyledTextCtrl, formFeed, 0}},
- {3239, {wxStyledTextCtrl, vCHome, 0}},
- {3240, {wxStyledTextCtrl, vCHomeExtend, 0}},
- {3241, {wxStyledTextCtrl, zoomIn, 0}},
- {3242, {wxStyledTextCtrl, zoomOut, 0}},
- {3243, {wxStyledTextCtrl, delWordLeft, 0}},
- {3244, {wxStyledTextCtrl, delWordRight, 0}},
- {3245, {wxStyledTextCtrl, lineCut, 0}},
- {3246, {wxStyledTextCtrl, lineDelete, 0}},
- {3247, {wxStyledTextCtrl, lineTranspose, 0}},
- {3248, {wxStyledTextCtrl, lineDuplicate, 0}},
- {3249, {wxStyledTextCtrl, lowerCase, 0}},
- {3250, {wxStyledTextCtrl, upperCase, 0}},
- {3251, {wxStyledTextCtrl, lineScrollDown, 0}},
- {3252, {wxStyledTextCtrl, lineScrollUp, 0}},
- {3253, {wxStyledTextCtrl, deleteBackNotLine, 0}},
- {3254, {wxStyledTextCtrl, homeDisplay, 0}},
- {3255, {wxStyledTextCtrl, homeDisplayExtend, 0}},
- {3256, {wxStyledTextCtrl, lineEndDisplay, 0}},
- {3257, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
- {3258, {wxStyledTextCtrl, homeWrapExtend, 0}},
- {3259, {wxStyledTextCtrl, lineEndWrap, 0}},
- {3260, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
- {3261, {wxStyledTextCtrl, vCHomeWrap, 0}},
- {3262, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
- {3263, {wxStyledTextCtrl, lineCopy, 0}},
- {3264, {wxStyledTextCtrl, moveCaretInsideView, 0}},
- {3265, {wxStyledTextCtrl, lineLength, 1}},
- {3266, {wxStyledTextCtrl, braceHighlight, 2}},
- {3267, {wxStyledTextCtrl, braceBadLight, 1}},
- {3268, {wxStyledTextCtrl, braceMatch, 1}},
- {3269, {wxStyledTextCtrl, getViewEOL, 0}},
- {3270, {wxStyledTextCtrl, setViewEOL, 1}},
- {3271, {wxStyledTextCtrl, setModEventMask, 1}},
- {3272, {wxStyledTextCtrl, getEdgeColumn, 0}},
- {3273, {wxStyledTextCtrl, setEdgeColumn, 1}},
- {3274, {wxStyledTextCtrl, setEdgeMode, 1}},
- {3275, {wxStyledTextCtrl, getEdgeMode, 0}},
- {3276, {wxStyledTextCtrl, getEdgeColour, 0}},
- {3277, {wxStyledTextCtrl, setEdgeColour, 1}},
- {3278, {wxStyledTextCtrl, searchAnchor, 0}},
- {3279, {wxStyledTextCtrl, searchNext, 2}},
- {3280, {wxStyledTextCtrl, searchPrev, 2}},
- {3281, {wxStyledTextCtrl, linesOnScreen, 0}},
- {3282, {wxStyledTextCtrl, usePopUp, 1}},
- {3283, {wxStyledTextCtrl, selectionIsRectangle, 0}},
- {3284, {wxStyledTextCtrl, setZoom, 1}},
- {3285, {wxStyledTextCtrl, getZoom, 0}},
- {3286, {wxStyledTextCtrl, getModEventMask, 0}},
- {3287, {wxStyledTextCtrl, setSTCFocus, 1}},
- {3288, {wxStyledTextCtrl, getSTCFocus, 0}},
- {3289, {wxStyledTextCtrl, setStatus, 1}},
- {3290, {wxStyledTextCtrl, getStatus, 0}},
- {3291, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
- {3292, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
- {3293, {wxStyledTextCtrl, setSTCCursor, 1}},
- {3294, {wxStyledTextCtrl, getSTCCursor, 0}},
- {3295, {wxStyledTextCtrl, setControlCharSymbol, 1}},
- {3296, {wxStyledTextCtrl, getControlCharSymbol, 0}},
- {3297, {wxStyledTextCtrl, wordPartLeft, 0}},
- {3298, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
- {3299, {wxStyledTextCtrl, wordPartRight, 0}},
- {3300, {wxStyledTextCtrl, wordPartRightExtend, 0}},
- {3301, {wxStyledTextCtrl, setVisiblePolicy, 2}},
- {3302, {wxStyledTextCtrl, delLineLeft, 0}},
- {3303, {wxStyledTextCtrl, delLineRight, 0}},
- {3304, {wxStyledTextCtrl, getXOffset, 0}},
- {3305, {wxStyledTextCtrl, chooseCaretX, 0}},
- {3306, {wxStyledTextCtrl, setXCaretPolicy, 2}},
- {3307, {wxStyledTextCtrl, setYCaretPolicy, 2}},
- {3308, {wxStyledTextCtrl, getPrintWrapMode, 0}},
- {3309, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
- {3310, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
- {3311, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
- {3312, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
- {3313, {wxStyledTextCtrl, paraDownExtend, 0}},
- {3314, {wxStyledTextCtrl, paraUp, 0}},
- {3315, {wxStyledTextCtrl, paraUpExtend, 0}},
- {3316, {wxStyledTextCtrl, positionBefore, 1}},
- {3317, {wxStyledTextCtrl, positionAfter, 1}},
- {3318, {wxStyledTextCtrl, copyRange, 2}},
- {3319, {wxStyledTextCtrl, copyText, 2}},
- {3320, {wxStyledTextCtrl, setSelectionMode, 1}},
- {3321, {wxStyledTextCtrl, getSelectionMode, 0}},
- {3322, {wxStyledTextCtrl, lineDownRectExtend, 0}},
- {3323, {wxStyledTextCtrl, lineUpRectExtend, 0}},
- {3324, {wxStyledTextCtrl, charLeftRectExtend, 0}},
- {3325, {wxStyledTextCtrl, charRightRectExtend, 0}},
- {3326, {wxStyledTextCtrl, homeRectExtend, 0}},
- {3327, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
- {3328, {wxStyledTextCtrl, lineEndRectExtend, 0}},
- {3329, {wxStyledTextCtrl, pageUpRectExtend, 0}},
- {3330, {wxStyledTextCtrl, pageDownRectExtend, 0}},
- {3331, {wxStyledTextCtrl, stutteredPageUp, 0}},
- {3332, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
- {3333, {wxStyledTextCtrl, stutteredPageDown, 0}},
- {3334, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
- {3335, {wxStyledTextCtrl, wordLeftEnd, 0}},
- {3336, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
- {3337, {wxStyledTextCtrl, wordRightEnd, 0}},
- {3338, {wxStyledTextCtrl, wordRightEndExtend, 0}},
- {3339, {wxStyledTextCtrl, setWhitespaceChars, 1}},
- {3340, {wxStyledTextCtrl, setCharsDefault, 0}},
- {3341, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
- {3342, {wxStyledTextCtrl, allocate, 1}},
- {3343, {wxStyledTextCtrl, findColumn, 2}},
- {3344, {wxStyledTextCtrl, getCaretSticky, 0}},
- {3345, {wxStyledTextCtrl, setCaretSticky, 1}},
- {3346, {wxStyledTextCtrl, toggleCaretSticky, 0}},
- {3347, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
- {3348, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
- {3349, {wxStyledTextCtrl, selectionDuplicate, 0}},
- {3350, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
- {3351, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
- {3352, {wxStyledTextCtrl, startRecord, 0}},
- {3353, {wxStyledTextCtrl, stopRecord, 0}},
- {3354, {wxStyledTextCtrl, setLexer, 1}},
- {3355, {wxStyledTextCtrl, getLexer, 0}},
- {3356, {wxStyledTextCtrl, colourise, 2}},
- {3357, {wxStyledTextCtrl, setProperty, 2}},
- {3358, {wxStyledTextCtrl, setKeyWords, 2}},
- {3359, {wxStyledTextCtrl, setLexerLanguage, 1}},
- {3360, {wxStyledTextCtrl, getProperty, 1}},
- {3361, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
- {3362, {wxStyledTextCtrl, getCurrentLine, 0}},
- {3363, {wxStyledTextCtrl, styleSetSpec, 2}},
- {3364, {wxStyledTextCtrl, styleSetFont, 2}},
- {3365, {wxStyledTextCtrl, styleSetFontAttr, 7}},
- {3366, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
- {3367, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
- {3368, {wxStyledTextCtrl, cmdKeyExecute, 1}},
- {3369, {wxStyledTextCtrl, setMargins, 2}},
- {3370, {wxStyledTextCtrl, getSelection, 2}},
- {3371, {wxStyledTextCtrl, pointFromPosition, 1}},
- {3372, {wxStyledTextCtrl, scrollToLine, 1}},
- {3373, {wxStyledTextCtrl, scrollToColumn, 1}},
- {3374, {wxStyledTextCtrl, setVScrollBar, 1}},
- {3375, {wxStyledTextCtrl, setHScrollBar, 1}},
- {3376, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
- {3377, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
- {3378, {wxStyledTextCtrl, saveFile, 1}},
- {3379, {wxStyledTextCtrl, loadFile, 1}},
- {3380, {wxStyledTextCtrl, doDragOver, 3}},
- {3381, {wxStyledTextCtrl, doDropText, 3}},
- {3382, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
- {3383, {wxStyledTextCtrl, addTextRaw, 1}},
- {3384, {wxStyledTextCtrl, insertTextRaw, 2}},
- {3385, {wxStyledTextCtrl, getCurLineRaw, 1}},
- {3386, {wxStyledTextCtrl, getLineRaw, 1}},
- {3387, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
- {3388, {wxStyledTextCtrl, getTextRangeRaw, 2}},
- {3389, {wxStyledTextCtrl, setTextRaw, 1}},
- {3390, {wxStyledTextCtrl, getTextRaw, 0}},
- {3391, {wxStyledTextCtrl, appendTextRaw, 1}},
- {3392, {wxArtProvider, getBitmap, 2}},
- {3393, {wxArtProvider, getIcon, 2}},
- {3394, {wxTreeEvent, getKeyCode, 0}},
- {3395, {wxTreeEvent, getItem, 0}},
- {3396, {wxTreeEvent, getKeyEvent, 0}},
- {3397, {wxTreeEvent, getLabel, 0}},
- {3398, {wxTreeEvent, getOldItem, 0}},
- {3399, {wxTreeEvent, getPoint, 0}},
- {3400, {wxTreeEvent, isEditCancelled, 0}},
- {3401, {wxTreeEvent, setToolTip, 1}},
- {3402, {wxNotebookEvent, getOldSelection, 0}},
- {3403, {wxNotebookEvent, getSelection, 0}},
- {3404, {wxNotebookEvent, setOldSelection, 1}},
- {3405, {wxNotebookEvent, setSelection, 1}},
- {3406, {wxFileDataObject, new, 0}},
- {3407, {wxFileDataObject, addFile, 1}},
- {3408, {wxFileDataObject, getFilenames, 0}},
- {3409, {wxFileDataObject, 'Destroy', undefined}},
- {3410, {wxTextDataObject, new, 1}},
- {3411, {wxTextDataObject, getTextLength, 0}},
- {3412, {wxTextDataObject, getText, 0}},
- {3413, {wxTextDataObject, setText, 1}},
- {3414, {wxTextDataObject, 'Destroy', undefined}},
- {3415, {wxBitmapDataObject, new_1_1, 1}},
- {3416, {wxBitmapDataObject, new_1_0, 1}},
- {3417, {wxBitmapDataObject, getBitmap, 0}},
- {3418, {wxBitmapDataObject, setBitmap, 1}},
- {3419, {wxBitmapDataObject, 'Destroy', undefined}},
- {3421, {wxClipboard, new, 0}},
- {3422, {wxClipboard, destruct, 0}},
- {3423, {wxClipboard, addData, 1}},
- {3424, {wxClipboard, clear, 0}},
- {3425, {wxClipboard, close, 0}},
- {3426, {wxClipboard, flush, 0}},
- {3427, {wxClipboard, getData, 1}},
- {3428, {wxClipboard, isOpened, 0}},
- {3429, {wxClipboard, open, 0}},
- {3430, {wxClipboard, setData, 1}},
- {3432, {wxClipboard, usePrimarySelection, 1}},
- {3433, {wxClipboard, isSupported, 1}},
- {3434, {wxClipboard, get, 0}},
- {3435, {wxSpinEvent, getPosition, 0}},
- {3436, {wxSpinEvent, setPosition, 1}},
- {3437, {wxSplitterWindow, new_0, 0}},
- {3438, {wxSplitterWindow, new_2, 2}},
- {3439, {wxSplitterWindow, destruct, 0}},
- {3440, {wxSplitterWindow, create, 2}},
- {3441, {wxSplitterWindow, getMinimumPaneSize, 0}},
- {3442, {wxSplitterWindow, getSashGravity, 0}},
- {3443, {wxSplitterWindow, getSashPosition, 0}},
- {3444, {wxSplitterWindow, getSplitMode, 0}},
- {3445, {wxSplitterWindow, getWindow1, 0}},
- {3446, {wxSplitterWindow, getWindow2, 0}},
- {3447, {wxSplitterWindow, initialize, 1}},
- {3448, {wxSplitterWindow, isSplit, 0}},
- {3449, {wxSplitterWindow, replaceWindow, 2}},
- {3450, {wxSplitterWindow, setSashGravity, 1}},
- {3451, {wxSplitterWindow, setSashPosition, 2}},
- {3452, {wxSplitterWindow, setSashSize, 1}},
- {3453, {wxSplitterWindow, setMinimumPaneSize, 1}},
- {3454, {wxSplitterWindow, setSplitMode, 1}},
- {3455, {wxSplitterWindow, splitHorizontally, 3}},
- {3456, {wxSplitterWindow, splitVertically, 3}},
- {3457, {wxSplitterWindow, unsplit, 1}},
- {3458, {wxSplitterWindow, updateSize, 0}},
- {3459, {wxSplitterEvent, getSashPosition, 0}},
- {3460, {wxSplitterEvent, getX, 0}},
- {3461, {wxSplitterEvent, getY, 0}},
- {3462, {wxSplitterEvent, getWindowBeingRemoved, 0}},
- {3463, {wxSplitterEvent, setSashPosition, 1}},
- {3464, {wxHtmlWindow, new_0, 0}},
- {3465, {wxHtmlWindow, new_2, 2}},
- {3466, {wxHtmlWindow, appendToPage, 1}},
- {3467, {wxHtmlWindow, getOpenedAnchor, 0}},
- {3468, {wxHtmlWindow, getOpenedPage, 0}},
- {3469, {wxHtmlWindow, getOpenedPageTitle, 0}},
- {3470, {wxHtmlWindow, getRelatedFrame, 0}},
- {3471, {wxHtmlWindow, historyBack, 0}},
- {3472, {wxHtmlWindow, historyCanBack, 0}},
- {3473, {wxHtmlWindow, historyCanForward, 0}},
- {3474, {wxHtmlWindow, historyClear, 0}},
- {3475, {wxHtmlWindow, historyForward, 0}},
- {3476, {wxHtmlWindow, loadFile, 1}},
- {3477, {wxHtmlWindow, loadPage, 1}},
- {3478, {wxHtmlWindow, selectAll, 0}},
- {3479, {wxHtmlWindow, selectionToText, 0}},
- {3480, {wxHtmlWindow, selectLine, 1}},
- {3481, {wxHtmlWindow, selectWord, 1}},
- {3482, {wxHtmlWindow, setBorders, 1}},
- {3483, {wxHtmlWindow, setFonts, 3}},
- {3484, {wxHtmlWindow, setPage, 1}},
- {3485, {wxHtmlWindow, setRelatedFrame, 2}},
- {3486, {wxHtmlWindow, setRelatedStatusBar, 1}},
- {3487, {wxHtmlWindow, toText, 0}},
- {3488, {wxHtmlWindow, 'Destroy', undefined}},
- {3489, {wxHtmlLinkEvent, getLinkInfo, 0}},
- {3490, {wxSystemSettings, getColour, 1}},
- {3491, {wxSystemSettings, getFont, 1}},
- {3492, {wxSystemSettings, getMetric, 2}},
- {3493, {wxSystemSettings, getScreenType, 0}},
- {3494, {wxSystemOptions, getOption, 1}},
- {3495, {wxSystemOptions, getOptionInt, 1}},
- {3496, {wxSystemOptions, hasOption, 1}},
- {3497, {wxSystemOptions, isFalse, 1}},
- {3498, {wxSystemOptions, setOption_2_1, 2}},
- {3499, {wxSystemOptions, setOption_2_0, 2}},
- {3500, {wxAuiNotebookEvent, setSelection, 1}},
- {3501, {wxAuiNotebookEvent, getSelection, 0}},
- {3502, {wxAuiNotebookEvent, setOldSelection, 1}},
- {3503, {wxAuiNotebookEvent, getOldSelection, 0}},
- {3504, {wxAuiNotebookEvent, setDragSource, 1}},
- {3505, {wxAuiNotebookEvent, getDragSource, 0}},
- {3506, {wxAuiManagerEvent, setManager, 1}},
- {3507, {wxAuiManagerEvent, getManager, 0}},
- {3508, {wxAuiManagerEvent, setPane, 1}},
- {3509, {wxAuiManagerEvent, getPane, 0}},
- {3510, {wxAuiManagerEvent, setButton, 1}},
- {3511, {wxAuiManagerEvent, getButton, 0}},
- {3512, {wxAuiManagerEvent, setDC, 1}},
- {3513, {wxAuiManagerEvent, getDC, 0}},
- {3514, {wxAuiManagerEvent, veto, 1}},
- {3515, {wxAuiManagerEvent, getVeto, 0}},
- {3516, {wxAuiManagerEvent, setCanVeto, 1}},
- {3517, {wxAuiManagerEvent, canVeto, 0}},
- {3518, {wxLogNull, new, 0}},
- {3519, {wxLogNull, 'Destroy', undefined}},
- {3520, {wxTaskBarIcon, new, 0}},
- {3521, {wxTaskBarIcon, destruct, 0}},
- {3522, {wxTaskBarIcon, popupMenu, 1}},
- {3523, {wxTaskBarIcon, removeIcon, 0}},
- {3524, {wxTaskBarIcon, setIcon, 2}},
+ {2927, {gdicmn, displaySize, 2}},
+ {2928, {gdicmn, setCursor, 1}},
+ {2929, {wxPrintout, new, 1}},
+ {2930, {wxPrintout, destruct, 0}},
+ {2931, {wxPrintout, getDC, 0}},
+ {2932, {wxPrintout, getPageSizeMM, 2}},
+ {2933, {wxPrintout, getPageSizePixels, 2}},
+ {2934, {wxPrintout, getPaperRectPixels, 0}},
+ {2935, {wxPrintout, getPPIPrinter, 2}},
+ {2936, {wxPrintout, getPPIScreen, 2}},
+ {2937, {wxPrintout, getTitle, 0}},
+ {2938, {wxPrintout, isPreview, 0}},
+ {2939, {wxPrintout, fitThisSizeToPaper, 1}},
+ {2940, {wxPrintout, fitThisSizeToPage, 1}},
+ {2941, {wxPrintout, fitThisSizeToPageMargins, 2}},
+ {2942, {wxPrintout, mapScreenSizeToPaper, 0}},
+ {2943, {wxPrintout, mapScreenSizeToPage, 0}},
+ {2944, {wxPrintout, mapScreenSizeToPageMargins, 1}},
+ {2945, {wxPrintout, mapScreenSizeToDevice, 0}},
+ {2946, {wxPrintout, getLogicalPaperRect, 0}},
+ {2947, {wxPrintout, getLogicalPageRect, 0}},
+ {2948, {wxPrintout, getLogicalPageMarginsRect, 1}},
+ {2949, {wxPrintout, setLogicalOrigin, 2}},
+ {2950, {wxPrintout, offsetLogicalOrigin, 2}},
+ {2951, {wxStyledTextCtrl, new_2, 2}},
+ {2952, {wxStyledTextCtrl, new_0, 0}},
+ {2953, {wxStyledTextCtrl, destruct, 0}},
+ {2954, {wxStyledTextCtrl, create, 2}},
+ {2955, {wxStyledTextCtrl, addText, 1}},
+ {2956, {wxStyledTextCtrl, addStyledText, 1}},
+ {2957, {wxStyledTextCtrl, insertText, 2}},
+ {2958, {wxStyledTextCtrl, clearAll, 0}},
+ {2959, {wxStyledTextCtrl, clearDocumentStyle, 0}},
+ {2960, {wxStyledTextCtrl, getLength, 0}},
+ {2961, {wxStyledTextCtrl, getCharAt, 1}},
+ {2962, {wxStyledTextCtrl, getCurrentPos, 0}},
+ {2963, {wxStyledTextCtrl, getAnchor, 0}},
+ {2964, {wxStyledTextCtrl, getStyleAt, 1}},
+ {2965, {wxStyledTextCtrl, redo, 0}},
+ {2966, {wxStyledTextCtrl, setUndoCollection, 1}},
+ {2967, {wxStyledTextCtrl, selectAll, 0}},
+ {2968, {wxStyledTextCtrl, setSavePoint, 0}},
+ {2969, {wxStyledTextCtrl, getStyledText, 2}},
+ {2970, {wxStyledTextCtrl, canRedo, 0}},
+ {2971, {wxStyledTextCtrl, markerLineFromHandle, 1}},
+ {2972, {wxStyledTextCtrl, markerDeleteHandle, 1}},
+ {2973, {wxStyledTextCtrl, getUndoCollection, 0}},
+ {2974, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
+ {2975, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
+ {2976, {wxStyledTextCtrl, positionFromPoint, 1}},
+ {2977, {wxStyledTextCtrl, positionFromPointClose, 2}},
+ {2978, {wxStyledTextCtrl, gotoLine, 1}},
+ {2979, {wxStyledTextCtrl, gotoPos, 1}},
+ {2980, {wxStyledTextCtrl, setAnchor, 1}},
+ {2981, {wxStyledTextCtrl, getCurLine, 1}},
+ {2982, {wxStyledTextCtrl, getEndStyled, 0}},
+ {2983, {wxStyledTextCtrl, convertEOLs, 1}},
+ {2984, {wxStyledTextCtrl, getEOLMode, 0}},
+ {2985, {wxStyledTextCtrl, setEOLMode, 1}},
+ {2986, {wxStyledTextCtrl, startStyling, 2}},
+ {2987, {wxStyledTextCtrl, setStyling, 2}},
+ {2988, {wxStyledTextCtrl, getBufferedDraw, 0}},
+ {2989, {wxStyledTextCtrl, setBufferedDraw, 1}},
+ {2990, {wxStyledTextCtrl, setTabWidth, 1}},
+ {2991, {wxStyledTextCtrl, getTabWidth, 0}},
+ {2992, {wxStyledTextCtrl, setCodePage, 1}},
+ {2993, {wxStyledTextCtrl, markerDefine, 3}},
+ {2994, {wxStyledTextCtrl, markerSetForeground, 2}},
+ {2995, {wxStyledTextCtrl, markerSetBackground, 2}},
+ {2996, {wxStyledTextCtrl, markerAdd, 2}},
+ {2997, {wxStyledTextCtrl, markerDelete, 2}},
+ {2998, {wxStyledTextCtrl, markerDeleteAll, 1}},
+ {2999, {wxStyledTextCtrl, markerGet, 1}},
+ {3000, {wxStyledTextCtrl, markerNext, 2}},
+ {3001, {wxStyledTextCtrl, markerPrevious, 2}},
+ {3002, {wxStyledTextCtrl, markerDefineBitmap, 2}},
+ {3003, {wxStyledTextCtrl, markerAddSet, 2}},
+ {3004, {wxStyledTextCtrl, markerSetAlpha, 2}},
+ {3005, {wxStyledTextCtrl, setMarginType, 2}},
+ {3006, {wxStyledTextCtrl, getMarginType, 1}},
+ {3007, {wxStyledTextCtrl, setMarginWidth, 2}},
+ {3008, {wxStyledTextCtrl, getMarginWidth, 1}},
+ {3009, {wxStyledTextCtrl, setMarginMask, 2}},
+ {3010, {wxStyledTextCtrl, getMarginMask, 1}},
+ {3011, {wxStyledTextCtrl, setMarginSensitive, 2}},
+ {3012, {wxStyledTextCtrl, getMarginSensitive, 1}},
+ {3013, {wxStyledTextCtrl, styleClearAll, 0}},
+ {3014, {wxStyledTextCtrl, styleSetForeground, 2}},
+ {3015, {wxStyledTextCtrl, styleSetBackground, 2}},
+ {3016, {wxStyledTextCtrl, styleSetBold, 2}},
+ {3017, {wxStyledTextCtrl, styleSetItalic, 2}},
+ {3018, {wxStyledTextCtrl, styleSetSize, 2}},
+ {3019, {wxStyledTextCtrl, styleSetFaceName, 2}},
+ {3020, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
+ {3021, {wxStyledTextCtrl, styleResetDefault, 0}},
+ {3022, {wxStyledTextCtrl, styleSetUnderline, 2}},
+ {3023, {wxStyledTextCtrl, styleSetCase, 2}},
+ {3024, {wxStyledTextCtrl, styleSetHotSpot, 2}},
+ {3025, {wxStyledTextCtrl, setSelForeground, 2}},
+ {3026, {wxStyledTextCtrl, setSelBackground, 2}},
+ {3027, {wxStyledTextCtrl, getSelAlpha, 0}},
+ {3028, {wxStyledTextCtrl, setSelAlpha, 1}},
+ {3029, {wxStyledTextCtrl, setCaretForeground, 1}},
+ {3030, {wxStyledTextCtrl, cmdKeyAssign, 3}},
+ {3031, {wxStyledTextCtrl, cmdKeyClear, 2}},
+ {3032, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
+ {3033, {wxStyledTextCtrl, setStyleBytes, 2}},
+ {3034, {wxStyledTextCtrl, styleSetVisible, 2}},
+ {3035, {wxStyledTextCtrl, getCaretPeriod, 0}},
+ {3036, {wxStyledTextCtrl, setCaretPeriod, 1}},
+ {3037, {wxStyledTextCtrl, setWordChars, 1}},
+ {3038, {wxStyledTextCtrl, beginUndoAction, 0}},
+ {3039, {wxStyledTextCtrl, endUndoAction, 0}},
+ {3040, {wxStyledTextCtrl, indicatorSetStyle, 2}},
+ {3041, {wxStyledTextCtrl, indicatorGetStyle, 1}},
+ {3042, {wxStyledTextCtrl, indicatorSetForeground, 2}},
+ {3043, {wxStyledTextCtrl, indicatorGetForeground, 1}},
+ {3044, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
+ {3045, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
+ {3046, {wxStyledTextCtrl, getStyleBits, 0}},
+ {3047, {wxStyledTextCtrl, setLineState, 2}},
+ {3048, {wxStyledTextCtrl, getLineState, 1}},
+ {3049, {wxStyledTextCtrl, getMaxLineState, 0}},
+ {3050, {wxStyledTextCtrl, getCaretLineVisible, 0}},
+ {3051, {wxStyledTextCtrl, setCaretLineVisible, 1}},
+ {3052, {wxStyledTextCtrl, getCaretLineBackground, 0}},
+ {3053, {wxStyledTextCtrl, setCaretLineBackground, 1}},
+ {3054, {wxStyledTextCtrl, autoCompShow, 2}},
+ {3055, {wxStyledTextCtrl, autoCompCancel, 0}},
+ {3056, {wxStyledTextCtrl, autoCompActive, 0}},
+ {3057, {wxStyledTextCtrl, autoCompPosStart, 0}},
+ {3058, {wxStyledTextCtrl, autoCompComplete, 0}},
+ {3059, {wxStyledTextCtrl, autoCompStops, 1}},
+ {3060, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
+ {3061, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
+ {3062, {wxStyledTextCtrl, autoCompSelect, 1}},
+ {3063, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
+ {3064, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
+ {3065, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
+ {3066, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
+ {3067, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
+ {3068, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
+ {3069, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
+ {3070, {wxStyledTextCtrl, userListShow, 2}},
+ {3071, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
+ {3072, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
+ {3073, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
+ {3074, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
+ {3075, {wxStyledTextCtrl, registerImage, 2}},
+ {3076, {wxStyledTextCtrl, clearRegisteredImages, 0}},
+ {3077, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
+ {3078, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
+ {3079, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
+ {3080, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
+ {3081, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
+ {3082, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
+ {3083, {wxStyledTextCtrl, setIndent, 1}},
+ {3084, {wxStyledTextCtrl, getIndent, 0}},
+ {3085, {wxStyledTextCtrl, setUseTabs, 1}},
+ {3086, {wxStyledTextCtrl, getUseTabs, 0}},
+ {3087, {wxStyledTextCtrl, setLineIndentation, 2}},
+ {3088, {wxStyledTextCtrl, getLineIndentation, 1}},
+ {3089, {wxStyledTextCtrl, getLineIndentPosition, 1}},
+ {3090, {wxStyledTextCtrl, getColumn, 1}},
+ {3091, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
+ {3092, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
+ {3093, {wxStyledTextCtrl, setIndentationGuides, 1}},
+ {3094, {wxStyledTextCtrl, getIndentationGuides, 0}},
+ {3095, {wxStyledTextCtrl, setHighlightGuide, 1}},
+ {3096, {wxStyledTextCtrl, getHighlightGuide, 0}},
+ {3097, {wxStyledTextCtrl, getLineEndPosition, 1}},
+ {3098, {wxStyledTextCtrl, getCodePage, 0}},
+ {3099, {wxStyledTextCtrl, getCaretForeground, 0}},
+ {3100, {wxStyledTextCtrl, getReadOnly, 0}},
+ {3101, {wxStyledTextCtrl, setCurrentPos, 1}},
+ {3102, {wxStyledTextCtrl, setSelectionStart, 1}},
+ {3103, {wxStyledTextCtrl, getSelectionStart, 0}},
+ {3104, {wxStyledTextCtrl, setSelectionEnd, 1}},
+ {3105, {wxStyledTextCtrl, getSelectionEnd, 0}},
+ {3106, {wxStyledTextCtrl, setPrintMagnification, 1}},
+ {3107, {wxStyledTextCtrl, getPrintMagnification, 0}},
+ {3108, {wxStyledTextCtrl, setPrintColourMode, 1}},
+ {3109, {wxStyledTextCtrl, getPrintColourMode, 0}},
+ {3110, {wxStyledTextCtrl, findText, 4}},
+ {3111, {wxStyledTextCtrl, formatRange, 7}},
+ {3112, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
+ {3113, {wxStyledTextCtrl, getLine, 1}},
+ {3114, {wxStyledTextCtrl, getLineCount, 0}},
+ {3115, {wxStyledTextCtrl, setMarginLeft, 1}},
+ {3116, {wxStyledTextCtrl, getMarginLeft, 0}},
+ {3117, {wxStyledTextCtrl, setMarginRight, 1}},
+ {3118, {wxStyledTextCtrl, getMarginRight, 0}},
+ {3119, {wxStyledTextCtrl, getModify, 0}},
+ {3120, {wxStyledTextCtrl, setSelection, 2}},
+ {3121, {wxStyledTextCtrl, getSelectedText, 0}},
+ {3122, {wxStyledTextCtrl, getTextRange, 2}},
+ {3123, {wxStyledTextCtrl, hideSelection, 1}},
+ {3124, {wxStyledTextCtrl, lineFromPosition, 1}},
+ {3125, {wxStyledTextCtrl, positionFromLine, 1}},
+ {3126, {wxStyledTextCtrl, lineScroll, 2}},
+ {3127, {wxStyledTextCtrl, ensureCaretVisible, 0}},
+ {3128, {wxStyledTextCtrl, replaceSelection, 1}},
+ {3129, {wxStyledTextCtrl, setReadOnly, 1}},
+ {3130, {wxStyledTextCtrl, canPaste, 0}},
+ {3131, {wxStyledTextCtrl, canUndo, 0}},
+ {3132, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
+ {3133, {wxStyledTextCtrl, undo, 0}},
+ {3134, {wxStyledTextCtrl, cut, 0}},
+ {3135, {wxStyledTextCtrl, copy, 0}},
+ {3136, {wxStyledTextCtrl, paste, 0}},
+ {3137, {wxStyledTextCtrl, clear, 0}},
+ {3138, {wxStyledTextCtrl, setText, 1}},
+ {3139, {wxStyledTextCtrl, getText, 0}},
+ {3140, {wxStyledTextCtrl, getTextLength, 0}},
+ {3141, {wxStyledTextCtrl, getOvertype, 0}},
+ {3142, {wxStyledTextCtrl, setCaretWidth, 1}},
+ {3143, {wxStyledTextCtrl, getCaretWidth, 0}},
+ {3144, {wxStyledTextCtrl, setTargetStart, 1}},
+ {3145, {wxStyledTextCtrl, getTargetStart, 0}},
+ {3146, {wxStyledTextCtrl, setTargetEnd, 1}},
+ {3147, {wxStyledTextCtrl, getTargetEnd, 0}},
+ {3148, {wxStyledTextCtrl, replaceTarget, 1}},
+ {3149, {wxStyledTextCtrl, searchInTarget, 1}},
+ {3150, {wxStyledTextCtrl, setSearchFlags, 1}},
+ {3151, {wxStyledTextCtrl, getSearchFlags, 0}},
+ {3152, {wxStyledTextCtrl, callTipShow, 2}},
+ {3153, {wxStyledTextCtrl, callTipCancel, 0}},
+ {3154, {wxStyledTextCtrl, callTipActive, 0}},
+ {3155, {wxStyledTextCtrl, callTipPosAtStart, 0}},
+ {3156, {wxStyledTextCtrl, callTipSetHighlight, 2}},
+ {3157, {wxStyledTextCtrl, callTipSetBackground, 1}},
+ {3158, {wxStyledTextCtrl, callTipSetForeground, 1}},
+ {3159, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
+ {3160, {wxStyledTextCtrl, callTipUseStyle, 1}},
+ {3161, {wxStyledTextCtrl, visibleFromDocLine, 1}},
+ {3162, {wxStyledTextCtrl, docLineFromVisible, 1}},
+ {3163, {wxStyledTextCtrl, wrapCount, 1}},
+ {3164, {wxStyledTextCtrl, setFoldLevel, 2}},
+ {3165, {wxStyledTextCtrl, getFoldLevel, 1}},
+ {3166, {wxStyledTextCtrl, getLastChild, 2}},
+ {3167, {wxStyledTextCtrl, getFoldParent, 1}},
+ {3168, {wxStyledTextCtrl, showLines, 2}},
+ {3169, {wxStyledTextCtrl, hideLines, 2}},
+ {3170, {wxStyledTextCtrl, getLineVisible, 1}},
+ {3171, {wxStyledTextCtrl, setFoldExpanded, 2}},
+ {3172, {wxStyledTextCtrl, getFoldExpanded, 1}},
+ {3173, {wxStyledTextCtrl, toggleFold, 1}},
+ {3174, {wxStyledTextCtrl, ensureVisible, 1}},
+ {3175, {wxStyledTextCtrl, setFoldFlags, 1}},
+ {3176, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
+ {3177, {wxStyledTextCtrl, setTabIndents, 1}},
+ {3178, {wxStyledTextCtrl, getTabIndents, 0}},
+ {3179, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
+ {3180, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
+ {3181, {wxStyledTextCtrl, setMouseDwellTime, 1}},
+ {3182, {wxStyledTextCtrl, getMouseDwellTime, 0}},
+ {3183, {wxStyledTextCtrl, wordStartPosition, 2}},
+ {3184, {wxStyledTextCtrl, wordEndPosition, 2}},
+ {3185, {wxStyledTextCtrl, setWrapMode, 1}},
+ {3186, {wxStyledTextCtrl, getWrapMode, 0}},
+ {3187, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
+ {3188, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
+ {3189, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
+ {3190, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
+ {3191, {wxStyledTextCtrl, setWrapStartIndent, 1}},
+ {3192, {wxStyledTextCtrl, getWrapStartIndent, 0}},
+ {3193, {wxStyledTextCtrl, setLayoutCache, 1}},
+ {3194, {wxStyledTextCtrl, getLayoutCache, 0}},
+ {3195, {wxStyledTextCtrl, setScrollWidth, 1}},
+ {3196, {wxStyledTextCtrl, getScrollWidth, 0}},
+ {3197, {wxStyledTextCtrl, textWidth, 2}},
+ {3198, {wxStyledTextCtrl, getEndAtLastLine, 0}},
+ {3199, {wxStyledTextCtrl, textHeight, 1}},
+ {3200, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
+ {3201, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
+ {3202, {wxStyledTextCtrl, appendText, 1}},
+ {3203, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
+ {3204, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
+ {3205, {wxStyledTextCtrl, targetFromSelection, 0}},
+ {3206, {wxStyledTextCtrl, linesJoin, 0}},
+ {3207, {wxStyledTextCtrl, linesSplit, 1}},
+ {3208, {wxStyledTextCtrl, setFoldMarginColour, 2}},
+ {3209, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
+ {3210, {wxStyledTextCtrl, lineDown, 0}},
+ {3211, {wxStyledTextCtrl, lineDownExtend, 0}},
+ {3212, {wxStyledTextCtrl, lineUp, 0}},
+ {3213, {wxStyledTextCtrl, lineUpExtend, 0}},
+ {3214, {wxStyledTextCtrl, charLeft, 0}},
+ {3215, {wxStyledTextCtrl, charLeftExtend, 0}},
+ {3216, {wxStyledTextCtrl, charRight, 0}},
+ {3217, {wxStyledTextCtrl, charRightExtend, 0}},
+ {3218, {wxStyledTextCtrl, wordLeft, 0}},
+ {3219, {wxStyledTextCtrl, wordLeftExtend, 0}},
+ {3220, {wxStyledTextCtrl, wordRight, 0}},
+ {3221, {wxStyledTextCtrl, wordRightExtend, 0}},
+ {3222, {wxStyledTextCtrl, home, 0}},
+ {3223, {wxStyledTextCtrl, homeExtend, 0}},
+ {3224, {wxStyledTextCtrl, lineEnd, 0}},
+ {3225, {wxStyledTextCtrl, lineEndExtend, 0}},
+ {3226, {wxStyledTextCtrl, documentStart, 0}},
+ {3227, {wxStyledTextCtrl, documentStartExtend, 0}},
+ {3228, {wxStyledTextCtrl, documentEnd, 0}},
+ {3229, {wxStyledTextCtrl, documentEndExtend, 0}},
+ {3230, {wxStyledTextCtrl, pageUp, 0}},
+ {3231, {wxStyledTextCtrl, pageUpExtend, 0}},
+ {3232, {wxStyledTextCtrl, pageDown, 0}},
+ {3233, {wxStyledTextCtrl, pageDownExtend, 0}},
+ {3234, {wxStyledTextCtrl, editToggleOvertype, 0}},
+ {3235, {wxStyledTextCtrl, cancel, 0}},
+ {3236, {wxStyledTextCtrl, deleteBack, 0}},
+ {3237, {wxStyledTextCtrl, tab, 0}},
+ {3238, {wxStyledTextCtrl, backTab, 0}},
+ {3239, {wxStyledTextCtrl, newLine, 0}},
+ {3240, {wxStyledTextCtrl, formFeed, 0}},
+ {3241, {wxStyledTextCtrl, vCHome, 0}},
+ {3242, {wxStyledTextCtrl, vCHomeExtend, 0}},
+ {3243, {wxStyledTextCtrl, zoomIn, 0}},
+ {3244, {wxStyledTextCtrl, zoomOut, 0}},
+ {3245, {wxStyledTextCtrl, delWordLeft, 0}},
+ {3246, {wxStyledTextCtrl, delWordRight, 0}},
+ {3247, {wxStyledTextCtrl, lineCut, 0}},
+ {3248, {wxStyledTextCtrl, lineDelete, 0}},
+ {3249, {wxStyledTextCtrl, lineTranspose, 0}},
+ {3250, {wxStyledTextCtrl, lineDuplicate, 0}},
+ {3251, {wxStyledTextCtrl, lowerCase, 0}},
+ {3252, {wxStyledTextCtrl, upperCase, 0}},
+ {3253, {wxStyledTextCtrl, lineScrollDown, 0}},
+ {3254, {wxStyledTextCtrl, lineScrollUp, 0}},
+ {3255, {wxStyledTextCtrl, deleteBackNotLine, 0}},
+ {3256, {wxStyledTextCtrl, homeDisplay, 0}},
+ {3257, {wxStyledTextCtrl, homeDisplayExtend, 0}},
+ {3258, {wxStyledTextCtrl, lineEndDisplay, 0}},
+ {3259, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
+ {3260, {wxStyledTextCtrl, homeWrapExtend, 0}},
+ {3261, {wxStyledTextCtrl, lineEndWrap, 0}},
+ {3262, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
+ {3263, {wxStyledTextCtrl, vCHomeWrap, 0}},
+ {3264, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
+ {3265, {wxStyledTextCtrl, lineCopy, 0}},
+ {3266, {wxStyledTextCtrl, moveCaretInsideView, 0}},
+ {3267, {wxStyledTextCtrl, lineLength, 1}},
+ {3268, {wxStyledTextCtrl, braceHighlight, 2}},
+ {3269, {wxStyledTextCtrl, braceBadLight, 1}},
+ {3270, {wxStyledTextCtrl, braceMatch, 1}},
+ {3271, {wxStyledTextCtrl, getViewEOL, 0}},
+ {3272, {wxStyledTextCtrl, setViewEOL, 1}},
+ {3273, {wxStyledTextCtrl, setModEventMask, 1}},
+ {3274, {wxStyledTextCtrl, getEdgeColumn, 0}},
+ {3275, {wxStyledTextCtrl, setEdgeColumn, 1}},
+ {3276, {wxStyledTextCtrl, setEdgeMode, 1}},
+ {3277, {wxStyledTextCtrl, getEdgeMode, 0}},
+ {3278, {wxStyledTextCtrl, getEdgeColour, 0}},
+ {3279, {wxStyledTextCtrl, setEdgeColour, 1}},
+ {3280, {wxStyledTextCtrl, searchAnchor, 0}},
+ {3281, {wxStyledTextCtrl, searchNext, 2}},
+ {3282, {wxStyledTextCtrl, searchPrev, 2}},
+ {3283, {wxStyledTextCtrl, linesOnScreen, 0}},
+ {3284, {wxStyledTextCtrl, usePopUp, 1}},
+ {3285, {wxStyledTextCtrl, selectionIsRectangle, 0}},
+ {3286, {wxStyledTextCtrl, setZoom, 1}},
+ {3287, {wxStyledTextCtrl, getZoom, 0}},
+ {3288, {wxStyledTextCtrl, getModEventMask, 0}},
+ {3289, {wxStyledTextCtrl, setSTCFocus, 1}},
+ {3290, {wxStyledTextCtrl, getSTCFocus, 0}},
+ {3291, {wxStyledTextCtrl, setStatus, 1}},
+ {3292, {wxStyledTextCtrl, getStatus, 0}},
+ {3293, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
+ {3294, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
+ {3295, {wxStyledTextCtrl, setSTCCursor, 1}},
+ {3296, {wxStyledTextCtrl, getSTCCursor, 0}},
+ {3297, {wxStyledTextCtrl, setControlCharSymbol, 1}},
+ {3298, {wxStyledTextCtrl, getControlCharSymbol, 0}},
+ {3299, {wxStyledTextCtrl, wordPartLeft, 0}},
+ {3300, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
+ {3301, {wxStyledTextCtrl, wordPartRight, 0}},
+ {3302, {wxStyledTextCtrl, wordPartRightExtend, 0}},
+ {3303, {wxStyledTextCtrl, setVisiblePolicy, 2}},
+ {3304, {wxStyledTextCtrl, delLineLeft, 0}},
+ {3305, {wxStyledTextCtrl, delLineRight, 0}},
+ {3306, {wxStyledTextCtrl, getXOffset, 0}},
+ {3307, {wxStyledTextCtrl, chooseCaretX, 0}},
+ {3308, {wxStyledTextCtrl, setXCaretPolicy, 2}},
+ {3309, {wxStyledTextCtrl, setYCaretPolicy, 2}},
+ {3310, {wxStyledTextCtrl, getPrintWrapMode, 0}},
+ {3311, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
+ {3312, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
+ {3313, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
+ {3314, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
+ {3315, {wxStyledTextCtrl, paraDownExtend, 0}},
+ {3316, {wxStyledTextCtrl, paraUp, 0}},
+ {3317, {wxStyledTextCtrl, paraUpExtend, 0}},
+ {3318, {wxStyledTextCtrl, positionBefore, 1}},
+ {3319, {wxStyledTextCtrl, positionAfter, 1}},
+ {3320, {wxStyledTextCtrl, copyRange, 2}},
+ {3321, {wxStyledTextCtrl, copyText, 2}},
+ {3322, {wxStyledTextCtrl, setSelectionMode, 1}},
+ {3323, {wxStyledTextCtrl, getSelectionMode, 0}},
+ {3324, {wxStyledTextCtrl, lineDownRectExtend, 0}},
+ {3325, {wxStyledTextCtrl, lineUpRectExtend, 0}},
+ {3326, {wxStyledTextCtrl, charLeftRectExtend, 0}},
+ {3327, {wxStyledTextCtrl, charRightRectExtend, 0}},
+ {3328, {wxStyledTextCtrl, homeRectExtend, 0}},
+ {3329, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
+ {3330, {wxStyledTextCtrl, lineEndRectExtend, 0}},
+ {3331, {wxStyledTextCtrl, pageUpRectExtend, 0}},
+ {3332, {wxStyledTextCtrl, pageDownRectExtend, 0}},
+ {3333, {wxStyledTextCtrl, stutteredPageUp, 0}},
+ {3334, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
+ {3335, {wxStyledTextCtrl, stutteredPageDown, 0}},
+ {3336, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
+ {3337, {wxStyledTextCtrl, wordLeftEnd, 0}},
+ {3338, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
+ {3339, {wxStyledTextCtrl, wordRightEnd, 0}},
+ {3340, {wxStyledTextCtrl, wordRightEndExtend, 0}},
+ {3341, {wxStyledTextCtrl, setWhitespaceChars, 1}},
+ {3342, {wxStyledTextCtrl, setCharsDefault, 0}},
+ {3343, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
+ {3344, {wxStyledTextCtrl, allocate, 1}},
+ {3345, {wxStyledTextCtrl, findColumn, 2}},
+ {3346, {wxStyledTextCtrl, getCaretSticky, 0}},
+ {3347, {wxStyledTextCtrl, setCaretSticky, 1}},
+ {3348, {wxStyledTextCtrl, toggleCaretSticky, 0}},
+ {3349, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
+ {3350, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
+ {3351, {wxStyledTextCtrl, selectionDuplicate, 0}},
+ {3352, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
+ {3353, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
+ {3354, {wxStyledTextCtrl, startRecord, 0}},
+ {3355, {wxStyledTextCtrl, stopRecord, 0}},
+ {3356, {wxStyledTextCtrl, setLexer, 1}},
+ {3357, {wxStyledTextCtrl, getLexer, 0}},
+ {3358, {wxStyledTextCtrl, colourise, 2}},
+ {3359, {wxStyledTextCtrl, setProperty, 2}},
+ {3360, {wxStyledTextCtrl, setKeyWords, 2}},
+ {3361, {wxStyledTextCtrl, setLexerLanguage, 1}},
+ {3362, {wxStyledTextCtrl, getProperty, 1}},
+ {3363, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
+ {3364, {wxStyledTextCtrl, getCurrentLine, 0}},
+ {3365, {wxStyledTextCtrl, styleSetSpec, 2}},
+ {3366, {wxStyledTextCtrl, styleSetFont, 2}},
+ {3367, {wxStyledTextCtrl, styleSetFontAttr, 7}},
+ {3368, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
+ {3369, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
+ {3370, {wxStyledTextCtrl, cmdKeyExecute, 1}},
+ {3371, {wxStyledTextCtrl, setMargins, 2}},
+ {3372, {wxStyledTextCtrl, getSelection, 2}},
+ {3373, {wxStyledTextCtrl, pointFromPosition, 1}},
+ {3374, {wxStyledTextCtrl, scrollToLine, 1}},
+ {3375, {wxStyledTextCtrl, scrollToColumn, 1}},
+ {3376, {wxStyledTextCtrl, setVScrollBar, 1}},
+ {3377, {wxStyledTextCtrl, setHScrollBar, 1}},
+ {3378, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
+ {3379, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
+ {3380, {wxStyledTextCtrl, saveFile, 1}},
+ {3381, {wxStyledTextCtrl, loadFile, 1}},
+ {3382, {wxStyledTextCtrl, doDragOver, 3}},
+ {3383, {wxStyledTextCtrl, doDropText, 3}},
+ {3384, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
+ {3385, {wxStyledTextCtrl, addTextRaw, 1}},
+ {3386, {wxStyledTextCtrl, insertTextRaw, 2}},
+ {3387, {wxStyledTextCtrl, getCurLineRaw, 1}},
+ {3388, {wxStyledTextCtrl, getLineRaw, 1}},
+ {3389, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
+ {3390, {wxStyledTextCtrl, getTextRangeRaw, 2}},
+ {3391, {wxStyledTextCtrl, setTextRaw, 1}},
+ {3392, {wxStyledTextCtrl, getTextRaw, 0}},
+ {3393, {wxStyledTextCtrl, appendTextRaw, 1}},
+ {3394, {wxArtProvider, getBitmap, 2}},
+ {3395, {wxArtProvider, getIcon, 2}},
+ {3396, {wxTreeEvent, getKeyCode, 0}},
+ {3397, {wxTreeEvent, getItem, 0}},
+ {3398, {wxTreeEvent, getKeyEvent, 0}},
+ {3399, {wxTreeEvent, getLabel, 0}},
+ {3400, {wxTreeEvent, getOldItem, 0}},
+ {3401, {wxTreeEvent, getPoint, 0}},
+ {3402, {wxTreeEvent, isEditCancelled, 0}},
+ {3403, {wxTreeEvent, setToolTip, 1}},
+ {3404, {wxNotebookEvent, getOldSelection, 0}},
+ {3405, {wxNotebookEvent, getSelection, 0}},
+ {3406, {wxNotebookEvent, setOldSelection, 1}},
+ {3407, {wxNotebookEvent, setSelection, 1}},
+ {3408, {wxFileDataObject, new, 0}},
+ {3409, {wxFileDataObject, addFile, 1}},
+ {3410, {wxFileDataObject, getFilenames, 0}},
+ {3411, {wxFileDataObject, 'Destroy', undefined}},
+ {3412, {wxTextDataObject, new, 1}},
+ {3413, {wxTextDataObject, getTextLength, 0}},
+ {3414, {wxTextDataObject, getText, 0}},
+ {3415, {wxTextDataObject, setText, 1}},
+ {3416, {wxTextDataObject, 'Destroy', undefined}},
+ {3417, {wxBitmapDataObject, new_1_1, 1}},
+ {3418, {wxBitmapDataObject, new_1_0, 1}},
+ {3419, {wxBitmapDataObject, getBitmap, 0}},
+ {3420, {wxBitmapDataObject, setBitmap, 1}},
+ {3421, {wxBitmapDataObject, 'Destroy', undefined}},
+ {3423, {wxClipboard, new, 0}},
+ {3424, {wxClipboard, destruct, 0}},
+ {3425, {wxClipboard, addData, 1}},
+ {3426, {wxClipboard, clear, 0}},
+ {3427, {wxClipboard, close, 0}},
+ {3428, {wxClipboard, flush, 0}},
+ {3429, {wxClipboard, getData, 1}},
+ {3430, {wxClipboard, isOpened, 0}},
+ {3431, {wxClipboard, open, 0}},
+ {3432, {wxClipboard, setData, 1}},
+ {3434, {wxClipboard, usePrimarySelection, 1}},
+ {3435, {wxClipboard, isSupported, 1}},
+ {3436, {wxClipboard, get, 0}},
+ {3437, {wxSpinEvent, getPosition, 0}},
+ {3438, {wxSpinEvent, setPosition, 1}},
+ {3439, {wxSplitterWindow, new_0, 0}},
+ {3440, {wxSplitterWindow, new_2, 2}},
+ {3441, {wxSplitterWindow, destruct, 0}},
+ {3442, {wxSplitterWindow, create, 2}},
+ {3443, {wxSplitterWindow, getMinimumPaneSize, 0}},
+ {3444, {wxSplitterWindow, getSashGravity, 0}},
+ {3445, {wxSplitterWindow, getSashPosition, 0}},
+ {3446, {wxSplitterWindow, getSplitMode, 0}},
+ {3447, {wxSplitterWindow, getWindow1, 0}},
+ {3448, {wxSplitterWindow, getWindow2, 0}},
+ {3449, {wxSplitterWindow, initialize, 1}},
+ {3450, {wxSplitterWindow, isSplit, 0}},
+ {3451, {wxSplitterWindow, replaceWindow, 2}},
+ {3452, {wxSplitterWindow, setSashGravity, 1}},
+ {3453, {wxSplitterWindow, setSashPosition, 2}},
+ {3454, {wxSplitterWindow, setSashSize, 1}},
+ {3455, {wxSplitterWindow, setMinimumPaneSize, 1}},
+ {3456, {wxSplitterWindow, setSplitMode, 1}},
+ {3457, {wxSplitterWindow, splitHorizontally, 3}},
+ {3458, {wxSplitterWindow, splitVertically, 3}},
+ {3459, {wxSplitterWindow, unsplit, 1}},
+ {3460, {wxSplitterWindow, updateSize, 0}},
+ {3461, {wxSplitterEvent, getSashPosition, 0}},
+ {3462, {wxSplitterEvent, getX, 0}},
+ {3463, {wxSplitterEvent, getY, 0}},
+ {3464, {wxSplitterEvent, getWindowBeingRemoved, 0}},
+ {3465, {wxSplitterEvent, setSashPosition, 1}},
+ {3466, {wxHtmlWindow, new_0, 0}},
+ {3467, {wxHtmlWindow, new_2, 2}},
+ {3468, {wxHtmlWindow, appendToPage, 1}},
+ {3469, {wxHtmlWindow, getOpenedAnchor, 0}},
+ {3470, {wxHtmlWindow, getOpenedPage, 0}},
+ {3471, {wxHtmlWindow, getOpenedPageTitle, 0}},
+ {3472, {wxHtmlWindow, getRelatedFrame, 0}},
+ {3473, {wxHtmlWindow, historyBack, 0}},
+ {3474, {wxHtmlWindow, historyCanBack, 0}},
+ {3475, {wxHtmlWindow, historyCanForward, 0}},
+ {3476, {wxHtmlWindow, historyClear, 0}},
+ {3477, {wxHtmlWindow, historyForward, 0}},
+ {3478, {wxHtmlWindow, loadFile, 1}},
+ {3479, {wxHtmlWindow, loadPage, 1}},
+ {3480, {wxHtmlWindow, selectAll, 0}},
+ {3481, {wxHtmlWindow, selectionToText, 0}},
+ {3482, {wxHtmlWindow, selectLine, 1}},
+ {3483, {wxHtmlWindow, selectWord, 1}},
+ {3484, {wxHtmlWindow, setBorders, 1}},
+ {3485, {wxHtmlWindow, setFonts, 3}},
+ {3486, {wxHtmlWindow, setPage, 1}},
+ {3487, {wxHtmlWindow, setRelatedFrame, 2}},
+ {3488, {wxHtmlWindow, setRelatedStatusBar, 1}},
+ {3489, {wxHtmlWindow, toText, 0}},
+ {3490, {wxHtmlWindow, 'Destroy', undefined}},
+ {3491, {wxHtmlLinkEvent, getLinkInfo, 0}},
+ {3492, {wxSystemSettings, getColour, 1}},
+ {3493, {wxSystemSettings, getFont, 1}},
+ {3494, {wxSystemSettings, getMetric, 2}},
+ {3495, {wxSystemSettings, getScreenType, 0}},
+ {3496, {wxSystemOptions, getOption, 1}},
+ {3497, {wxSystemOptions, getOptionInt, 1}},
+ {3498, {wxSystemOptions, hasOption, 1}},
+ {3499, {wxSystemOptions, isFalse, 1}},
+ {3500, {wxSystemOptions, setOption_2_1, 2}},
+ {3501, {wxSystemOptions, setOption_2_0, 2}},
+ {3502, {wxAuiNotebookEvent, setSelection, 1}},
+ {3503, {wxAuiNotebookEvent, getSelection, 0}},
+ {3504, {wxAuiNotebookEvent, setOldSelection, 1}},
+ {3505, {wxAuiNotebookEvent, getOldSelection, 0}},
+ {3506, {wxAuiNotebookEvent, setDragSource, 1}},
+ {3507, {wxAuiNotebookEvent, getDragSource, 0}},
+ {3508, {wxAuiManagerEvent, setManager, 1}},
+ {3509, {wxAuiManagerEvent, getManager, 0}},
+ {3510, {wxAuiManagerEvent, setPane, 1}},
+ {3511, {wxAuiManagerEvent, getPane, 0}},
+ {3512, {wxAuiManagerEvent, setButton, 1}},
+ {3513, {wxAuiManagerEvent, getButton, 0}},
+ {3514, {wxAuiManagerEvent, setDC, 1}},
+ {3515, {wxAuiManagerEvent, getDC, 0}},
+ {3516, {wxAuiManagerEvent, veto, 1}},
+ {3517, {wxAuiManagerEvent, getVeto, 0}},
+ {3518, {wxAuiManagerEvent, setCanVeto, 1}},
+ {3519, {wxAuiManagerEvent, canVeto, 0}},
+ {3520, {wxLogNull, new, 0}},
+ {3521, {wxLogNull, 'Destroy', undefined}},
+ {3522, {wxTaskBarIcon, new, 0}},
+ {3523, {wxTaskBarIcon, destruct, 0}},
+ {3524, {wxTaskBarIcon, popupMenu, 1}},
+ {3525, {wxTaskBarIcon, removeIcon, 0}},
+ {3526, {wxTaskBarIcon, setIcon, 2}},
+ {3527, {wxLocale, new_0, 0}},
+ {3529, {wxLocale, new_2, 2}},
+ {3530, {wxLocale, destruct, 0}},
+ {3532, {wxLocale, init, 1}},
+ {3533, {wxLocale, addCatalog_1, 1}},
+ {3534, {wxLocale, addCatalog_3, 3}},
+ {3535, {wxLocale, addCatalogLookupPathPrefix, 1}},
+ {3536, {wxLocale, getCanonicalName, 0}},
+ {3537, {wxLocale, getLanguage, 0}},
+ {3538, {wxLocale, getLanguageName, 1}},
+ {3539, {wxLocale, getLocale, 0}},
+ {3540, {wxLocale, getName, 0}},
+ {3541, {wxLocale, getString_2, 2}},
+ {3542, {wxLocale, getString_4, 4}},
+ {3543, {wxLocale, getHeaderValue, 2}},
+ {3544, {wxLocale, getSysName, 0}},
+ {3545, {wxLocale, getSystemEncoding, 0}},
+ {3546, {wxLocale, getSystemEncodingName, 0}},
+ {3547, {wxLocale, getSystemLanguage, 0}},
+ {3548, {wxLocale, isLoaded, 1}},
+ {3549, {wxLocale, isOk, 0}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index 109368adb3..97f9bd9695 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -2706,599 +2706,622 @@
-define(utils_wxGetOsDescription, 2924).
-define(utils_wxIsPlatformLittleEndian, 2925).
-define(utils_wxIsPlatform64Bit, 2926).
--define(wxPrintout_new, 2927).
--define(wxPrintout_destruct, 2928).
--define(wxPrintout_GetDC, 2929).
--define(wxPrintout_GetPageSizeMM, 2930).
--define(wxPrintout_GetPageSizePixels, 2931).
--define(wxPrintout_GetPaperRectPixels, 2932).
--define(wxPrintout_GetPPIPrinter, 2933).
--define(wxPrintout_GetPPIScreen, 2934).
--define(wxPrintout_GetTitle, 2935).
--define(wxPrintout_IsPreview, 2936).
--define(wxPrintout_FitThisSizeToPaper, 2937).
--define(wxPrintout_FitThisSizeToPage, 2938).
--define(wxPrintout_FitThisSizeToPageMargins, 2939).
--define(wxPrintout_MapScreenSizeToPaper, 2940).
--define(wxPrintout_MapScreenSizeToPage, 2941).
--define(wxPrintout_MapScreenSizeToPageMargins, 2942).
--define(wxPrintout_MapScreenSizeToDevice, 2943).
--define(wxPrintout_GetLogicalPaperRect, 2944).
--define(wxPrintout_GetLogicalPageRect, 2945).
--define(wxPrintout_GetLogicalPageMarginsRect, 2946).
--define(wxPrintout_SetLogicalOrigin, 2947).
--define(wxPrintout_OffsetLogicalOrigin, 2948).
--define(wxStyledTextCtrl_new_2, 2949).
--define(wxStyledTextCtrl_new_0, 2950).
--define(wxStyledTextCtrl_destruct, 2951).
--define(wxStyledTextCtrl_Create, 2952).
--define(wxStyledTextCtrl_AddText, 2953).
--define(wxStyledTextCtrl_AddStyledText, 2954).
--define(wxStyledTextCtrl_InsertText, 2955).
--define(wxStyledTextCtrl_ClearAll, 2956).
--define(wxStyledTextCtrl_ClearDocumentStyle, 2957).
--define(wxStyledTextCtrl_GetLength, 2958).
--define(wxStyledTextCtrl_GetCharAt, 2959).
--define(wxStyledTextCtrl_GetCurrentPos, 2960).
--define(wxStyledTextCtrl_GetAnchor, 2961).
--define(wxStyledTextCtrl_GetStyleAt, 2962).
--define(wxStyledTextCtrl_Redo, 2963).
--define(wxStyledTextCtrl_SetUndoCollection, 2964).
--define(wxStyledTextCtrl_SelectAll, 2965).
--define(wxStyledTextCtrl_SetSavePoint, 2966).
--define(wxStyledTextCtrl_GetStyledText, 2967).
--define(wxStyledTextCtrl_CanRedo, 2968).
--define(wxStyledTextCtrl_MarkerLineFromHandle, 2969).
--define(wxStyledTextCtrl_MarkerDeleteHandle, 2970).
--define(wxStyledTextCtrl_GetUndoCollection, 2971).
--define(wxStyledTextCtrl_GetViewWhiteSpace, 2972).
--define(wxStyledTextCtrl_SetViewWhiteSpace, 2973).
--define(wxStyledTextCtrl_PositionFromPoint, 2974).
--define(wxStyledTextCtrl_PositionFromPointClose, 2975).
--define(wxStyledTextCtrl_GotoLine, 2976).
--define(wxStyledTextCtrl_GotoPos, 2977).
--define(wxStyledTextCtrl_SetAnchor, 2978).
--define(wxStyledTextCtrl_GetCurLine, 2979).
--define(wxStyledTextCtrl_GetEndStyled, 2980).
--define(wxStyledTextCtrl_ConvertEOLs, 2981).
--define(wxStyledTextCtrl_GetEOLMode, 2982).
--define(wxStyledTextCtrl_SetEOLMode, 2983).
--define(wxStyledTextCtrl_StartStyling, 2984).
--define(wxStyledTextCtrl_SetStyling, 2985).
--define(wxStyledTextCtrl_GetBufferedDraw, 2986).
--define(wxStyledTextCtrl_SetBufferedDraw, 2987).
--define(wxStyledTextCtrl_SetTabWidth, 2988).
--define(wxStyledTextCtrl_GetTabWidth, 2989).
--define(wxStyledTextCtrl_SetCodePage, 2990).
--define(wxStyledTextCtrl_MarkerDefine, 2991).
--define(wxStyledTextCtrl_MarkerSetForeground, 2992).
--define(wxStyledTextCtrl_MarkerSetBackground, 2993).
--define(wxStyledTextCtrl_MarkerAdd, 2994).
--define(wxStyledTextCtrl_MarkerDelete, 2995).
--define(wxStyledTextCtrl_MarkerDeleteAll, 2996).
--define(wxStyledTextCtrl_MarkerGet, 2997).
--define(wxStyledTextCtrl_MarkerNext, 2998).
--define(wxStyledTextCtrl_MarkerPrevious, 2999).
--define(wxStyledTextCtrl_MarkerDefineBitmap, 3000).
--define(wxStyledTextCtrl_MarkerAddSet, 3001).
--define(wxStyledTextCtrl_MarkerSetAlpha, 3002).
--define(wxStyledTextCtrl_SetMarginType, 3003).
--define(wxStyledTextCtrl_GetMarginType, 3004).
--define(wxStyledTextCtrl_SetMarginWidth, 3005).
--define(wxStyledTextCtrl_GetMarginWidth, 3006).
--define(wxStyledTextCtrl_SetMarginMask, 3007).
--define(wxStyledTextCtrl_GetMarginMask, 3008).
--define(wxStyledTextCtrl_SetMarginSensitive, 3009).
--define(wxStyledTextCtrl_GetMarginSensitive, 3010).
--define(wxStyledTextCtrl_StyleClearAll, 3011).
--define(wxStyledTextCtrl_StyleSetForeground, 3012).
--define(wxStyledTextCtrl_StyleSetBackground, 3013).
--define(wxStyledTextCtrl_StyleSetBold, 3014).
--define(wxStyledTextCtrl_StyleSetItalic, 3015).
--define(wxStyledTextCtrl_StyleSetSize, 3016).
--define(wxStyledTextCtrl_StyleSetFaceName, 3017).
--define(wxStyledTextCtrl_StyleSetEOLFilled, 3018).
--define(wxStyledTextCtrl_StyleResetDefault, 3019).
--define(wxStyledTextCtrl_StyleSetUnderline, 3020).
--define(wxStyledTextCtrl_StyleSetCase, 3021).
--define(wxStyledTextCtrl_StyleSetHotSpot, 3022).
--define(wxStyledTextCtrl_SetSelForeground, 3023).
--define(wxStyledTextCtrl_SetSelBackground, 3024).
--define(wxStyledTextCtrl_GetSelAlpha, 3025).
--define(wxStyledTextCtrl_SetSelAlpha, 3026).
--define(wxStyledTextCtrl_SetCaretForeground, 3027).
--define(wxStyledTextCtrl_CmdKeyAssign, 3028).
--define(wxStyledTextCtrl_CmdKeyClear, 3029).
--define(wxStyledTextCtrl_CmdKeyClearAll, 3030).
--define(wxStyledTextCtrl_SetStyleBytes, 3031).
--define(wxStyledTextCtrl_StyleSetVisible, 3032).
--define(wxStyledTextCtrl_GetCaretPeriod, 3033).
--define(wxStyledTextCtrl_SetCaretPeriod, 3034).
--define(wxStyledTextCtrl_SetWordChars, 3035).
--define(wxStyledTextCtrl_BeginUndoAction, 3036).
--define(wxStyledTextCtrl_EndUndoAction, 3037).
--define(wxStyledTextCtrl_IndicatorSetStyle, 3038).
--define(wxStyledTextCtrl_IndicatorGetStyle, 3039).
--define(wxStyledTextCtrl_IndicatorSetForeground, 3040).
--define(wxStyledTextCtrl_IndicatorGetForeground, 3041).
--define(wxStyledTextCtrl_SetWhitespaceForeground, 3042).
--define(wxStyledTextCtrl_SetWhitespaceBackground, 3043).
--define(wxStyledTextCtrl_GetStyleBits, 3044).
--define(wxStyledTextCtrl_SetLineState, 3045).
--define(wxStyledTextCtrl_GetLineState, 3046).
--define(wxStyledTextCtrl_GetMaxLineState, 3047).
--define(wxStyledTextCtrl_GetCaretLineVisible, 3048).
--define(wxStyledTextCtrl_SetCaretLineVisible, 3049).
--define(wxStyledTextCtrl_GetCaretLineBackground, 3050).
--define(wxStyledTextCtrl_SetCaretLineBackground, 3051).
--define(wxStyledTextCtrl_AutoCompShow, 3052).
--define(wxStyledTextCtrl_AutoCompCancel, 3053).
--define(wxStyledTextCtrl_AutoCompActive, 3054).
--define(wxStyledTextCtrl_AutoCompPosStart, 3055).
--define(wxStyledTextCtrl_AutoCompComplete, 3056).
--define(wxStyledTextCtrl_AutoCompStops, 3057).
--define(wxStyledTextCtrl_AutoCompSetSeparator, 3058).
--define(wxStyledTextCtrl_AutoCompGetSeparator, 3059).
--define(wxStyledTextCtrl_AutoCompSelect, 3060).
--define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3061).
--define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3062).
--define(wxStyledTextCtrl_AutoCompSetFillUps, 3063).
--define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3064).
--define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3065).
--define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3066).
--define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3067).
--define(wxStyledTextCtrl_UserListShow, 3068).
--define(wxStyledTextCtrl_AutoCompSetAutoHide, 3069).
--define(wxStyledTextCtrl_AutoCompGetAutoHide, 3070).
--define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3071).
--define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3072).
--define(wxStyledTextCtrl_RegisterImage, 3073).
--define(wxStyledTextCtrl_ClearRegisteredImages, 3074).
--define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3075).
--define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3076).
--define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3077).
--define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3078).
--define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3079).
--define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3080).
--define(wxStyledTextCtrl_SetIndent, 3081).
--define(wxStyledTextCtrl_GetIndent, 3082).
--define(wxStyledTextCtrl_SetUseTabs, 3083).
--define(wxStyledTextCtrl_GetUseTabs, 3084).
--define(wxStyledTextCtrl_SetLineIndentation, 3085).
--define(wxStyledTextCtrl_GetLineIndentation, 3086).
--define(wxStyledTextCtrl_GetLineIndentPosition, 3087).
--define(wxStyledTextCtrl_GetColumn, 3088).
--define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3089).
--define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3090).
--define(wxStyledTextCtrl_SetIndentationGuides, 3091).
--define(wxStyledTextCtrl_GetIndentationGuides, 3092).
--define(wxStyledTextCtrl_SetHighlightGuide, 3093).
--define(wxStyledTextCtrl_GetHighlightGuide, 3094).
--define(wxStyledTextCtrl_GetLineEndPosition, 3095).
--define(wxStyledTextCtrl_GetCodePage, 3096).
--define(wxStyledTextCtrl_GetCaretForeground, 3097).
--define(wxStyledTextCtrl_GetReadOnly, 3098).
--define(wxStyledTextCtrl_SetCurrentPos, 3099).
--define(wxStyledTextCtrl_SetSelectionStart, 3100).
--define(wxStyledTextCtrl_GetSelectionStart, 3101).
--define(wxStyledTextCtrl_SetSelectionEnd, 3102).
--define(wxStyledTextCtrl_GetSelectionEnd, 3103).
--define(wxStyledTextCtrl_SetPrintMagnification, 3104).
--define(wxStyledTextCtrl_GetPrintMagnification, 3105).
--define(wxStyledTextCtrl_SetPrintColourMode, 3106).
--define(wxStyledTextCtrl_GetPrintColourMode, 3107).
--define(wxStyledTextCtrl_FindText, 3108).
--define(wxStyledTextCtrl_FormatRange, 3109).
--define(wxStyledTextCtrl_GetFirstVisibleLine, 3110).
--define(wxStyledTextCtrl_GetLine, 3111).
--define(wxStyledTextCtrl_GetLineCount, 3112).
--define(wxStyledTextCtrl_SetMarginLeft, 3113).
--define(wxStyledTextCtrl_GetMarginLeft, 3114).
--define(wxStyledTextCtrl_SetMarginRight, 3115).
--define(wxStyledTextCtrl_GetMarginRight, 3116).
--define(wxStyledTextCtrl_GetModify, 3117).
--define(wxStyledTextCtrl_SetSelection, 3118).
--define(wxStyledTextCtrl_GetSelectedText, 3119).
--define(wxStyledTextCtrl_GetTextRange, 3120).
--define(wxStyledTextCtrl_HideSelection, 3121).
--define(wxStyledTextCtrl_LineFromPosition, 3122).
--define(wxStyledTextCtrl_PositionFromLine, 3123).
--define(wxStyledTextCtrl_LineScroll, 3124).
--define(wxStyledTextCtrl_EnsureCaretVisible, 3125).
--define(wxStyledTextCtrl_ReplaceSelection, 3126).
--define(wxStyledTextCtrl_SetReadOnly, 3127).
--define(wxStyledTextCtrl_CanPaste, 3128).
--define(wxStyledTextCtrl_CanUndo, 3129).
--define(wxStyledTextCtrl_EmptyUndoBuffer, 3130).
--define(wxStyledTextCtrl_Undo, 3131).
--define(wxStyledTextCtrl_Cut, 3132).
--define(wxStyledTextCtrl_Copy, 3133).
--define(wxStyledTextCtrl_Paste, 3134).
--define(wxStyledTextCtrl_Clear, 3135).
--define(wxStyledTextCtrl_SetText, 3136).
--define(wxStyledTextCtrl_GetText, 3137).
--define(wxStyledTextCtrl_GetTextLength, 3138).
--define(wxStyledTextCtrl_GetOvertype, 3139).
--define(wxStyledTextCtrl_SetCaretWidth, 3140).
--define(wxStyledTextCtrl_GetCaretWidth, 3141).
--define(wxStyledTextCtrl_SetTargetStart, 3142).
--define(wxStyledTextCtrl_GetTargetStart, 3143).
--define(wxStyledTextCtrl_SetTargetEnd, 3144).
--define(wxStyledTextCtrl_GetTargetEnd, 3145).
--define(wxStyledTextCtrl_ReplaceTarget, 3146).
--define(wxStyledTextCtrl_SearchInTarget, 3147).
--define(wxStyledTextCtrl_SetSearchFlags, 3148).
--define(wxStyledTextCtrl_GetSearchFlags, 3149).
--define(wxStyledTextCtrl_CallTipShow, 3150).
--define(wxStyledTextCtrl_CallTipCancel, 3151).
--define(wxStyledTextCtrl_CallTipActive, 3152).
--define(wxStyledTextCtrl_CallTipPosAtStart, 3153).
--define(wxStyledTextCtrl_CallTipSetHighlight, 3154).
--define(wxStyledTextCtrl_CallTipSetBackground, 3155).
--define(wxStyledTextCtrl_CallTipSetForeground, 3156).
--define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3157).
--define(wxStyledTextCtrl_CallTipUseStyle, 3158).
--define(wxStyledTextCtrl_VisibleFromDocLine, 3159).
--define(wxStyledTextCtrl_DocLineFromVisible, 3160).
--define(wxStyledTextCtrl_WrapCount, 3161).
--define(wxStyledTextCtrl_SetFoldLevel, 3162).
--define(wxStyledTextCtrl_GetFoldLevel, 3163).
--define(wxStyledTextCtrl_GetLastChild, 3164).
--define(wxStyledTextCtrl_GetFoldParent, 3165).
--define(wxStyledTextCtrl_ShowLines, 3166).
--define(wxStyledTextCtrl_HideLines, 3167).
--define(wxStyledTextCtrl_GetLineVisible, 3168).
--define(wxStyledTextCtrl_SetFoldExpanded, 3169).
--define(wxStyledTextCtrl_GetFoldExpanded, 3170).
--define(wxStyledTextCtrl_ToggleFold, 3171).
--define(wxStyledTextCtrl_EnsureVisible, 3172).
--define(wxStyledTextCtrl_SetFoldFlags, 3173).
--define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3174).
--define(wxStyledTextCtrl_SetTabIndents, 3175).
--define(wxStyledTextCtrl_GetTabIndents, 3176).
--define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3177).
--define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3178).
--define(wxStyledTextCtrl_SetMouseDwellTime, 3179).
--define(wxStyledTextCtrl_GetMouseDwellTime, 3180).
--define(wxStyledTextCtrl_WordStartPosition, 3181).
--define(wxStyledTextCtrl_WordEndPosition, 3182).
--define(wxStyledTextCtrl_SetWrapMode, 3183).
--define(wxStyledTextCtrl_GetWrapMode, 3184).
--define(wxStyledTextCtrl_SetWrapVisualFlags, 3185).
--define(wxStyledTextCtrl_GetWrapVisualFlags, 3186).
--define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3187).
--define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3188).
--define(wxStyledTextCtrl_SetWrapStartIndent, 3189).
--define(wxStyledTextCtrl_GetWrapStartIndent, 3190).
--define(wxStyledTextCtrl_SetLayoutCache, 3191).
--define(wxStyledTextCtrl_GetLayoutCache, 3192).
--define(wxStyledTextCtrl_SetScrollWidth, 3193).
--define(wxStyledTextCtrl_GetScrollWidth, 3194).
--define(wxStyledTextCtrl_TextWidth, 3195).
--define(wxStyledTextCtrl_GetEndAtLastLine, 3196).
--define(wxStyledTextCtrl_TextHeight, 3197).
--define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3198).
--define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3199).
--define(wxStyledTextCtrl_AppendText, 3200).
--define(wxStyledTextCtrl_GetTwoPhaseDraw, 3201).
--define(wxStyledTextCtrl_SetTwoPhaseDraw, 3202).
--define(wxStyledTextCtrl_TargetFromSelection, 3203).
--define(wxStyledTextCtrl_LinesJoin, 3204).
--define(wxStyledTextCtrl_LinesSplit, 3205).
--define(wxStyledTextCtrl_SetFoldMarginColour, 3206).
--define(wxStyledTextCtrl_SetFoldMarginHiColour, 3207).
--define(wxStyledTextCtrl_LineDown, 3208).
--define(wxStyledTextCtrl_LineDownExtend, 3209).
--define(wxStyledTextCtrl_LineUp, 3210).
--define(wxStyledTextCtrl_LineUpExtend, 3211).
--define(wxStyledTextCtrl_CharLeft, 3212).
--define(wxStyledTextCtrl_CharLeftExtend, 3213).
--define(wxStyledTextCtrl_CharRight, 3214).
--define(wxStyledTextCtrl_CharRightExtend, 3215).
--define(wxStyledTextCtrl_WordLeft, 3216).
--define(wxStyledTextCtrl_WordLeftExtend, 3217).
--define(wxStyledTextCtrl_WordRight, 3218).
--define(wxStyledTextCtrl_WordRightExtend, 3219).
--define(wxStyledTextCtrl_Home, 3220).
--define(wxStyledTextCtrl_HomeExtend, 3221).
--define(wxStyledTextCtrl_LineEnd, 3222).
--define(wxStyledTextCtrl_LineEndExtend, 3223).
--define(wxStyledTextCtrl_DocumentStart, 3224).
--define(wxStyledTextCtrl_DocumentStartExtend, 3225).
--define(wxStyledTextCtrl_DocumentEnd, 3226).
--define(wxStyledTextCtrl_DocumentEndExtend, 3227).
--define(wxStyledTextCtrl_PageUp, 3228).
--define(wxStyledTextCtrl_PageUpExtend, 3229).
--define(wxStyledTextCtrl_PageDown, 3230).
--define(wxStyledTextCtrl_PageDownExtend, 3231).
--define(wxStyledTextCtrl_EditToggleOvertype, 3232).
--define(wxStyledTextCtrl_Cancel, 3233).
--define(wxStyledTextCtrl_DeleteBack, 3234).
--define(wxStyledTextCtrl_Tab, 3235).
--define(wxStyledTextCtrl_BackTab, 3236).
--define(wxStyledTextCtrl_NewLine, 3237).
--define(wxStyledTextCtrl_FormFeed, 3238).
--define(wxStyledTextCtrl_VCHome, 3239).
--define(wxStyledTextCtrl_VCHomeExtend, 3240).
--define(wxStyledTextCtrl_ZoomIn, 3241).
--define(wxStyledTextCtrl_ZoomOut, 3242).
--define(wxStyledTextCtrl_DelWordLeft, 3243).
--define(wxStyledTextCtrl_DelWordRight, 3244).
--define(wxStyledTextCtrl_LineCut, 3245).
--define(wxStyledTextCtrl_LineDelete, 3246).
--define(wxStyledTextCtrl_LineTranspose, 3247).
--define(wxStyledTextCtrl_LineDuplicate, 3248).
--define(wxStyledTextCtrl_LowerCase, 3249).
--define(wxStyledTextCtrl_UpperCase, 3250).
--define(wxStyledTextCtrl_LineScrollDown, 3251).
--define(wxStyledTextCtrl_LineScrollUp, 3252).
--define(wxStyledTextCtrl_DeleteBackNotLine, 3253).
--define(wxStyledTextCtrl_HomeDisplay, 3254).
--define(wxStyledTextCtrl_HomeDisplayExtend, 3255).
--define(wxStyledTextCtrl_LineEndDisplay, 3256).
--define(wxStyledTextCtrl_LineEndDisplayExtend, 3257).
--define(wxStyledTextCtrl_HomeWrapExtend, 3258).
--define(wxStyledTextCtrl_LineEndWrap, 3259).
--define(wxStyledTextCtrl_LineEndWrapExtend, 3260).
--define(wxStyledTextCtrl_VCHomeWrap, 3261).
--define(wxStyledTextCtrl_VCHomeWrapExtend, 3262).
--define(wxStyledTextCtrl_LineCopy, 3263).
--define(wxStyledTextCtrl_MoveCaretInsideView, 3264).
--define(wxStyledTextCtrl_LineLength, 3265).
--define(wxStyledTextCtrl_BraceHighlight, 3266).
--define(wxStyledTextCtrl_BraceBadLight, 3267).
--define(wxStyledTextCtrl_BraceMatch, 3268).
--define(wxStyledTextCtrl_GetViewEOL, 3269).
--define(wxStyledTextCtrl_SetViewEOL, 3270).
--define(wxStyledTextCtrl_SetModEventMask, 3271).
--define(wxStyledTextCtrl_GetEdgeColumn, 3272).
--define(wxStyledTextCtrl_SetEdgeColumn, 3273).
--define(wxStyledTextCtrl_SetEdgeMode, 3274).
--define(wxStyledTextCtrl_GetEdgeMode, 3275).
--define(wxStyledTextCtrl_GetEdgeColour, 3276).
--define(wxStyledTextCtrl_SetEdgeColour, 3277).
--define(wxStyledTextCtrl_SearchAnchor, 3278).
--define(wxStyledTextCtrl_SearchNext, 3279).
--define(wxStyledTextCtrl_SearchPrev, 3280).
--define(wxStyledTextCtrl_LinesOnScreen, 3281).
--define(wxStyledTextCtrl_UsePopUp, 3282).
--define(wxStyledTextCtrl_SelectionIsRectangle, 3283).
--define(wxStyledTextCtrl_SetZoom, 3284).
--define(wxStyledTextCtrl_GetZoom, 3285).
--define(wxStyledTextCtrl_GetModEventMask, 3286).
--define(wxStyledTextCtrl_SetSTCFocus, 3287).
--define(wxStyledTextCtrl_GetSTCFocus, 3288).
--define(wxStyledTextCtrl_SetStatus, 3289).
--define(wxStyledTextCtrl_GetStatus, 3290).
--define(wxStyledTextCtrl_SetMouseDownCaptures, 3291).
--define(wxStyledTextCtrl_GetMouseDownCaptures, 3292).
--define(wxStyledTextCtrl_SetSTCCursor, 3293).
--define(wxStyledTextCtrl_GetSTCCursor, 3294).
--define(wxStyledTextCtrl_SetControlCharSymbol, 3295).
--define(wxStyledTextCtrl_GetControlCharSymbol, 3296).
--define(wxStyledTextCtrl_WordPartLeft, 3297).
--define(wxStyledTextCtrl_WordPartLeftExtend, 3298).
--define(wxStyledTextCtrl_WordPartRight, 3299).
--define(wxStyledTextCtrl_WordPartRightExtend, 3300).
--define(wxStyledTextCtrl_SetVisiblePolicy, 3301).
--define(wxStyledTextCtrl_DelLineLeft, 3302).
--define(wxStyledTextCtrl_DelLineRight, 3303).
--define(wxStyledTextCtrl_GetXOffset, 3304).
--define(wxStyledTextCtrl_ChooseCaretX, 3305).
--define(wxStyledTextCtrl_SetXCaretPolicy, 3306).
--define(wxStyledTextCtrl_SetYCaretPolicy, 3307).
--define(wxStyledTextCtrl_GetPrintWrapMode, 3308).
--define(wxStyledTextCtrl_SetHotspotActiveForeground, 3309).
--define(wxStyledTextCtrl_SetHotspotActiveBackground, 3310).
--define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3311).
--define(wxStyledTextCtrl_SetHotspotSingleLine, 3312).
--define(wxStyledTextCtrl_ParaDownExtend, 3313).
--define(wxStyledTextCtrl_ParaUp, 3314).
--define(wxStyledTextCtrl_ParaUpExtend, 3315).
--define(wxStyledTextCtrl_PositionBefore, 3316).
--define(wxStyledTextCtrl_PositionAfter, 3317).
--define(wxStyledTextCtrl_CopyRange, 3318).
--define(wxStyledTextCtrl_CopyText, 3319).
--define(wxStyledTextCtrl_SetSelectionMode, 3320).
--define(wxStyledTextCtrl_GetSelectionMode, 3321).
--define(wxStyledTextCtrl_LineDownRectExtend, 3322).
--define(wxStyledTextCtrl_LineUpRectExtend, 3323).
--define(wxStyledTextCtrl_CharLeftRectExtend, 3324).
--define(wxStyledTextCtrl_CharRightRectExtend, 3325).
--define(wxStyledTextCtrl_HomeRectExtend, 3326).
--define(wxStyledTextCtrl_VCHomeRectExtend, 3327).
--define(wxStyledTextCtrl_LineEndRectExtend, 3328).
--define(wxStyledTextCtrl_PageUpRectExtend, 3329).
--define(wxStyledTextCtrl_PageDownRectExtend, 3330).
--define(wxStyledTextCtrl_StutteredPageUp, 3331).
--define(wxStyledTextCtrl_StutteredPageUpExtend, 3332).
--define(wxStyledTextCtrl_StutteredPageDown, 3333).
--define(wxStyledTextCtrl_StutteredPageDownExtend, 3334).
--define(wxStyledTextCtrl_WordLeftEnd, 3335).
--define(wxStyledTextCtrl_WordLeftEndExtend, 3336).
--define(wxStyledTextCtrl_WordRightEnd, 3337).
--define(wxStyledTextCtrl_WordRightEndExtend, 3338).
--define(wxStyledTextCtrl_SetWhitespaceChars, 3339).
--define(wxStyledTextCtrl_SetCharsDefault, 3340).
--define(wxStyledTextCtrl_AutoCompGetCurrent, 3341).
--define(wxStyledTextCtrl_Allocate, 3342).
--define(wxStyledTextCtrl_FindColumn, 3343).
--define(wxStyledTextCtrl_GetCaretSticky, 3344).
--define(wxStyledTextCtrl_SetCaretSticky, 3345).
--define(wxStyledTextCtrl_ToggleCaretSticky, 3346).
--define(wxStyledTextCtrl_SetPasteConvertEndings, 3347).
--define(wxStyledTextCtrl_GetPasteConvertEndings, 3348).
--define(wxStyledTextCtrl_SelectionDuplicate, 3349).
--define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3350).
--define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3351).
--define(wxStyledTextCtrl_StartRecord, 3352).
--define(wxStyledTextCtrl_StopRecord, 3353).
--define(wxStyledTextCtrl_SetLexer, 3354).
--define(wxStyledTextCtrl_GetLexer, 3355).
--define(wxStyledTextCtrl_Colourise, 3356).
--define(wxStyledTextCtrl_SetProperty, 3357).
--define(wxStyledTextCtrl_SetKeyWords, 3358).
--define(wxStyledTextCtrl_SetLexerLanguage, 3359).
--define(wxStyledTextCtrl_GetProperty, 3360).
--define(wxStyledTextCtrl_GetStyleBitsNeeded, 3361).
--define(wxStyledTextCtrl_GetCurrentLine, 3362).
--define(wxStyledTextCtrl_StyleSetSpec, 3363).
--define(wxStyledTextCtrl_StyleSetFont, 3364).
--define(wxStyledTextCtrl_StyleSetFontAttr, 3365).
--define(wxStyledTextCtrl_StyleSetCharacterSet, 3366).
--define(wxStyledTextCtrl_StyleSetFontEncoding, 3367).
--define(wxStyledTextCtrl_CmdKeyExecute, 3368).
--define(wxStyledTextCtrl_SetMargins, 3369).
--define(wxStyledTextCtrl_GetSelection, 3370).
--define(wxStyledTextCtrl_PointFromPosition, 3371).
--define(wxStyledTextCtrl_ScrollToLine, 3372).
--define(wxStyledTextCtrl_ScrollToColumn, 3373).
--define(wxStyledTextCtrl_SetVScrollBar, 3374).
--define(wxStyledTextCtrl_SetHScrollBar, 3375).
--define(wxStyledTextCtrl_GetLastKeydownProcessed, 3376).
--define(wxStyledTextCtrl_SetLastKeydownProcessed, 3377).
--define(wxStyledTextCtrl_SaveFile, 3378).
--define(wxStyledTextCtrl_LoadFile, 3379).
--define(wxStyledTextCtrl_DoDragOver, 3380).
--define(wxStyledTextCtrl_DoDropText, 3381).
--define(wxStyledTextCtrl_GetUseAntiAliasing, 3382).
--define(wxStyledTextCtrl_AddTextRaw, 3383).
--define(wxStyledTextCtrl_InsertTextRaw, 3384).
--define(wxStyledTextCtrl_GetCurLineRaw, 3385).
--define(wxStyledTextCtrl_GetLineRaw, 3386).
--define(wxStyledTextCtrl_GetSelectedTextRaw, 3387).
--define(wxStyledTextCtrl_GetTextRangeRaw, 3388).
--define(wxStyledTextCtrl_SetTextRaw, 3389).
--define(wxStyledTextCtrl_GetTextRaw, 3390).
--define(wxStyledTextCtrl_AppendTextRaw, 3391).
--define(wxArtProvider_GetBitmap, 3392).
--define(wxArtProvider_GetIcon, 3393).
--define(wxTreeEvent_GetKeyCode, 3394).
--define(wxTreeEvent_GetItem, 3395).
--define(wxTreeEvent_GetKeyEvent, 3396).
--define(wxTreeEvent_GetLabel, 3397).
--define(wxTreeEvent_GetOldItem, 3398).
--define(wxTreeEvent_GetPoint, 3399).
--define(wxTreeEvent_IsEditCancelled, 3400).
--define(wxTreeEvent_SetToolTip, 3401).
--define(wxNotebookEvent_GetOldSelection, 3402).
--define(wxNotebookEvent_GetSelection, 3403).
--define(wxNotebookEvent_SetOldSelection, 3404).
--define(wxNotebookEvent_SetSelection, 3405).
--define(wxFileDataObject_new, 3406).
--define(wxFileDataObject_AddFile, 3407).
--define(wxFileDataObject_GetFilenames, 3408).
--define(wxFileDataObject_destroy, 3409).
--define(wxTextDataObject_new, 3410).
--define(wxTextDataObject_GetTextLength, 3411).
--define(wxTextDataObject_GetText, 3412).
--define(wxTextDataObject_SetText, 3413).
--define(wxTextDataObject_destroy, 3414).
--define(wxBitmapDataObject_new_1_1, 3415).
--define(wxBitmapDataObject_new_1_0, 3416).
--define(wxBitmapDataObject_GetBitmap, 3417).
--define(wxBitmapDataObject_SetBitmap, 3418).
--define(wxBitmapDataObject_destroy, 3419).
--define(wxClipboard_new, 3421).
--define(wxClipboard_destruct, 3422).
--define(wxClipboard_AddData, 3423).
--define(wxClipboard_Clear, 3424).
--define(wxClipboard_Close, 3425).
--define(wxClipboard_Flush, 3426).
--define(wxClipboard_GetData, 3427).
--define(wxClipboard_IsOpened, 3428).
--define(wxClipboard_Open, 3429).
--define(wxClipboard_SetData, 3430).
--define(wxClipboard_UsePrimarySelection, 3432).
--define(wxClipboard_IsSupported, 3433).
--define(wxClipboard_Get, 3434).
--define(wxSpinEvent_GetPosition, 3435).
--define(wxSpinEvent_SetPosition, 3436).
--define(wxSplitterWindow_new_0, 3437).
--define(wxSplitterWindow_new_2, 3438).
--define(wxSplitterWindow_destruct, 3439).
--define(wxSplitterWindow_Create, 3440).
--define(wxSplitterWindow_GetMinimumPaneSize, 3441).
--define(wxSplitterWindow_GetSashGravity, 3442).
--define(wxSplitterWindow_GetSashPosition, 3443).
--define(wxSplitterWindow_GetSplitMode, 3444).
--define(wxSplitterWindow_GetWindow1, 3445).
--define(wxSplitterWindow_GetWindow2, 3446).
--define(wxSplitterWindow_Initialize, 3447).
--define(wxSplitterWindow_IsSplit, 3448).
--define(wxSplitterWindow_ReplaceWindow, 3449).
--define(wxSplitterWindow_SetSashGravity, 3450).
--define(wxSplitterWindow_SetSashPosition, 3451).
--define(wxSplitterWindow_SetSashSize, 3452).
--define(wxSplitterWindow_SetMinimumPaneSize, 3453).
--define(wxSplitterWindow_SetSplitMode, 3454).
--define(wxSplitterWindow_SplitHorizontally, 3455).
--define(wxSplitterWindow_SplitVertically, 3456).
--define(wxSplitterWindow_Unsplit, 3457).
--define(wxSplitterWindow_UpdateSize, 3458).
--define(wxSplitterEvent_GetSashPosition, 3459).
--define(wxSplitterEvent_GetX, 3460).
--define(wxSplitterEvent_GetY, 3461).
--define(wxSplitterEvent_GetWindowBeingRemoved, 3462).
--define(wxSplitterEvent_SetSashPosition, 3463).
--define(wxHtmlWindow_new_0, 3464).
--define(wxHtmlWindow_new_2, 3465).
--define(wxHtmlWindow_AppendToPage, 3466).
--define(wxHtmlWindow_GetOpenedAnchor, 3467).
--define(wxHtmlWindow_GetOpenedPage, 3468).
--define(wxHtmlWindow_GetOpenedPageTitle, 3469).
--define(wxHtmlWindow_GetRelatedFrame, 3470).
--define(wxHtmlWindow_HistoryBack, 3471).
--define(wxHtmlWindow_HistoryCanBack, 3472).
--define(wxHtmlWindow_HistoryCanForward, 3473).
--define(wxHtmlWindow_HistoryClear, 3474).
--define(wxHtmlWindow_HistoryForward, 3475).
--define(wxHtmlWindow_LoadFile, 3476).
--define(wxHtmlWindow_LoadPage, 3477).
--define(wxHtmlWindow_SelectAll, 3478).
--define(wxHtmlWindow_SelectionToText, 3479).
--define(wxHtmlWindow_SelectLine, 3480).
--define(wxHtmlWindow_SelectWord, 3481).
--define(wxHtmlWindow_SetBorders, 3482).
--define(wxHtmlWindow_SetFonts, 3483).
--define(wxHtmlWindow_SetPage, 3484).
--define(wxHtmlWindow_SetRelatedFrame, 3485).
--define(wxHtmlWindow_SetRelatedStatusBar, 3486).
--define(wxHtmlWindow_ToText, 3487).
--define(wxHtmlWindow_destroy, 3488).
--define(wxHtmlLinkEvent_GetLinkInfo, 3489).
--define(wxSystemSettings_GetColour, 3490).
--define(wxSystemSettings_GetFont, 3491).
--define(wxSystemSettings_GetMetric, 3492).
--define(wxSystemSettings_GetScreenType, 3493).
--define(wxSystemOptions_GetOption, 3494).
--define(wxSystemOptions_GetOptionInt, 3495).
--define(wxSystemOptions_HasOption, 3496).
--define(wxSystemOptions_IsFalse, 3497).
--define(wxSystemOptions_SetOption_2_1, 3498).
--define(wxSystemOptions_SetOption_2_0, 3499).
--define(wxAuiNotebookEvent_SetSelection, 3500).
--define(wxAuiNotebookEvent_GetSelection, 3501).
--define(wxAuiNotebookEvent_SetOldSelection, 3502).
--define(wxAuiNotebookEvent_GetOldSelection, 3503).
--define(wxAuiNotebookEvent_SetDragSource, 3504).
--define(wxAuiNotebookEvent_GetDragSource, 3505).
--define(wxAuiManagerEvent_SetManager, 3506).
--define(wxAuiManagerEvent_GetManager, 3507).
--define(wxAuiManagerEvent_SetPane, 3508).
--define(wxAuiManagerEvent_GetPane, 3509).
--define(wxAuiManagerEvent_SetButton, 3510).
--define(wxAuiManagerEvent_GetButton, 3511).
--define(wxAuiManagerEvent_SetDC, 3512).
--define(wxAuiManagerEvent_GetDC, 3513).
--define(wxAuiManagerEvent_Veto, 3514).
--define(wxAuiManagerEvent_GetVeto, 3515).
--define(wxAuiManagerEvent_SetCanVeto, 3516).
--define(wxAuiManagerEvent_CanVeto, 3517).
--define(wxLogNull_new, 3518).
--define(wxLogNull_destroy, 3519).
--define(wxTaskBarIcon_new, 3520).
--define(wxTaskBarIcon_destruct, 3521).
--define(wxTaskBarIcon_PopupMenu, 3522).
--define(wxTaskBarIcon_RemoveIcon, 3523).
--define(wxTaskBarIcon_SetIcon, 3524).
+-define(gdicmn_wxDisplaySize, 2927).
+-define(gdicmn_wxSetCursor, 2928).
+-define(wxPrintout_new, 2929).
+-define(wxPrintout_destruct, 2930).
+-define(wxPrintout_GetDC, 2931).
+-define(wxPrintout_GetPageSizeMM, 2932).
+-define(wxPrintout_GetPageSizePixels, 2933).
+-define(wxPrintout_GetPaperRectPixels, 2934).
+-define(wxPrintout_GetPPIPrinter, 2935).
+-define(wxPrintout_GetPPIScreen, 2936).
+-define(wxPrintout_GetTitle, 2937).
+-define(wxPrintout_IsPreview, 2938).
+-define(wxPrintout_FitThisSizeToPaper, 2939).
+-define(wxPrintout_FitThisSizeToPage, 2940).
+-define(wxPrintout_FitThisSizeToPageMargins, 2941).
+-define(wxPrintout_MapScreenSizeToPaper, 2942).
+-define(wxPrintout_MapScreenSizeToPage, 2943).
+-define(wxPrintout_MapScreenSizeToPageMargins, 2944).
+-define(wxPrintout_MapScreenSizeToDevice, 2945).
+-define(wxPrintout_GetLogicalPaperRect, 2946).
+-define(wxPrintout_GetLogicalPageRect, 2947).
+-define(wxPrintout_GetLogicalPageMarginsRect, 2948).
+-define(wxPrintout_SetLogicalOrigin, 2949).
+-define(wxPrintout_OffsetLogicalOrigin, 2950).
+-define(wxStyledTextCtrl_new_2, 2951).
+-define(wxStyledTextCtrl_new_0, 2952).
+-define(wxStyledTextCtrl_destruct, 2953).
+-define(wxStyledTextCtrl_Create, 2954).
+-define(wxStyledTextCtrl_AddText, 2955).
+-define(wxStyledTextCtrl_AddStyledText, 2956).
+-define(wxStyledTextCtrl_InsertText, 2957).
+-define(wxStyledTextCtrl_ClearAll, 2958).
+-define(wxStyledTextCtrl_ClearDocumentStyle, 2959).
+-define(wxStyledTextCtrl_GetLength, 2960).
+-define(wxStyledTextCtrl_GetCharAt, 2961).
+-define(wxStyledTextCtrl_GetCurrentPos, 2962).
+-define(wxStyledTextCtrl_GetAnchor, 2963).
+-define(wxStyledTextCtrl_GetStyleAt, 2964).
+-define(wxStyledTextCtrl_Redo, 2965).
+-define(wxStyledTextCtrl_SetUndoCollection, 2966).
+-define(wxStyledTextCtrl_SelectAll, 2967).
+-define(wxStyledTextCtrl_SetSavePoint, 2968).
+-define(wxStyledTextCtrl_GetStyledText, 2969).
+-define(wxStyledTextCtrl_CanRedo, 2970).
+-define(wxStyledTextCtrl_MarkerLineFromHandle, 2971).
+-define(wxStyledTextCtrl_MarkerDeleteHandle, 2972).
+-define(wxStyledTextCtrl_GetUndoCollection, 2973).
+-define(wxStyledTextCtrl_GetViewWhiteSpace, 2974).
+-define(wxStyledTextCtrl_SetViewWhiteSpace, 2975).
+-define(wxStyledTextCtrl_PositionFromPoint, 2976).
+-define(wxStyledTextCtrl_PositionFromPointClose, 2977).
+-define(wxStyledTextCtrl_GotoLine, 2978).
+-define(wxStyledTextCtrl_GotoPos, 2979).
+-define(wxStyledTextCtrl_SetAnchor, 2980).
+-define(wxStyledTextCtrl_GetCurLine, 2981).
+-define(wxStyledTextCtrl_GetEndStyled, 2982).
+-define(wxStyledTextCtrl_ConvertEOLs, 2983).
+-define(wxStyledTextCtrl_GetEOLMode, 2984).
+-define(wxStyledTextCtrl_SetEOLMode, 2985).
+-define(wxStyledTextCtrl_StartStyling, 2986).
+-define(wxStyledTextCtrl_SetStyling, 2987).
+-define(wxStyledTextCtrl_GetBufferedDraw, 2988).
+-define(wxStyledTextCtrl_SetBufferedDraw, 2989).
+-define(wxStyledTextCtrl_SetTabWidth, 2990).
+-define(wxStyledTextCtrl_GetTabWidth, 2991).
+-define(wxStyledTextCtrl_SetCodePage, 2992).
+-define(wxStyledTextCtrl_MarkerDefine, 2993).
+-define(wxStyledTextCtrl_MarkerSetForeground, 2994).
+-define(wxStyledTextCtrl_MarkerSetBackground, 2995).
+-define(wxStyledTextCtrl_MarkerAdd, 2996).
+-define(wxStyledTextCtrl_MarkerDelete, 2997).
+-define(wxStyledTextCtrl_MarkerDeleteAll, 2998).
+-define(wxStyledTextCtrl_MarkerGet, 2999).
+-define(wxStyledTextCtrl_MarkerNext, 3000).
+-define(wxStyledTextCtrl_MarkerPrevious, 3001).
+-define(wxStyledTextCtrl_MarkerDefineBitmap, 3002).
+-define(wxStyledTextCtrl_MarkerAddSet, 3003).
+-define(wxStyledTextCtrl_MarkerSetAlpha, 3004).
+-define(wxStyledTextCtrl_SetMarginType, 3005).
+-define(wxStyledTextCtrl_GetMarginType, 3006).
+-define(wxStyledTextCtrl_SetMarginWidth, 3007).
+-define(wxStyledTextCtrl_GetMarginWidth, 3008).
+-define(wxStyledTextCtrl_SetMarginMask, 3009).
+-define(wxStyledTextCtrl_GetMarginMask, 3010).
+-define(wxStyledTextCtrl_SetMarginSensitive, 3011).
+-define(wxStyledTextCtrl_GetMarginSensitive, 3012).
+-define(wxStyledTextCtrl_StyleClearAll, 3013).
+-define(wxStyledTextCtrl_StyleSetForeground, 3014).
+-define(wxStyledTextCtrl_StyleSetBackground, 3015).
+-define(wxStyledTextCtrl_StyleSetBold, 3016).
+-define(wxStyledTextCtrl_StyleSetItalic, 3017).
+-define(wxStyledTextCtrl_StyleSetSize, 3018).
+-define(wxStyledTextCtrl_StyleSetFaceName, 3019).
+-define(wxStyledTextCtrl_StyleSetEOLFilled, 3020).
+-define(wxStyledTextCtrl_StyleResetDefault, 3021).
+-define(wxStyledTextCtrl_StyleSetUnderline, 3022).
+-define(wxStyledTextCtrl_StyleSetCase, 3023).
+-define(wxStyledTextCtrl_StyleSetHotSpot, 3024).
+-define(wxStyledTextCtrl_SetSelForeground, 3025).
+-define(wxStyledTextCtrl_SetSelBackground, 3026).
+-define(wxStyledTextCtrl_GetSelAlpha, 3027).
+-define(wxStyledTextCtrl_SetSelAlpha, 3028).
+-define(wxStyledTextCtrl_SetCaretForeground, 3029).
+-define(wxStyledTextCtrl_CmdKeyAssign, 3030).
+-define(wxStyledTextCtrl_CmdKeyClear, 3031).
+-define(wxStyledTextCtrl_CmdKeyClearAll, 3032).
+-define(wxStyledTextCtrl_SetStyleBytes, 3033).
+-define(wxStyledTextCtrl_StyleSetVisible, 3034).
+-define(wxStyledTextCtrl_GetCaretPeriod, 3035).
+-define(wxStyledTextCtrl_SetCaretPeriod, 3036).
+-define(wxStyledTextCtrl_SetWordChars, 3037).
+-define(wxStyledTextCtrl_BeginUndoAction, 3038).
+-define(wxStyledTextCtrl_EndUndoAction, 3039).
+-define(wxStyledTextCtrl_IndicatorSetStyle, 3040).
+-define(wxStyledTextCtrl_IndicatorGetStyle, 3041).
+-define(wxStyledTextCtrl_IndicatorSetForeground, 3042).
+-define(wxStyledTextCtrl_IndicatorGetForeground, 3043).
+-define(wxStyledTextCtrl_SetWhitespaceForeground, 3044).
+-define(wxStyledTextCtrl_SetWhitespaceBackground, 3045).
+-define(wxStyledTextCtrl_GetStyleBits, 3046).
+-define(wxStyledTextCtrl_SetLineState, 3047).
+-define(wxStyledTextCtrl_GetLineState, 3048).
+-define(wxStyledTextCtrl_GetMaxLineState, 3049).
+-define(wxStyledTextCtrl_GetCaretLineVisible, 3050).
+-define(wxStyledTextCtrl_SetCaretLineVisible, 3051).
+-define(wxStyledTextCtrl_GetCaretLineBackground, 3052).
+-define(wxStyledTextCtrl_SetCaretLineBackground, 3053).
+-define(wxStyledTextCtrl_AutoCompShow, 3054).
+-define(wxStyledTextCtrl_AutoCompCancel, 3055).
+-define(wxStyledTextCtrl_AutoCompActive, 3056).
+-define(wxStyledTextCtrl_AutoCompPosStart, 3057).
+-define(wxStyledTextCtrl_AutoCompComplete, 3058).
+-define(wxStyledTextCtrl_AutoCompStops, 3059).
+-define(wxStyledTextCtrl_AutoCompSetSeparator, 3060).
+-define(wxStyledTextCtrl_AutoCompGetSeparator, 3061).
+-define(wxStyledTextCtrl_AutoCompSelect, 3062).
+-define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3063).
+-define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3064).
+-define(wxStyledTextCtrl_AutoCompSetFillUps, 3065).
+-define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3066).
+-define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3067).
+-define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3068).
+-define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3069).
+-define(wxStyledTextCtrl_UserListShow, 3070).
+-define(wxStyledTextCtrl_AutoCompSetAutoHide, 3071).
+-define(wxStyledTextCtrl_AutoCompGetAutoHide, 3072).
+-define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3073).
+-define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3074).
+-define(wxStyledTextCtrl_RegisterImage, 3075).
+-define(wxStyledTextCtrl_ClearRegisteredImages, 3076).
+-define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3077).
+-define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3078).
+-define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3079).
+-define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3080).
+-define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3081).
+-define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3082).
+-define(wxStyledTextCtrl_SetIndent, 3083).
+-define(wxStyledTextCtrl_GetIndent, 3084).
+-define(wxStyledTextCtrl_SetUseTabs, 3085).
+-define(wxStyledTextCtrl_GetUseTabs, 3086).
+-define(wxStyledTextCtrl_SetLineIndentation, 3087).
+-define(wxStyledTextCtrl_GetLineIndentation, 3088).
+-define(wxStyledTextCtrl_GetLineIndentPosition, 3089).
+-define(wxStyledTextCtrl_GetColumn, 3090).
+-define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3091).
+-define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3092).
+-define(wxStyledTextCtrl_SetIndentationGuides, 3093).
+-define(wxStyledTextCtrl_GetIndentationGuides, 3094).
+-define(wxStyledTextCtrl_SetHighlightGuide, 3095).
+-define(wxStyledTextCtrl_GetHighlightGuide, 3096).
+-define(wxStyledTextCtrl_GetLineEndPosition, 3097).
+-define(wxStyledTextCtrl_GetCodePage, 3098).
+-define(wxStyledTextCtrl_GetCaretForeground, 3099).
+-define(wxStyledTextCtrl_GetReadOnly, 3100).
+-define(wxStyledTextCtrl_SetCurrentPos, 3101).
+-define(wxStyledTextCtrl_SetSelectionStart, 3102).
+-define(wxStyledTextCtrl_GetSelectionStart, 3103).
+-define(wxStyledTextCtrl_SetSelectionEnd, 3104).
+-define(wxStyledTextCtrl_GetSelectionEnd, 3105).
+-define(wxStyledTextCtrl_SetPrintMagnification, 3106).
+-define(wxStyledTextCtrl_GetPrintMagnification, 3107).
+-define(wxStyledTextCtrl_SetPrintColourMode, 3108).
+-define(wxStyledTextCtrl_GetPrintColourMode, 3109).
+-define(wxStyledTextCtrl_FindText, 3110).
+-define(wxStyledTextCtrl_FormatRange, 3111).
+-define(wxStyledTextCtrl_GetFirstVisibleLine, 3112).
+-define(wxStyledTextCtrl_GetLine, 3113).
+-define(wxStyledTextCtrl_GetLineCount, 3114).
+-define(wxStyledTextCtrl_SetMarginLeft, 3115).
+-define(wxStyledTextCtrl_GetMarginLeft, 3116).
+-define(wxStyledTextCtrl_SetMarginRight, 3117).
+-define(wxStyledTextCtrl_GetMarginRight, 3118).
+-define(wxStyledTextCtrl_GetModify, 3119).
+-define(wxStyledTextCtrl_SetSelection, 3120).
+-define(wxStyledTextCtrl_GetSelectedText, 3121).
+-define(wxStyledTextCtrl_GetTextRange, 3122).
+-define(wxStyledTextCtrl_HideSelection, 3123).
+-define(wxStyledTextCtrl_LineFromPosition, 3124).
+-define(wxStyledTextCtrl_PositionFromLine, 3125).
+-define(wxStyledTextCtrl_LineScroll, 3126).
+-define(wxStyledTextCtrl_EnsureCaretVisible, 3127).
+-define(wxStyledTextCtrl_ReplaceSelection, 3128).
+-define(wxStyledTextCtrl_SetReadOnly, 3129).
+-define(wxStyledTextCtrl_CanPaste, 3130).
+-define(wxStyledTextCtrl_CanUndo, 3131).
+-define(wxStyledTextCtrl_EmptyUndoBuffer, 3132).
+-define(wxStyledTextCtrl_Undo, 3133).
+-define(wxStyledTextCtrl_Cut, 3134).
+-define(wxStyledTextCtrl_Copy, 3135).
+-define(wxStyledTextCtrl_Paste, 3136).
+-define(wxStyledTextCtrl_Clear, 3137).
+-define(wxStyledTextCtrl_SetText, 3138).
+-define(wxStyledTextCtrl_GetText, 3139).
+-define(wxStyledTextCtrl_GetTextLength, 3140).
+-define(wxStyledTextCtrl_GetOvertype, 3141).
+-define(wxStyledTextCtrl_SetCaretWidth, 3142).
+-define(wxStyledTextCtrl_GetCaretWidth, 3143).
+-define(wxStyledTextCtrl_SetTargetStart, 3144).
+-define(wxStyledTextCtrl_GetTargetStart, 3145).
+-define(wxStyledTextCtrl_SetTargetEnd, 3146).
+-define(wxStyledTextCtrl_GetTargetEnd, 3147).
+-define(wxStyledTextCtrl_ReplaceTarget, 3148).
+-define(wxStyledTextCtrl_SearchInTarget, 3149).
+-define(wxStyledTextCtrl_SetSearchFlags, 3150).
+-define(wxStyledTextCtrl_GetSearchFlags, 3151).
+-define(wxStyledTextCtrl_CallTipShow, 3152).
+-define(wxStyledTextCtrl_CallTipCancel, 3153).
+-define(wxStyledTextCtrl_CallTipActive, 3154).
+-define(wxStyledTextCtrl_CallTipPosAtStart, 3155).
+-define(wxStyledTextCtrl_CallTipSetHighlight, 3156).
+-define(wxStyledTextCtrl_CallTipSetBackground, 3157).
+-define(wxStyledTextCtrl_CallTipSetForeground, 3158).
+-define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3159).
+-define(wxStyledTextCtrl_CallTipUseStyle, 3160).
+-define(wxStyledTextCtrl_VisibleFromDocLine, 3161).
+-define(wxStyledTextCtrl_DocLineFromVisible, 3162).
+-define(wxStyledTextCtrl_WrapCount, 3163).
+-define(wxStyledTextCtrl_SetFoldLevel, 3164).
+-define(wxStyledTextCtrl_GetFoldLevel, 3165).
+-define(wxStyledTextCtrl_GetLastChild, 3166).
+-define(wxStyledTextCtrl_GetFoldParent, 3167).
+-define(wxStyledTextCtrl_ShowLines, 3168).
+-define(wxStyledTextCtrl_HideLines, 3169).
+-define(wxStyledTextCtrl_GetLineVisible, 3170).
+-define(wxStyledTextCtrl_SetFoldExpanded, 3171).
+-define(wxStyledTextCtrl_GetFoldExpanded, 3172).
+-define(wxStyledTextCtrl_ToggleFold, 3173).
+-define(wxStyledTextCtrl_EnsureVisible, 3174).
+-define(wxStyledTextCtrl_SetFoldFlags, 3175).
+-define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3176).
+-define(wxStyledTextCtrl_SetTabIndents, 3177).
+-define(wxStyledTextCtrl_GetTabIndents, 3178).
+-define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3179).
+-define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3180).
+-define(wxStyledTextCtrl_SetMouseDwellTime, 3181).
+-define(wxStyledTextCtrl_GetMouseDwellTime, 3182).
+-define(wxStyledTextCtrl_WordStartPosition, 3183).
+-define(wxStyledTextCtrl_WordEndPosition, 3184).
+-define(wxStyledTextCtrl_SetWrapMode, 3185).
+-define(wxStyledTextCtrl_GetWrapMode, 3186).
+-define(wxStyledTextCtrl_SetWrapVisualFlags, 3187).
+-define(wxStyledTextCtrl_GetWrapVisualFlags, 3188).
+-define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3189).
+-define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3190).
+-define(wxStyledTextCtrl_SetWrapStartIndent, 3191).
+-define(wxStyledTextCtrl_GetWrapStartIndent, 3192).
+-define(wxStyledTextCtrl_SetLayoutCache, 3193).
+-define(wxStyledTextCtrl_GetLayoutCache, 3194).
+-define(wxStyledTextCtrl_SetScrollWidth, 3195).
+-define(wxStyledTextCtrl_GetScrollWidth, 3196).
+-define(wxStyledTextCtrl_TextWidth, 3197).
+-define(wxStyledTextCtrl_GetEndAtLastLine, 3198).
+-define(wxStyledTextCtrl_TextHeight, 3199).
+-define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3200).
+-define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3201).
+-define(wxStyledTextCtrl_AppendText, 3202).
+-define(wxStyledTextCtrl_GetTwoPhaseDraw, 3203).
+-define(wxStyledTextCtrl_SetTwoPhaseDraw, 3204).
+-define(wxStyledTextCtrl_TargetFromSelection, 3205).
+-define(wxStyledTextCtrl_LinesJoin, 3206).
+-define(wxStyledTextCtrl_LinesSplit, 3207).
+-define(wxStyledTextCtrl_SetFoldMarginColour, 3208).
+-define(wxStyledTextCtrl_SetFoldMarginHiColour, 3209).
+-define(wxStyledTextCtrl_LineDown, 3210).
+-define(wxStyledTextCtrl_LineDownExtend, 3211).
+-define(wxStyledTextCtrl_LineUp, 3212).
+-define(wxStyledTextCtrl_LineUpExtend, 3213).
+-define(wxStyledTextCtrl_CharLeft, 3214).
+-define(wxStyledTextCtrl_CharLeftExtend, 3215).
+-define(wxStyledTextCtrl_CharRight, 3216).
+-define(wxStyledTextCtrl_CharRightExtend, 3217).
+-define(wxStyledTextCtrl_WordLeft, 3218).
+-define(wxStyledTextCtrl_WordLeftExtend, 3219).
+-define(wxStyledTextCtrl_WordRight, 3220).
+-define(wxStyledTextCtrl_WordRightExtend, 3221).
+-define(wxStyledTextCtrl_Home, 3222).
+-define(wxStyledTextCtrl_HomeExtend, 3223).
+-define(wxStyledTextCtrl_LineEnd, 3224).
+-define(wxStyledTextCtrl_LineEndExtend, 3225).
+-define(wxStyledTextCtrl_DocumentStart, 3226).
+-define(wxStyledTextCtrl_DocumentStartExtend, 3227).
+-define(wxStyledTextCtrl_DocumentEnd, 3228).
+-define(wxStyledTextCtrl_DocumentEndExtend, 3229).
+-define(wxStyledTextCtrl_PageUp, 3230).
+-define(wxStyledTextCtrl_PageUpExtend, 3231).
+-define(wxStyledTextCtrl_PageDown, 3232).
+-define(wxStyledTextCtrl_PageDownExtend, 3233).
+-define(wxStyledTextCtrl_EditToggleOvertype, 3234).
+-define(wxStyledTextCtrl_Cancel, 3235).
+-define(wxStyledTextCtrl_DeleteBack, 3236).
+-define(wxStyledTextCtrl_Tab, 3237).
+-define(wxStyledTextCtrl_BackTab, 3238).
+-define(wxStyledTextCtrl_NewLine, 3239).
+-define(wxStyledTextCtrl_FormFeed, 3240).
+-define(wxStyledTextCtrl_VCHome, 3241).
+-define(wxStyledTextCtrl_VCHomeExtend, 3242).
+-define(wxStyledTextCtrl_ZoomIn, 3243).
+-define(wxStyledTextCtrl_ZoomOut, 3244).
+-define(wxStyledTextCtrl_DelWordLeft, 3245).
+-define(wxStyledTextCtrl_DelWordRight, 3246).
+-define(wxStyledTextCtrl_LineCut, 3247).
+-define(wxStyledTextCtrl_LineDelete, 3248).
+-define(wxStyledTextCtrl_LineTranspose, 3249).
+-define(wxStyledTextCtrl_LineDuplicate, 3250).
+-define(wxStyledTextCtrl_LowerCase, 3251).
+-define(wxStyledTextCtrl_UpperCase, 3252).
+-define(wxStyledTextCtrl_LineScrollDown, 3253).
+-define(wxStyledTextCtrl_LineScrollUp, 3254).
+-define(wxStyledTextCtrl_DeleteBackNotLine, 3255).
+-define(wxStyledTextCtrl_HomeDisplay, 3256).
+-define(wxStyledTextCtrl_HomeDisplayExtend, 3257).
+-define(wxStyledTextCtrl_LineEndDisplay, 3258).
+-define(wxStyledTextCtrl_LineEndDisplayExtend, 3259).
+-define(wxStyledTextCtrl_HomeWrapExtend, 3260).
+-define(wxStyledTextCtrl_LineEndWrap, 3261).
+-define(wxStyledTextCtrl_LineEndWrapExtend, 3262).
+-define(wxStyledTextCtrl_VCHomeWrap, 3263).
+-define(wxStyledTextCtrl_VCHomeWrapExtend, 3264).
+-define(wxStyledTextCtrl_LineCopy, 3265).
+-define(wxStyledTextCtrl_MoveCaretInsideView, 3266).
+-define(wxStyledTextCtrl_LineLength, 3267).
+-define(wxStyledTextCtrl_BraceHighlight, 3268).
+-define(wxStyledTextCtrl_BraceBadLight, 3269).
+-define(wxStyledTextCtrl_BraceMatch, 3270).
+-define(wxStyledTextCtrl_GetViewEOL, 3271).
+-define(wxStyledTextCtrl_SetViewEOL, 3272).
+-define(wxStyledTextCtrl_SetModEventMask, 3273).
+-define(wxStyledTextCtrl_GetEdgeColumn, 3274).
+-define(wxStyledTextCtrl_SetEdgeColumn, 3275).
+-define(wxStyledTextCtrl_SetEdgeMode, 3276).
+-define(wxStyledTextCtrl_GetEdgeMode, 3277).
+-define(wxStyledTextCtrl_GetEdgeColour, 3278).
+-define(wxStyledTextCtrl_SetEdgeColour, 3279).
+-define(wxStyledTextCtrl_SearchAnchor, 3280).
+-define(wxStyledTextCtrl_SearchNext, 3281).
+-define(wxStyledTextCtrl_SearchPrev, 3282).
+-define(wxStyledTextCtrl_LinesOnScreen, 3283).
+-define(wxStyledTextCtrl_UsePopUp, 3284).
+-define(wxStyledTextCtrl_SelectionIsRectangle, 3285).
+-define(wxStyledTextCtrl_SetZoom, 3286).
+-define(wxStyledTextCtrl_GetZoom, 3287).
+-define(wxStyledTextCtrl_GetModEventMask, 3288).
+-define(wxStyledTextCtrl_SetSTCFocus, 3289).
+-define(wxStyledTextCtrl_GetSTCFocus, 3290).
+-define(wxStyledTextCtrl_SetStatus, 3291).
+-define(wxStyledTextCtrl_GetStatus, 3292).
+-define(wxStyledTextCtrl_SetMouseDownCaptures, 3293).
+-define(wxStyledTextCtrl_GetMouseDownCaptures, 3294).
+-define(wxStyledTextCtrl_SetSTCCursor, 3295).
+-define(wxStyledTextCtrl_GetSTCCursor, 3296).
+-define(wxStyledTextCtrl_SetControlCharSymbol, 3297).
+-define(wxStyledTextCtrl_GetControlCharSymbol, 3298).
+-define(wxStyledTextCtrl_WordPartLeft, 3299).
+-define(wxStyledTextCtrl_WordPartLeftExtend, 3300).
+-define(wxStyledTextCtrl_WordPartRight, 3301).
+-define(wxStyledTextCtrl_WordPartRightExtend, 3302).
+-define(wxStyledTextCtrl_SetVisiblePolicy, 3303).
+-define(wxStyledTextCtrl_DelLineLeft, 3304).
+-define(wxStyledTextCtrl_DelLineRight, 3305).
+-define(wxStyledTextCtrl_GetXOffset, 3306).
+-define(wxStyledTextCtrl_ChooseCaretX, 3307).
+-define(wxStyledTextCtrl_SetXCaretPolicy, 3308).
+-define(wxStyledTextCtrl_SetYCaretPolicy, 3309).
+-define(wxStyledTextCtrl_GetPrintWrapMode, 3310).
+-define(wxStyledTextCtrl_SetHotspotActiveForeground, 3311).
+-define(wxStyledTextCtrl_SetHotspotActiveBackground, 3312).
+-define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3313).
+-define(wxStyledTextCtrl_SetHotspotSingleLine, 3314).
+-define(wxStyledTextCtrl_ParaDownExtend, 3315).
+-define(wxStyledTextCtrl_ParaUp, 3316).
+-define(wxStyledTextCtrl_ParaUpExtend, 3317).
+-define(wxStyledTextCtrl_PositionBefore, 3318).
+-define(wxStyledTextCtrl_PositionAfter, 3319).
+-define(wxStyledTextCtrl_CopyRange, 3320).
+-define(wxStyledTextCtrl_CopyText, 3321).
+-define(wxStyledTextCtrl_SetSelectionMode, 3322).
+-define(wxStyledTextCtrl_GetSelectionMode, 3323).
+-define(wxStyledTextCtrl_LineDownRectExtend, 3324).
+-define(wxStyledTextCtrl_LineUpRectExtend, 3325).
+-define(wxStyledTextCtrl_CharLeftRectExtend, 3326).
+-define(wxStyledTextCtrl_CharRightRectExtend, 3327).
+-define(wxStyledTextCtrl_HomeRectExtend, 3328).
+-define(wxStyledTextCtrl_VCHomeRectExtend, 3329).
+-define(wxStyledTextCtrl_LineEndRectExtend, 3330).
+-define(wxStyledTextCtrl_PageUpRectExtend, 3331).
+-define(wxStyledTextCtrl_PageDownRectExtend, 3332).
+-define(wxStyledTextCtrl_StutteredPageUp, 3333).
+-define(wxStyledTextCtrl_StutteredPageUpExtend, 3334).
+-define(wxStyledTextCtrl_StutteredPageDown, 3335).
+-define(wxStyledTextCtrl_StutteredPageDownExtend, 3336).
+-define(wxStyledTextCtrl_WordLeftEnd, 3337).
+-define(wxStyledTextCtrl_WordLeftEndExtend, 3338).
+-define(wxStyledTextCtrl_WordRightEnd, 3339).
+-define(wxStyledTextCtrl_WordRightEndExtend, 3340).
+-define(wxStyledTextCtrl_SetWhitespaceChars, 3341).
+-define(wxStyledTextCtrl_SetCharsDefault, 3342).
+-define(wxStyledTextCtrl_AutoCompGetCurrent, 3343).
+-define(wxStyledTextCtrl_Allocate, 3344).
+-define(wxStyledTextCtrl_FindColumn, 3345).
+-define(wxStyledTextCtrl_GetCaretSticky, 3346).
+-define(wxStyledTextCtrl_SetCaretSticky, 3347).
+-define(wxStyledTextCtrl_ToggleCaretSticky, 3348).
+-define(wxStyledTextCtrl_SetPasteConvertEndings, 3349).
+-define(wxStyledTextCtrl_GetPasteConvertEndings, 3350).
+-define(wxStyledTextCtrl_SelectionDuplicate, 3351).
+-define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3352).
+-define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3353).
+-define(wxStyledTextCtrl_StartRecord, 3354).
+-define(wxStyledTextCtrl_StopRecord, 3355).
+-define(wxStyledTextCtrl_SetLexer, 3356).
+-define(wxStyledTextCtrl_GetLexer, 3357).
+-define(wxStyledTextCtrl_Colourise, 3358).
+-define(wxStyledTextCtrl_SetProperty, 3359).
+-define(wxStyledTextCtrl_SetKeyWords, 3360).
+-define(wxStyledTextCtrl_SetLexerLanguage, 3361).
+-define(wxStyledTextCtrl_GetProperty, 3362).
+-define(wxStyledTextCtrl_GetStyleBitsNeeded, 3363).
+-define(wxStyledTextCtrl_GetCurrentLine, 3364).
+-define(wxStyledTextCtrl_StyleSetSpec, 3365).
+-define(wxStyledTextCtrl_StyleSetFont, 3366).
+-define(wxStyledTextCtrl_StyleSetFontAttr, 3367).
+-define(wxStyledTextCtrl_StyleSetCharacterSet, 3368).
+-define(wxStyledTextCtrl_StyleSetFontEncoding, 3369).
+-define(wxStyledTextCtrl_CmdKeyExecute, 3370).
+-define(wxStyledTextCtrl_SetMargins, 3371).
+-define(wxStyledTextCtrl_GetSelection, 3372).
+-define(wxStyledTextCtrl_PointFromPosition, 3373).
+-define(wxStyledTextCtrl_ScrollToLine, 3374).
+-define(wxStyledTextCtrl_ScrollToColumn, 3375).
+-define(wxStyledTextCtrl_SetVScrollBar, 3376).
+-define(wxStyledTextCtrl_SetHScrollBar, 3377).
+-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3378).
+-define(wxStyledTextCtrl_SetLastKeydownProcessed, 3379).
+-define(wxStyledTextCtrl_SaveFile, 3380).
+-define(wxStyledTextCtrl_LoadFile, 3381).
+-define(wxStyledTextCtrl_DoDragOver, 3382).
+-define(wxStyledTextCtrl_DoDropText, 3383).
+-define(wxStyledTextCtrl_GetUseAntiAliasing, 3384).
+-define(wxStyledTextCtrl_AddTextRaw, 3385).
+-define(wxStyledTextCtrl_InsertTextRaw, 3386).
+-define(wxStyledTextCtrl_GetCurLineRaw, 3387).
+-define(wxStyledTextCtrl_GetLineRaw, 3388).
+-define(wxStyledTextCtrl_GetSelectedTextRaw, 3389).
+-define(wxStyledTextCtrl_GetTextRangeRaw, 3390).
+-define(wxStyledTextCtrl_SetTextRaw, 3391).
+-define(wxStyledTextCtrl_GetTextRaw, 3392).
+-define(wxStyledTextCtrl_AppendTextRaw, 3393).
+-define(wxArtProvider_GetBitmap, 3394).
+-define(wxArtProvider_GetIcon, 3395).
+-define(wxTreeEvent_GetKeyCode, 3396).
+-define(wxTreeEvent_GetItem, 3397).
+-define(wxTreeEvent_GetKeyEvent, 3398).
+-define(wxTreeEvent_GetLabel, 3399).
+-define(wxTreeEvent_GetOldItem, 3400).
+-define(wxTreeEvent_GetPoint, 3401).
+-define(wxTreeEvent_IsEditCancelled, 3402).
+-define(wxTreeEvent_SetToolTip, 3403).
+-define(wxNotebookEvent_GetOldSelection, 3404).
+-define(wxNotebookEvent_GetSelection, 3405).
+-define(wxNotebookEvent_SetOldSelection, 3406).
+-define(wxNotebookEvent_SetSelection, 3407).
+-define(wxFileDataObject_new, 3408).
+-define(wxFileDataObject_AddFile, 3409).
+-define(wxFileDataObject_GetFilenames, 3410).
+-define(wxFileDataObject_destroy, 3411).
+-define(wxTextDataObject_new, 3412).
+-define(wxTextDataObject_GetTextLength, 3413).
+-define(wxTextDataObject_GetText, 3414).
+-define(wxTextDataObject_SetText, 3415).
+-define(wxTextDataObject_destroy, 3416).
+-define(wxBitmapDataObject_new_1_1, 3417).
+-define(wxBitmapDataObject_new_1_0, 3418).
+-define(wxBitmapDataObject_GetBitmap, 3419).
+-define(wxBitmapDataObject_SetBitmap, 3420).
+-define(wxBitmapDataObject_destroy, 3421).
+-define(wxClipboard_new, 3423).
+-define(wxClipboard_destruct, 3424).
+-define(wxClipboard_AddData, 3425).
+-define(wxClipboard_Clear, 3426).
+-define(wxClipboard_Close, 3427).
+-define(wxClipboard_Flush, 3428).
+-define(wxClipboard_GetData, 3429).
+-define(wxClipboard_IsOpened, 3430).
+-define(wxClipboard_Open, 3431).
+-define(wxClipboard_SetData, 3432).
+-define(wxClipboard_UsePrimarySelection, 3434).
+-define(wxClipboard_IsSupported, 3435).
+-define(wxClipboard_Get, 3436).
+-define(wxSpinEvent_GetPosition, 3437).
+-define(wxSpinEvent_SetPosition, 3438).
+-define(wxSplitterWindow_new_0, 3439).
+-define(wxSplitterWindow_new_2, 3440).
+-define(wxSplitterWindow_destruct, 3441).
+-define(wxSplitterWindow_Create, 3442).
+-define(wxSplitterWindow_GetMinimumPaneSize, 3443).
+-define(wxSplitterWindow_GetSashGravity, 3444).
+-define(wxSplitterWindow_GetSashPosition, 3445).
+-define(wxSplitterWindow_GetSplitMode, 3446).
+-define(wxSplitterWindow_GetWindow1, 3447).
+-define(wxSplitterWindow_GetWindow2, 3448).
+-define(wxSplitterWindow_Initialize, 3449).
+-define(wxSplitterWindow_IsSplit, 3450).
+-define(wxSplitterWindow_ReplaceWindow, 3451).
+-define(wxSplitterWindow_SetSashGravity, 3452).
+-define(wxSplitterWindow_SetSashPosition, 3453).
+-define(wxSplitterWindow_SetSashSize, 3454).
+-define(wxSplitterWindow_SetMinimumPaneSize, 3455).
+-define(wxSplitterWindow_SetSplitMode, 3456).
+-define(wxSplitterWindow_SplitHorizontally, 3457).
+-define(wxSplitterWindow_SplitVertically, 3458).
+-define(wxSplitterWindow_Unsplit, 3459).
+-define(wxSplitterWindow_UpdateSize, 3460).
+-define(wxSplitterEvent_GetSashPosition, 3461).
+-define(wxSplitterEvent_GetX, 3462).
+-define(wxSplitterEvent_GetY, 3463).
+-define(wxSplitterEvent_GetWindowBeingRemoved, 3464).
+-define(wxSplitterEvent_SetSashPosition, 3465).
+-define(wxHtmlWindow_new_0, 3466).
+-define(wxHtmlWindow_new_2, 3467).
+-define(wxHtmlWindow_AppendToPage, 3468).
+-define(wxHtmlWindow_GetOpenedAnchor, 3469).
+-define(wxHtmlWindow_GetOpenedPage, 3470).
+-define(wxHtmlWindow_GetOpenedPageTitle, 3471).
+-define(wxHtmlWindow_GetRelatedFrame, 3472).
+-define(wxHtmlWindow_HistoryBack, 3473).
+-define(wxHtmlWindow_HistoryCanBack, 3474).
+-define(wxHtmlWindow_HistoryCanForward, 3475).
+-define(wxHtmlWindow_HistoryClear, 3476).
+-define(wxHtmlWindow_HistoryForward, 3477).
+-define(wxHtmlWindow_LoadFile, 3478).
+-define(wxHtmlWindow_LoadPage, 3479).
+-define(wxHtmlWindow_SelectAll, 3480).
+-define(wxHtmlWindow_SelectionToText, 3481).
+-define(wxHtmlWindow_SelectLine, 3482).
+-define(wxHtmlWindow_SelectWord, 3483).
+-define(wxHtmlWindow_SetBorders, 3484).
+-define(wxHtmlWindow_SetFonts, 3485).
+-define(wxHtmlWindow_SetPage, 3486).
+-define(wxHtmlWindow_SetRelatedFrame, 3487).
+-define(wxHtmlWindow_SetRelatedStatusBar, 3488).
+-define(wxHtmlWindow_ToText, 3489).
+-define(wxHtmlWindow_destroy, 3490).
+-define(wxHtmlLinkEvent_GetLinkInfo, 3491).
+-define(wxSystemSettings_GetColour, 3492).
+-define(wxSystemSettings_GetFont, 3493).
+-define(wxSystemSettings_GetMetric, 3494).
+-define(wxSystemSettings_GetScreenType, 3495).
+-define(wxSystemOptions_GetOption, 3496).
+-define(wxSystemOptions_GetOptionInt, 3497).
+-define(wxSystemOptions_HasOption, 3498).
+-define(wxSystemOptions_IsFalse, 3499).
+-define(wxSystemOptions_SetOption_2_1, 3500).
+-define(wxSystemOptions_SetOption_2_0, 3501).
+-define(wxAuiNotebookEvent_SetSelection, 3502).
+-define(wxAuiNotebookEvent_GetSelection, 3503).
+-define(wxAuiNotebookEvent_SetOldSelection, 3504).
+-define(wxAuiNotebookEvent_GetOldSelection, 3505).
+-define(wxAuiNotebookEvent_SetDragSource, 3506).
+-define(wxAuiNotebookEvent_GetDragSource, 3507).
+-define(wxAuiManagerEvent_SetManager, 3508).
+-define(wxAuiManagerEvent_GetManager, 3509).
+-define(wxAuiManagerEvent_SetPane, 3510).
+-define(wxAuiManagerEvent_GetPane, 3511).
+-define(wxAuiManagerEvent_SetButton, 3512).
+-define(wxAuiManagerEvent_GetButton, 3513).
+-define(wxAuiManagerEvent_SetDC, 3514).
+-define(wxAuiManagerEvent_GetDC, 3515).
+-define(wxAuiManagerEvent_Veto, 3516).
+-define(wxAuiManagerEvent_GetVeto, 3517).
+-define(wxAuiManagerEvent_SetCanVeto, 3518).
+-define(wxAuiManagerEvent_CanVeto, 3519).
+-define(wxLogNull_new, 3520).
+-define(wxLogNull_destroy, 3521).
+-define(wxTaskBarIcon_new, 3522).
+-define(wxTaskBarIcon_destruct, 3523).
+-define(wxTaskBarIcon_PopupMenu, 3524).
+-define(wxTaskBarIcon_RemoveIcon, 3525).
+-define(wxTaskBarIcon_SetIcon, 3526).
+-define(wxLocale_new_0, 3527).
+-define(wxLocale_new_2, 3529).
+-define(wxLocale_destruct, 3530).
+-define(wxLocale_Init, 3532).
+-define(wxLocale_AddCatalog_1, 3533).
+-define(wxLocale_AddCatalog_3, 3534).
+-define(wxLocale_AddCatalogLookupPathPrefix, 3535).
+-define(wxLocale_GetCanonicalName, 3536).
+-define(wxLocale_GetLanguage, 3537).
+-define(wxLocale_GetLanguageName, 3538).
+-define(wxLocale_GetLocale, 3539).
+-define(wxLocale_GetName, 3540).
+-define(wxLocale_GetString_2, 3541).
+-define(wxLocale_GetString_4, 3542).
+-define(wxLocale_GetHeaderValue, 3543).
+-define(wxLocale_GetSysName, 3544).
+-define(wxLocale_GetSystemEncoding, 3545).
+-define(wxLocale_GetSystemEncodingName, 3546).
+-define(wxLocale_GetSystemLanguage, 3547).
+-define(wxLocale_IsLoaded, 3548).
+-define(wxLocale_IsOk, 3549).
diff --git a/lib/wx/src/wx.app.src b/lib/wx/src/wx.app.src
index e13982b0c1..d5ac478f20 100644
--- a/lib/wx/src/wx.app.src
+++ b/lib/wx/src/wx.app.src
@@ -33,5 +33,6 @@
]},
{registered, []},
{applications, [stdlib, kernel]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/wx/src/wx.appup.src b/lib/wx/src/wx.appup.src
index 1102af612e..311c0c0f52 100644
--- a/lib/wx/src/wx.appup.src
+++ b/lib/wx/src/wx.appup.src
@@ -1,8 +1,7 @@
-%% This is an -*- erlang -*- file.
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -16,8 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-
{"%VSN%",
- [ ],
- [ ]
+ [{<<".*">>,[{restart_application, wx}]}],
+ [{<<".*">>,[{restart_application, wx}]}]
}.
diff --git a/lib/wx/src/wxe.hrl b/lib/wx/src/wxe.hrl
index bd34b13385..66ec9ac45e 100644
--- a/lib/wx/src/wxe.hrl
+++ b/lib/wx/src/wxe.hrl
@@ -29,6 +29,11 @@
-record(wx_mem, {bin, size}).
+-record(evh, {et=null,id=-1,lastId=-1,cb=0,
+ skip=undefined,userdata=[], % temp
+ handler=undefined % added after connect
+ }).
+
-define(CLASS(Type,Class), ((Type) =:= Class) orelse (Type):parent_class(Class)).
-define(CLASS_T(Type,Class),
diff --git a/lib/wx/src/wxe_master.erl b/lib/wx/src/wxe_master.erl
index b98a7c793e..4b8a8f5720 100644
--- a/lib/wx/src/wxe_master.erl
+++ b/lib/wx/src/wxe_master.erl
@@ -28,7 +28,7 @@
-behaviour(gen_server).
%% API
--export([start/1, init_port/1, init_opengl/0]).
+-export([start/1, init_port/1, init_opengl/0, fetch_msgs/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -36,7 +36,9 @@
-record(state, {cb_port, %% Callback port and to erlang messages goes via it.
users, %% List of wx servers, needed ??
- driver}). %% Driver name so wx_server can create it's own port
+ driver, %% Driver name so wx_server can create it's own port
+ msgs=[] %% Early messages (such as openfiles on OSX)
+ }).
-include("wxe.hrl").
-include("gen/wxe_debug.hrl").
@@ -76,12 +78,18 @@ init_port(SilentStart) ->
%%--------------------------------------------------------------------
-%% Initlizes the opengl library
+%% Initalizes the opengl library
%%--------------------------------------------------------------------
init_opengl() ->
GLLib = wxe_util:wxgl_dl(),
wxe_util:call(?WXE_INIT_OPENGL, <<(list_to_binary(GLLib))/binary, 0:8>>).
+%%--------------------------------------------------------------------
+%% Fetch early messages, hack to get start up args on mac
+%%--------------------------------------------------------------------
+fetch_msgs() ->
+ gen_server:call(?MODULE, fetch_msgs, infinity).
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -152,6 +160,8 @@ init([SilentStart]) ->
%%--------------------------------------------------------------------
handle_call(init_port, From, State=#state{driver=Driver,cb_port=CBPort, users=Users}) ->
{reply, {Driver,CBPort}, State#state{users=gb_sets:add(From,Users)}};
+handle_call(fetch_msgs, _From, State=#state{msgs=Msgs}) ->
+ {reply, lists:reverse(Msgs), State#state{msgs=[]}};
handle_call(_Request, _From, State) ->
%%io:format("Unknown request ~p sent to ~p from ~p ~n",[_Request, ?MODULE, _From]),
Reply = ok,
@@ -182,6 +192,8 @@ handle_info({wxe_driver, internal_error, Msg}, State) ->
handle_info({wxe_driver, debug, Msg}, State) ->
io:format("WX DBG: ~s~n", [Msg]),
{noreply, State};
+handle_info({wxe_driver, open_file, File}, State=#state{msgs=Msgs}) ->
+ {noreply, State#state{msgs=[File|Msgs]}};
handle_info(_Info, State) ->
io:format("Unknown message ~p sent to ~p~n",[_Info, ?MODULE]),
{noreply, State}.
diff --git a/lib/wx/src/wxe_server.erl b/lib/wx/src/wxe_server.erl
index 04e0d0faf4..465b9da2e0 100644
--- a/lib/wx/src/wxe_server.erl
+++ b/lib/wx/src/wxe_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -36,8 +36,8 @@
terminate/2, code_change/3]).
-record(state, {port,cb_port,users,cleaners=[],cb,cb_cnt}).
--record(user, {events=[], evt_handler}).
--record(event, {object, callback, cb_handler}).
+-record(user, {events=[]}).
+%%-record(event, {object, callback, cb_handler}).
-define(APPLICATION, wxe).
-define(log(S,A), log(?MODULE,?LINE,S,A)).
@@ -119,7 +119,7 @@ handle_call(stop,{_From,_},State = #state{users=Users0, cleaners=Cs0}) ->
Env = get(?WXE_IDENTIFIER),
Users = gb_trees:to_list(Users0),
Cs = lists:map(fun({_Pid,User}) ->
- spawn_link(fun() -> cleanup(Env,[User], false) end)
+ spawn_link(fun() -> cleanup(Env,[User]) end)
end, Users),
{noreply, State#state{users=gb_trees:empty(), cleaners=Cs ++ Cs0}};
@@ -178,13 +178,13 @@ handle_info({'DOWN',_,process,Pid,_}, State=#state{users=Users0,cleaners=Cs}) ->
Users = gb_trees:delete(Pid,Users0),
Env = wx:get_env(),
case User of
- #user{events=[], evt_handler=undefined} -> %% No need to spawn
+ #user{events=[]} -> %% No need to spawn
case Cs =:= [] andalso gb_trees:is_empty(Users) of
- true -> {stop, normal, State#state{cleaners=Cs}};
- false -> {noreply, State#state{users=Users,cleaners=Cs}}
+ true -> {stop, normal, State#state{users=Users}};
+ false -> {noreply, State#state{users=Users}}
end;
_ ->
- Cleaner = spawn_link(fun() -> cleanup(Env,[User],true) end),
+ Cleaner = spawn_link(fun() -> cleanup(Env,[User]) end),
{noreply, State#state{users=Users,cleaners=[Cleaner|Cs]}}
end
catch _E:_R ->
@@ -192,17 +192,6 @@ handle_info({'DOWN',_,process,Pid,_}, State=#state{users=Users0,cleaners=Cs}) ->
{noreply, State}
end;
-handle_info(Msg = {'_wxe_destroy_', Pid}, State)
- when is_pid(Pid) ->
- case erlang:is_process_alive(Pid) of
- true ->
- Pid ! Msg,
- ok;
- false ->
- ok
- end,
- {noreply, State};
-
handle_info(_Info, State) ->
?log("Unknown message ~p sent to ~p~n",[_Info, ?MODULE]),
{noreply, State}.
@@ -222,36 +211,26 @@ code_change(_OldVsn, State, _Extra) ->
log(Mod,Line,Str,Args) ->
error_logger:format("~p:~p: " ++ Str, [Mod,Line|Args]).
-handle_connect(Object, EvData, From, State0 = #state{users=Users}) ->
- User0 = #user{events=Evs0,evt_handler=Handler0} = gb_trees:get(From, Users),
- Callback = wxEvtHandler:get_callback(EvData),
- case Handler0 of
- #wx_ref{} when Callback =:= 0 ->
- CBHandler = Handler0,
- Handler = Handler0;
- undefined when Callback =:= 0 ->
- Handler = new_evt_listener(State0, From),
- CBHandler = Handler;
- _ ->
- CBHandler = new_evt_listener(State0, From),
- Handler = Handler0
- end,
- Evs = [#event{object=Object,callback=Callback, cb_handler=CBHandler}|Evs0],
- User = User0#user{events=Evs, evt_handler=Handler},
- State1 = State0#state{users=gb_trees:update(From, User, Users)},
- if is_function(Callback) orelse is_pid(Callback) ->
- {FunId, State} = attach_fun(Callback,State1),
- Res = wxEvtHandler:connect_impl(CBHandler,Object,
- wxEvtHandler:replace_fun_with_id(EvData,FunId)),
- case Res of
- ok -> {reply,Res,State};
- _Error -> {reply,Res,State0}
- end;
-
- true ->
- Res = {call_impl, connect_cb, CBHandler},
- {reply, Res, State1}
- end.
+handle_connect(Object, #evh{handler=undefined, cb=Callback} = EvData0,
+ From, State0) ->
+ %% Callback let this process listen to the events
+ {FunId, State} = attach_fun(Callback,State0),
+ EvData1 = EvData0#evh{cb=FunId},
+ case wxEvtHandler:connect_impl(Object,EvData1) of
+ {ok, Handler} ->
+ EvData = EvData1#evh{handler=Handler,userdata=undefined},
+ handle_connect(Object, EvData, From, State);
+ Error ->
+ {reply, Error, State0}
+ end;
+handle_connect(Object, EvData=#evh{handler=Handler},
+ From, State0 = #state{users=Users}) ->
+ %% Correct process is already listening just register it
+ put(Handler, From),
+ User0 = #user{events=Listeners0} = gb_trees:get(From, Users),
+ User = User0#user{events=[{Object,EvData}|Listeners0]},
+ State = State0#state{users=gb_trees:update(From, User, Users)},
+ {reply, ok, State}.
invoke_cb({{Ev=#wx{}, Ref=#wx_ref{}}, FunId,_}, _S) ->
%% Event callbacks
@@ -329,20 +308,6 @@ get_wx_object_state(Pid) ->
_ -> ignore
end.
-new_evt_listener(State, Owner) ->
- #wx_env{port=Port} = wx:get_env(),
- _ = erlang:port_control(Port,98,<<>>),
- Listener = get_result(State),
- put(Listener, Owner),
- Listener.
-
-get_result(_State) ->
- receive
- {'_wxe_result_', Res} -> Res;
- {'_wxe_error_', Op, Error} ->
- erlang:error({Error, {wxEvtHandler, {internal_installer, Op}}})
- end.
-
attach_fun(Fun, S = #state{cb=CB,cb_cnt=Next}) ->
case gb_trees:lookup(Fun,CB) of
{value, ID} ->
@@ -367,90 +332,72 @@ delete_fun(FunId, State = #state{cb=CB}) ->
State
end.
-cleanup_evt_listener(U=#user{events=Evs0,evt_handler=Handler}, EvtListener, Object) ->
- {Evs, Del} = lists:foldl(fun(#event{object=Obj,cb_handler=CBH}, {Acc, Delete})
- when CBH =:= EvtListener, Obj =:= Object ->
- {Acc, Delete};
- (Event = #event{cb_handler=CBH}, {Acc, _Delete})
- when CBH =:= EvtListener ->
- {[Event|Acc], false};
- (Event, {Acc, Delete}) ->
- {[Event|Acc], Delete}
- end, {[], true}, Evs0),
- Del andalso wxEvtHandler:destroy_evt_listener(EvtListener),
- case Del andalso Handler =:= EvtListener of
- true ->
- U#user{events=Evs, evt_handler=undefined};
- false ->
- U#user{events=Evs}
- end.
+cleanup_evt_listener(U=#user{events=Evs0}, EvtListener, Object) ->
+ Filter = fun({Obj,#evh{handler=Evl}}) ->
+ not (Object =:= Obj andalso Evl =:= EvtListener)
+ end,
+ U#user{events=lists:filter(Filter, Evs0)}.
-handle_disconnect(Object, Evh, From, State0 = #state{users=Users0}) ->
+handle_disconnect(Object, Evh = #evh{cb=Fun}, From,
+ State0 = #state{users=Users0, cb=Callbacks}) ->
#user{events=Evs0} = gb_trees:get(From, Users0),
- Fun = wxEvtHandler:get_callback(Evh),
- case find_handler(Evs0, Object, Fun) of
- [] -> {reply, false, State0};
+ FunId = gb_trees:lookup(Fun, Callbacks),
+ case find_handler(Evs0, Object, Evh#evh{cb=FunId}) of
+ [] ->
+ {reply, false, State0};
Handlers ->
- case disconnect(Object,Evh, Handlers) of
- #event{} ->
- {reply, true, State0};
- Result ->
- {reply, Result, State0}
+ case disconnect(Object,Handlers) of
+ #evh{} -> {reply, true, State0};
+ Result -> {reply, Result, State0}
end
end.
-disconnect(Object,Evh,[Ev=#event{cb_handler=Handler}|Evs]) ->
- try wxEvtHandler:disconnect_impl(Handler,Object,Evh) of
+disconnect(Object,[Ev|Evs]) ->
+ try wxEvtHandler:disconnect_impl(Object,Ev) of
true -> Ev;
- false -> disconnect(Object, Evh, Evs);
+ false -> disconnect(Object, Evs);
Error -> Error
catch _:_ ->
false
end;
-disconnect(_, _, []) -> false.
-
-find_handler(Evs, Object, Fun) ->
- find_handler(Evs, Object, Fun, []).
-
-find_handler([Ev =#event{object=Object,callback=FunReg}|Evs],Object,Search,Acc) ->
- case FunReg =:= Search of
- true -> find_handler(Evs,Object,Search,[Ev|Acc]);
- false when is_function(FunReg), Search =:= 0 ->
- find_handler(Evs,Object,Search,[Ev|Acc]);
- _ ->
- find_handler(Evs,Object,Search,Acc)
+disconnect(_, []) -> false.
+
+find_handler([{Object,Evh}|Evs], Object, Match) ->
+ case match_handler(Match, Evh) of
+ false -> find_handler(Evs, Object, Match);
+ Res -> [Res|find_handler(Evs,Object,Match)]
end;
-find_handler([_|Evs],Object,Fun,Res) ->
- find_handler(Evs,Object,Fun,Res);
-find_handler([],_Object,_Fun,Res) ->
- Res.
+find_handler([_|Evs], Object, Match) ->
+ find_handler(Evs, Object, Match);
+find_handler([], _, _) -> [].
+
+match_handler(M=#evh{et=MET, cb=MCB},
+ #evh{et=ET, cb=CB, handler=Handler}) ->
+ %% Let wxWidgets handle the id matching
+ Match = match_et(MET, ET)
+ andalso match_cb(MCB, CB),
+ Match andalso M#evh{handler=Handler}.
+match_et(null, _) -> true;
+match_et(Met, Et) -> Met =:= Et.
+
+match_cb(none, _) -> true;
+match_cb({value,MId}, Id) -> MId =:= Id.
%% Cleanup
%% The server handles callbacks from driver so every other wx call must
%% be called from another process, therefore the cleaning must be spawned.
%%
-%% Using Disconnect when we terminate can crash, it is timing releated
-%% but it seems that disconnect on windows that are being deleted are bad.
-%% Since we are terminating the data will be cleaned up anyway.
-cleanup(Env, Data, Disconnect) ->
+cleanup(Env, Data) ->
put(?WXE_IDENTIFIER, Env),
- lists:foreach(fun(User) -> cleanup(User, Disconnect) end, Data),
+ Disconnect = fun({Object, Ev}) ->
+ try wxEvtHandler:disconnect_impl(Object,Ev)
+ catch _:_ -> ok
+ end
+ end,
+
+ lists:foreach(fun(#user{events=Evs}) ->
+ [Disconnect(Ev) || Ev <- Evs]
+ end, Data),
gen_server:cast(Env#wx_env.sv, {cleaned, self()}),
normal.
-
-cleanup(#user{events=Evs, evt_handler=Handler}, Disconnect) ->
- lists:foreach(fun(#event{object=O, callback=CB, cb_handler=CbH}) ->
- Disconnect andalso (catch wxEvtHandler:disconnect_impl(CbH,O)),
- case is_function(CB) of
- true ->
- wxEvtHandler:destroy_evt_listener(CbH);
- false ->
- ignore
- end
- end, Evs),
- case Handler of
- undefined -> ignore;
- _ -> wxEvtHandler:destroy_evt_listener(Handler)
- end,
- ok.
diff --git a/lib/wx/src/wxe_util.erl b/lib/wx/src/wxe_util.erl
index a31c3e30b8..02ac4ddfa6 100644
--- a/lib/wx/src/wxe_util.erl
+++ b/lib/wx/src/wxe_util.erl
@@ -74,7 +74,7 @@ call(Op, Args) ->
true ->
debug_call(Dbg band 15, Op, Args, Port)
end.
-
+
rec(Op) ->
receive
{'_wxe_result_', Res} -> Res;
@@ -108,21 +108,26 @@ send_bin(Bin) when is_binary(Bin) ->
get_cbId(Fun) ->
gen_server:call((wx:get_env())#wx_env.sv,{register_cb, Fun}, infinity).
-connect_cb(Object,EvData) ->
- handle_listener(connect_cb, Object, EvData).
-
-disconnect_cb(Object,EvData) ->
- handle_listener(disconnect_cb, Object, EvData).
-
-handle_listener(Op,Object,EvData) ->
- Listener = gen_server:call((wx:get_env())#wx_env.sv, {Op,Object,EvData}, infinity),
- case Listener of
- {call_impl, connect_cb, EvtList} ->
- wxEvtHandler:connect_impl(EvtList,Object,EvData);
- Res ->
- Res
+connect_cb(Object,EvData0 = #evh{cb=Callback}) ->
+ Server = (wx:get_env())#wx_env.sv,
+ case Callback of
+ 0 -> %% Message api connect from this process
+ case wxEvtHandler:connect_impl(Object,EvData0) of
+ {ok, Listener} ->
+ EvData = EvData0#evh{handler=Listener, userdata=undefined},
+ gen_server:call(Server, {connect_cb,Object,EvData}, infinity);
+ Error ->
+ Error
+ end;
+ _ -> %% callback, fun or pid (pid for wx_object:sync_events masked callbacks)
+ %% let the server do the connect
+ gen_server:call(Server, {connect_cb,Object,EvData0}, infinity)
end.
+disconnect_cb(Object,EvData) ->
+ Server = (wx:get_env())#wx_env.sv,
+ gen_server:call(Server, {disconnect_cb,Object,EvData}, infinity).
+
debug_cast(1, Op, _Args, _Port) ->
check_previous(),
case ets:lookup(wx_debug_info,Op) of
diff --git a/lib/wx/test/wx_app_SUITE.erl b/lib/wx/test/wx_app_SUITE.erl
index 162923eaa3..6331180ece 100644
--- a/lib/wx/test/wx_app_SUITE.erl
+++ b/lib/wx/test/wx_app_SUITE.erl
@@ -26,6 +26,7 @@
-compile(export_all).
-include("wx_test_lib.hrl").
+-include_lib("common_test/include/ct.hrl").
t() -> wx_test_lib:t(?MODULE).
@@ -50,7 +51,7 @@ end_per_testcase(Func,Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [fields, modules, exportall, app_depend, undef_funcs].
+ [fields, modules, exportall, app_depend, undef_funcs, appup].
groups() ->
[].
@@ -281,3 +282,9 @@ key1search(Key, L) ->
{value, {Key, Value}} ->
Value
end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Test that the wx appup file is ok
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(wx).
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index 79dbea0575..e3bbb21a23 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -98,8 +98,23 @@ several_apps(Config) ->
Pids = [spawn_link(fun() -> several_apps(Parent, N, Config) end)
|| N <- lists:seq(1,4)],
process_flag(trap_exit,true),
- ?m_multi_receive([{complete,Pid} || Pid <- Pids]),
+ Wait = fun(Pid,Acc) ->
+ receive {complete, Pid} -> Acc
+ after 20000 -> [Pid|Acc]
+ end
+ end,
+ Res = lists:foldl(Wait, [], Pids),
[Pid ! quit || Pid <- Pids],
+
+ Dbg = fun(Pid) ->
+ io:format("Stack ~p~n",[erlang:process_info(Pid, current_stacktrace)]),
+ io:format("Stack ~p~n",[erlang:process_info(Pid)])
+ end,
+ case Res of
+ [] -> ok;
+ Failed ->
+ [Dbg(Pid)|| Pid <- Failed]
+ end,
case wx_test_lib:user_available(Config) of
true ->
receive {'EXIT',_,foo} -> ok end;
@@ -216,7 +231,7 @@ create_menus(Frame) ->
%% Test the wx_misc.erl api functionality.
wx_misc(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
-wx_misc(Config) ->
+wx_misc(_Config) ->
wx:new([{debug, trace}]),
put(wx_test_verbose, true),
?m(ok, wx_misc:bell()),
@@ -326,23 +341,22 @@ wx_object(Config) ->
Me = self(),
?m({call, foobar, {Me, _}}, wx_object:call(Frame, foobar)),
?m(ok, wx_object:cast(Frame, foobar2)),
- ?m([{cast, foobar2}], flush()),
+ ?m([{cast, foobar2}|_], flush()),
FramePid = wx_object:get_pid(Frame),
io:format("wx_object pid ~p~n",[FramePid]),
FramePid ! foo3,
- ?m([{info, foo3}], flush()),
+ ?m([{info, foo3}|_], flush()),
?m(ok, wx_object:cast(Frame, fun(_) -> hehe end)),
- ?m([{cast, hehe}], flush()),
+ ?m([{cast, hehe}|_], flush()),
wxWindow:refresh(Frame),
- ?m([{sync_event, #wx{event=#wxPaint{}}, _}], flush()),
+ ?m([{sync_event, #wx{event=#wxPaint{}}, _}|_], flush()),
?m(ok, wx_object:cast(Frame, fun(_) -> timer:sleep(200), slept end)),
%% The sleep above should not hinder the Paint event below
%% Which it did in my buggy handling of the sync_callback
wxWindow:refresh(Frame),
- ?m([{sync_event, #wx{event=#wxPaint{}}, _}], flush()),
timer:sleep(500),
- ?m([{cast, slept}], flush()),
+ ?m([{sync_event, #wx{event=#wxPaint{}}, _}, {cast, slept}|_], flush()),
Monitor = erlang:monitor(process, FramePid),
case proplists:get_value(user, Config, false) of
@@ -382,7 +396,7 @@ check_events([], Async, Sync) ->
end.
flush() ->
- flush([], 500).
+ flush([], 1500).
flush(Acc, Wait) ->
receive
diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index 0e151ccc9b..b375c9d515 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -344,21 +344,21 @@ listCtrlSort(Config) ->
Wx = wx:new(),
Frame = wxFrame:new(Wx, ?wxID_ANY, "Frame"),
- LC = wxListCtrl:new(Frame, [{style, ?wxLC_REPORT bor ?wxLC_SORT_ASCENDING}]),
+ LC = wxListCtrl:new(Frame, [{style, ?wxLC_REPORT}]),
%% must be done crashes in wxwidgets otherwise.
wxListCtrl:insertColumn(LC, 0, "Column"),
Add = fun(Int) ->
- wxListCtrl:insertItem(LC, Int, integer_to_list(Int)),
+ wxListCtrl:insertItem(LC, Int, "ABC " ++ integer_to_list(Int)),
%% ItemData Can only be integers currently
- wxListCtrl:setItemData(LC, Int, abs(2500-Int))
+ wxListCtrl:setItemData(LC, Int, abs(50-Int))
end,
- wx:foreach(Add, lists:seq(0,5000)),
+ wx:foreach(Add, lists:seq(0,50)),
wxWindow:show(Frame),
- timer:sleep(200),
+ timer:sleep(2000),
Sort = fun() ->
wxListCtrl:sortItems(LC, fun(A, B) ->
@@ -374,11 +374,12 @@ listCtrlSort(Config) ->
io:format("Sorted ~p ~n",[Time]),
Item = wxListItem:new(),
+ wxListItem:setMask(Item, ?wxLIST_MASK_TEXT),
_List = wx:map(fun(Int) ->
wxListItem:setId(Item, Int),
?m(true, wxListCtrl:getItem(LC, Item)),
- io:format("~s~n",[wxListItem:getText(Item)])
- end, lists:seq(0,100)),
+ io:format("~p: ~s~n",[Int, wxListItem:getText(Item)])
+ end, lists:seq(0,10)),
wxListItem:destroy(Item),
wx_test_lib:wx_destroy(Frame,Config).
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index b9c2fafe0e..076f16ba16 100644
--- a/lib/wx/test/wx_event_SUITE.erl
+++ b/lib/wx/test/wx_event_SUITE.erl
@@ -149,7 +149,7 @@ disconnect(Config) ->
timer:sleep(1000),
wx_test_lib:flush(),
- ?m({'EXIT', {{badarg,_},_}}, wxEvtHandler:disconnect(Panel, non_existing_event_type)),
+ ?m(false, wxEvtHandler:disconnect(Panel, non_existing_event_type)),
?m(true, wxEvtHandler:disconnect(Panel, size)),
?m(ok, wxWindow:setSize(Panel, {200,102})),
timer:sleep(1000),
@@ -499,14 +499,14 @@ callback_clean(Config) ->
end
end),
timer:sleep(500), %% Give it time to remove it
- ?m({[{Pid,_,_}],[_],[_]}, white_box_check_event_handlers()),
+ ?m({[{Pid,_}],[_],[_]}, white_box_check_event_handlers()),
Pid ! remove,
timer:sleep(500), %% Give it time to remove it
?m({[],[],[]}, white_box_check_event_handlers()),
SetupEventHandlers(),
- ?m({[{_,_,_}],[_],[_]}, white_box_check_event_handlers()),
+ ?m({[{_,_}],[_],[_]}, white_box_check_event_handlers()),
wxDialog:show(Dlg),
wx_test_lib:wx_close(Dlg, Config),
@@ -526,9 +526,9 @@ white_box_check_event_handlers() ->
{status, _, _, [Env, _, _, _, Data]} = sys:get_status(Server),
[_H, _data, {data, [{_, Record}]}] = Data,
{state, _Port1, _Port2, Users, [], CBs, _Next} = Record,
- {[{Pid, Evs, Listener} ||
- {Pid, {user, Evs, Listener}} <- gb_trees:to_list(Users),
- (Evs =/= [] orelse Listener =/= undefined)], %% Ignore empty
+ {[{Pid, Evs} ||
+ {Pid, {user, Evs}} <- gb_trees:to_list(Users),
+ Evs =/= []], %% Ignore empty
gb_trees:to_list(CBs),
[Funs || Funs = {Id, {Fun,_}} <- Env, is_integer(Id), is_function(Fun)]
}.
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index c018b4fb86..5523c20440 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.1.2
+WX_VSN = 1.2
diff --git a/lib/xmerl/src/xmerl.app.src b/lib/xmerl/src/xmerl.app.src
index b471447bbd..45cfe9d250 100644
--- a/lib/xmerl/src/xmerl.app.src
+++ b/lib/xmerl/src/xmerl.app.src
@@ -39,5 +39,6 @@
{registered, []},
{env, []},
- {applications, [kernel, stdlib]}
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/xmerl/src/xmerl.appup.src b/lib/xmerl/src/xmerl.appup.src
index 0d8aa4eb04..0a84966576 100644
--- a/lib/xmerl/src/xmerl.appup.src
+++ b/lib/xmerl/src/xmerl.appup.src
@@ -1,14 +1,21 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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%
{"%VSN%",
- [
- {"1.1.11",
- [
- ]
- }
- ],
- [
- {"1.1.11",
- [
- ]
- }
- ]
+ [{<<".*">>,[{restart_application, xmerl}]}],
+ [{<<".*">>,[{restart_application, xmerl}]}]
}.
diff --git a/lib/xmerl/test/xmerl_appup_test.erl b/lib/xmerl/test/xmerl_appup_test.erl
index 80c8d8e4fd..ff6b368bcc 100644
--- a/lib/xmerl/test/xmerl_appup_test.erl
+++ b/lib/xmerl/test/xmerl_appup_test.erl
@@ -23,20 +23,7 @@
-module(xmerl_appup_test).
-compile(export_all).
-
-%-include("megaco_test_lib.hrl").
-
-
-%t() -> megaco_test_lib:t(?MODULE).
-%t(Case) -> megaco_test_lib:t({?MODULE, Case}).
-
-
-%% Test server callbacks
-% init_per_testcase(Case, Config) ->
-% megaco_test_lib:init_per_testcase(Case, Config).
-
-% end_per_testcase(Case, Config) ->
-% megaco_test_lib:end_per_testcase(Case, Config).
+-include_lib("common_test/include/ct.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -58,14 +45,7 @@ end_per_group(_GroupName, Config) ->
init_per_suite(suite) -> [];
init_per_suite(doc) -> [];
init_per_suite(Config) when is_list(Config) ->
- AppFile = file_name(xmerl, ".app"),
- AppupFile = file_name(xmerl, ".appup"),
- [{app_file, AppFile}, {appup_file, AppupFile}|Config].
-
-
-file_name(App, Ext) ->
- LibDir = code:lib_dir(App),
- filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
+ Config.
end_per_suite(suite) -> [];
@@ -76,317 +56,6 @@ end_per_suite(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup(suite) ->
- [];
-appup(doc) ->
- "perform a simple check of the appup file";
+%% perform a simple check of the appup file
appup(Config) when is_list(Config) ->
- AppupFile = key1search(appup_file, Config),
- AppFile = key1search(app_file, Config),
- Modules = modules(AppFile),
- check_appup(AppupFile, Modules).
-
-modules(File) ->
- case file:consult(File) of
- {ok, [{application,xmerl,Info}]} ->
- case lists:keysearch(modules,1,Info) of
- {value, {modules, Modules}} ->
- Modules;
- false ->
- fail({bad_appinfo, Info})
- end;
- Error ->
- fail({bad_appfile, Error})
- end.
-
-
-check_appup(AppupFile, Modules) ->
- case file:consult(AppupFile) of
- {ok, [{V, UpFrom, DownTo}]} ->
- io:format("V= ~p, UpFrom= ~p, DownTo= ~p, Modules= ~p~n",
- [V, UpFrom, DownTo, Modules]),
- check_appup(V, UpFrom, DownTo, Modules);
- Else ->
- fail({bad_appupfile, Else})
- end.
-
-
-check_appup(V, UpFrom, DownTo, Modules) ->
- check_version(V),
- check_depends(up, UpFrom, Modules),
- check_depends(down, DownTo, Modules),
- ok.
-
-
-check_depends(_, [], _) ->
- ok;
-check_depends(UpDown, [Dep|Deps], Modules) ->
- check_depend(UpDown, Dep, Modules),
- check_depends(UpDown, Deps, Modules).
-
-
-check_depend(up,I={add_application, _App}, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [up,I , Modules]),
- ok;
-check_depend(down,I={remove_application, _App}, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [down,I , Modules]),
- ok;
-check_depend(UpDown, {V, Instructions}, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n V: ~p"
- "~n Modules: ~p", [UpDown, V, Modules]),
- check_version(V),
- case check_instructions(UpDown,
- Instructions, Instructions, [], [], Modules) of
- {_Good, []} ->
- ok;
- {_, Bad} ->
- fail({bad_instructions, Bad, UpDown})
- end.
-
-
-check_instructions(_, [], _, Good, Bad, _) ->
- {lists:reverse(Good), lists:reverse(Bad)};
-check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instr: ~p", [UpDown,Instr]),
- case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of
- ok ->
- check_instructions(UpDown, Instrs, AllInstr,
- [Instr|Good], Bad, Modules);
- {error, Reason} ->
- d("check_instructions(~w) -> bad instruction: "
- "~n Reason: ~p", [UpDown,Reason]),
- check_instructions(UpDown, Instrs, AllInstr, Good,
- [{Instr, Reason}|Bad], Modules)
- end.
-
-%% A new module is added
-check_instruction(up, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when up-add_module instruction with"
- "~n Module: ~p", [Module]),
- check_module(Module, Modules);
-
-%% An old module is re-added
-check_instruction(down, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when down-add_module instruction with"
- "~n Module: ~p", [Module]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- local_error({existing_readded_module, Module})
- end;
-
-%% Removing a module on upgrade:
-%% - the module has been removed from the app-file.
-%% - check that no module depends on this (removed) module
-check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post) ->
- d("check_instruction -> entry when up-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- check_purge(Pre),
- check_purge(Post);
- ok ->
- local_error({existing_removed_module, Module})
- end;
-
-%% Removing a module on downgrade: the module exist
-%% in the app-file.
-check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post) ->
- d("check_instruction -> entry when down-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- ok ->
- check_purge(Pre),
- check_purge(Post),
- check_no_remove_depends(Module, AllInstr);
- {error, {unknown_module, Module, Modules}} ->
- local_error({nonexisting_removed_module, Module})
- end;
-
-check_instruction(_, {load_module, Module, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- d("check_instruction -> entry when load_module instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {update, Module, Change, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
- d("check_instruction -> entry when update instruction with"
- "~n Module: ~p"
- "~n Change: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Change, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_change(Change),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, Instr, _AllInstr, _Modules) ->
- d("check_instruction -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- local_error({error, {unknown_instruction, Instr}}).
-
-
-%% If Module X depends on Module Y, then module Y must have an update
-%% instruction of some sort (otherwise the depend is faulty).
-updated_modules([], Modules) ->
- d("update_modules -> entry when done with"
- "~n Modules: ~p", [Modules]),
- Modules;
-updated_modules([Instr |Instrs], Modules) ->
- d("update_modules -> entry with"
- "~n Instr: ~p"
- "~n Modules: ~p", [Instr,Modules]),
- Module = instruction_module(Instr),
- d("update_modules -> Module: ~p", [Module]),
- updated_modules(Instrs, [Module|Modules]).
-
-instruction_module({add_module, Module}) ->
- Module;
-instruction_module({remove, {Module, _, _}}) ->
- Module;
-instruction_module({load_module, Module, _, _, _}) ->
- Module;
-instruction_module({update, Module, _, _, _, _}) ->
- Module;
-instruction_module(Instr) ->
- d("instruction_module -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- local_error({error, {unknown_instruction, Instr}}).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-check_version(V) when is_list(V) ->
- ok;
-check_version(V) ->
- local_error({bad_version, V}).
-
-
-check_module(M, Modules) when is_atom(M) ->
- case lists:member(M,Modules) of
- true ->
- ok;
- false ->
- local_error({unknown_module, M, Modules})
- end;
-check_module(M, _) ->
- local_error({bad_module, M}).
-
-
-check_module_depend(M, [], _) when is_atom(M) ->
- d("check_module_depend -> entry with"
- "~n M: ~p", [M]),
- ok;
-check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) ->
- d("check_module_depend -> entry with"
- "~n M: ~p"
- "~n Deps: ~p"
- "~n Modules: ~p", [M, Deps, Modules]),
- case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of
- [] ->
- ok;
- Unknown ->
- local_error({unknown_depend_modules, Unknown})
- end;
-check_module_depend(_M, D, _Modules) ->
- d("check_module_depend -> entry when bad depend with"
- "~n D: ~p", [D]),
- local_error({bad_depend, D}).
-
-
-check_no_remove_depends(_Module, []) ->
- ok;
-check_no_remove_depends(Module, [Instr|Instrs]) ->
- check_no_remove_depend(Module, Instr),
- check_no_remove_depends(Module, Instrs).
-
-check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- local_error({removed_module_in_depend, load_module, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- local_error({removed_module_in_depend, update, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(_, _) ->
- ok.
-
-
-check_change(soft) ->
- ok;
-check_change({advanced, _Something}) ->
- ok;
-check_change(Change) ->
- local_error({bad_change, Change}).
-
-
-check_purge(soft_purge) ->
- ok;
-check_purge(brutal_purge) ->
- ok;
-check_purge(Purge) ->
- local_error({bad_purge, Purge}).
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-local_error(Reason) ->
- throw({error, Reason}).
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-d(F, A) ->
- d(false, F, A).
-
-d(true, F, A) ->
- io:format(F ++ "~n", A);
-d(_, _, _) ->
- ok.
-
-
+ ok = ?t:appup_test(xmerl).
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 333466c11e..aab2a37d6c 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.6
+XMERL_VSN = 1.3.7